Compare commits

...

388 commits

Author SHA1 Message Date
Daniel Nephin
a75c16cb1b Bump 1.6.1
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-02-23 12:08:56 -08:00
Mary Anthony
e08409f18d Updating Dockerfile
Signed-off-by: Mary Anthony <mary@docker.com>
2016-02-23 12:08:56 -08:00
Daniel Nephin
9d7dbe3857 Make environment variables without a value the same as docker-cli.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-02-23 11:58:16 -08:00
Daniel Nephin
bcd5286cd3 Revert "Change special case from '_', None to ()"
This reverts commit 677c50650c.

Revert "Modify service_test.py::ServiceTest::test_resolve_env to reflect new behavior"

This reverts commit 0019037712.

Revert "Mangle the tests. They pass for better or worse!"

This reverts commit 7ab9509ce6.

Revert "If an env var is passthrough but not defined on the host don't set it."

This reverts commit 6540efb3d3.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-02-23 11:58:16 -08:00
Daniel Nephin
daebf74d6c Stop other containers if the flag is set.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-02-23 11:52:51 -08:00
Richard Bann
155d813606 Add failing test for --abort-on-container-exit
Handle --abort-on-container-exit. Fixes #2940

Signed-off-by: Richard Bann <richardbann@gmail.com>
2016-02-23 11:52:51 -08:00
Daniel Nephin
73c2f8ee37 Fix warning about boolean values.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-02-23 11:52:51 -08:00
Joffrey F
4aae2c3b7b Use docker-py 1.7.1
Signed-off-by: Joffrey F <joffrey@docker.com>
2016-02-23 11:52:51 -08:00
Daniel Nephin
b79ad5f966 Fix validation message when there are multiple ested oneOf validations.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-02-23 11:52:51 -08:00
Victoria Bialas
f8e3c46fbb copyedit to make show as file format
Signed-off-by: Victoria Bialas <victoria.bialas@docker.com>
2016-02-23 11:52:51 -08:00
Victoria Bialas
f7c923062d corrected description of network aliases, added real-world example per #2907
Signed-off-by: Victoria Bialas <victoria.bialas@docker.com>
2016-02-23 11:52:51 -08:00
Joffrey F
61906ac2ff Update documentation for volume_driver option.
Signed-off-by: Joffrey F <joffrey@docker.com>
2016-02-23 11:52:51 -08:00
Daniel Nephin
ea8032c115 Make config validation error messages more consistent.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-02-23 11:52:51 -08:00
Daniel Nephin
7d22809ef4 Validate that each section of the config is a mapping before running interpolation.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-02-23 11:52:51 -08:00
Anthon van der Neut
cfda9d844e for 1.6.0 the version value needs to be a string
After conversion a file would immediately not load in docker-compose 1.6.0 with the message:

  ERROR: Version in "./converted.yml" is invalid - it should be a string.

Signed-off-by: Anthon van der Neut anthon@mnt.org
Signed-off-by: Anthon van der Neut <a.van.der.neut@ruamel.eu>
2016-02-23 11:52:51 -08:00
Joffrey F
1e29ad9fc7 Apply driver_opts processing to network configs
Signed-off-by: Joffrey F <joffrey@docker.com>
2016-02-23 11:52:51 -08:00
Joffrey F
fcf78fe3de Constraint build argument types. Numbers are cast into strings
Numerical driver_opts are also valid and typecast into strings.
Additional config tests.

Signed-off-by: Joffrey F <joffrey@docker.com>
2016-02-23 11:52:51 -08:00
Daniel Nephin
8d7b1e9047 Update guides to use v2 config format.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-02-23 11:52:51 -08:00
Daniel Nephin
6d2aa80435 Update link to docker volume create docs.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-02-23 11:52:51 -08:00
Daniel Nephin
13ec3d0217 Fix copying of volumes by using the name of the volume instead of the host path.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-02-23 11:52:51 -08:00
Anthon van der Neut
f59fef09a6 reset colors after warning
If a warning is shown, and you happen to have no color setting in your (bash) prompt, the \033[37m setting, stays active. With the message hardly readable (light grey on my default light yellow background), that means the prompt is barely visible and you need to do `tput reset`.
Would probably be better if the background color was set as well in case you have dark on light theme by default in your terminal.

Signed-off-by: Anthon van der Neut <a.van.der.neut@ruamel.eu>
2016-02-23 11:52:51 -08:00
Chia-liang Kao
f0a8c65b05 Quote argv as they are
Signed-off-by: Chia-liang Kao <clkao@clkao.org>
2016-02-23 11:52:51 -08:00
Chia-liang Kao
674e541cf7 Detect -t and -i separately
Signed-off-by: Chia-liang Kao <clkao@clkao.org>
2016-02-23 11:52:51 -08:00
Chia-liang Kao
ed5fedf516 Don't mount pwd if it is /
Signed-off-by: Chia-liang Kao <clkao@clkao.org>
2016-02-23 11:52:51 -08:00
Joffrey F
654b3710f7 Use modern set notation in _get_aliases
Signed-off-by: Joffrey F <joffrey@docker.com>
2016-02-23 11:52:51 -08:00
Joffrey F
f4a22b94ed Handle mismatched network formats in config files
Signed-off-by: Joffrey F <joffrey@docker.com>
2016-02-23 11:52:51 -08:00
Joffrey F
e5689afe4c Network aliases are now part of the network dictionary
Signed-off-by: Joffrey F <joffrey@docker.com>
2016-02-23 11:52:51 -08:00
Joffrey F
42cb719b52 Add v2_only decorator to network aliases test
Signed-off-by: Joffrey F <joffrey@docker.com>
2016-02-23 11:52:51 -08:00
Joffrey F
6ac6860dda Fix network list serialization in py3
Signed-off-by: Joffrey F <joffrey@docker.com>
2016-02-23 11:52:51 -08:00
Joffrey F
353da73eab Document network_aliases config
Signed-off-by: Joffrey F <joffrey@docker.com>
2016-02-23 11:52:51 -08:00
Joffrey F
c686be8fd3 Test network_aliases feature
Signed-off-by: Joffrey F <joffrey@docker.com>
2016-02-23 11:52:51 -08:00
Joffrey F
2f7a77e954 Allow user to specify custom network aliases
Signed-off-by: Joffrey F <joffrey@docker.com>
2016-02-23 11:52:51 -08:00
Joffrey F
d78ea85301 driver_opts can only be of type string
Signed-off-by: Joffrey F <joffrey@docker.com>
2016-02-23 11:52:51 -08:00
Daniel Nephin
7c95c733a9 Only set a container affinity if there are volumes to copy over.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-02-23 11:52:51 -08:00
Joffrey F
dd9a8d6eee Bring up all dependencies when running a single service.
Added test for running a depends_on service

Signed-off-by: Joffrey F <joffrey@docker.com>
2016-02-23 11:52:51 -08:00
Daniel Nephin
4f7c950ca8 Upgrade pyinstaller.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-02-23 11:52:51 -08:00
Joffrey F
a1d6e3b9e3 Add logging when initializing a volume.
Signed-off-by: Joffrey F <joffrey@docker.com>
2016-02-23 11:52:51 -08:00
Joffrey F
3eac70a9d3 Detailed error message when daemon version is too old.
Signed-off-by: Joffrey F <joffrey@docker.com>
2016-02-23 11:52:51 -08:00
Manuel Kaufmann
c7687592ff Typo fixed
Signed-off-by: Manuel Kaufmann <humitos@gmail.com>
2016-02-23 11:52:51 -08:00
Daniel Nephin
155efd28fa Merge build.args when merging services.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-02-23 11:52:51 -08:00
Daniel Nephin
2ced83e3d9 Fix build section without context.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-02-23 11:52:51 -08:00
Yohan Graterol
cea7911f56 Typo into the doc with networks in yaml
Signed-off-by: Yohan Graterol <yohangraterol92@gmail.com>
2016-02-23 11:52:51 -08:00
Daniel Nephin
db12794b1c Fix upgrading url.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-02-23 11:52:51 -08:00
Daniel Nephin
deeca57a0d Use 12 characters for the short id to match docker and fix backwards compatibility.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-02-23 11:52:51 -08:00
cr7pt0gr4ph7
edcbe2eb4d Simplify unit tests in config/config_test.py by using class variables instead of methods for parametrizing tests.
Signed-off-by: cr7pt0gr4ph7 <cr7pt0gr4ph7@gmail.com>
2016-02-23 11:52:51 -08:00
Lukas Waslowski
ad00f3dd21 Handle the 'network_mode' key when merging multiple compose files.
Fixes docker/compose#2840.

Signed-off-by: Lukas Waslowski <cr7pt0gr4ph7@gmail.com>
2016-02-23 11:52:51 -08:00
Lukas Waslowski
c77a8cfe3b Correctly merge the 'services/<service>/networks' key in the case of multiple compose files.
Fixes docker/compose#2839.

Signed-off-by: Lukas Waslowski <cr7pt0gr4ph7@gmail.com>
2016-02-23 11:52:51 -08:00
Lukas Waslowski
8548b75582 Separate MergePortsTest from MergeListsTest and add MergeNetworksTest.
Signed-off-by: Lukas Waslowski <cr7pt0gr4ph7@gmail.com>
2016-02-23 11:52:51 -08:00
Michael Käufl
c1be49ad53 Used absolute links in readme
This prevents links being broken on pypi
(e.g. https://pypi.python.org/pypi/docs/index.md#features)

Signed-off-by: Michael Käufl <docker-compose@c.michael-kaeufl.de>
2016-02-23 11:52:51 -08:00
Aanand Prasad
a716bdc482 Update Swarm integration guide and make it an official part of the docs
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-02-23 11:52:51 -08:00
jrabbit
abec6f5891 Change special case from '_', None to ()
Signed-off-by: jrabbit <jackjrabbit@gmail.com>
2016-02-23 11:52:51 -08:00
jrabbit
c195915263 Modify service_test.py::ServiceTest::test_resolve_env to reflect new behavior
Signed-off-by: jrabbit <jackjrabbit@gmail.com>
2016-02-23 11:52:51 -08:00
jrabbit
34d8f9b55a Mangle the tests. They pass for better or worse!
Signed-off-by: jrabbit <jackjrabbit@gmail.com>
2016-02-23 11:52:51 -08:00
jrabbit
f5533c1ed8 If an env var is passthrough but not defined on the host don't set it. This doesn't change too much code and keeps the generators.
Signed-off-by: jrabbit <jackjrabbit@gmail.com>
2016-02-23 11:52:51 -08:00
Daniel Nephin
722e3a2fc7 Merge remote-tracking branch 'docker/release' into bump-1.6.1 2016-02-23 11:15:34 -08:00
Aanand Prasad
9503aa2b5f Merge pull request #2821 from aanand/bump-1.6.0
Bump 1.6.0
2016-02-04 20:20:58 +00:00
Aanand Prasad
d99cad60e7 Bump 1.6.0
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-02-04 19:43:26 +00:00
Ben Firshman
8199c4a6e1 Improve names in Compose file 2 example
Just makes it a bit clearer what's going on.

Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2016-02-04 19:43:26 +00:00
Aanand Prasad
19ae76a442 Update docker-py and dockerpty
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-02-04 19:41:11 +00:00
Daniel Nephin
a55210413c Update docs for version being a string.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-02-04 17:21:15 +00:00
Spencer Rinehart
6caa188730 Fix example formatting for depends_on.
Markdown was acting against expectations here by merging the example indented YAML into the previous list item instead of treating it as a code block.

I decided that a better way of handling this would be to add a "Simple example:" line that is also used elsewhere in this file.  It resets the markdown indentation in a way that works.

Signed-off-by: Spencer Rinehart <anubis@overthemonkey.com>
2016-02-04 15:57:29 +00:00
Aanand Prasad
413a55aa71 Connect container to networks before starting it
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-02-04 14:43:11 +00:00
Dimitar Bonev
64336615cf Falling back to default project name when COMPOSE_PROJECT_NAME is set to empty
Signed-off-by: Dimitar Bonev <dimitar.bonev@gmail.com>
2016-02-03 18:21:44 +00:00
Mary Anthony
a59982eb11 Fixing duplicate identifiers
Signed-off-by: Mary Anthony <mary@docker.com>
2016-02-03 18:21:44 +00:00
Daniel Nephin
46f33f12b0 Update merge docs with depends_on, and correction about how links and volumes_from are merged.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-02-03 18:21:44 +00:00
Daniel Nephin
dc718eae65 Make links unique-by-alias when merging
Factor out MergeDict from merge_service_dicts to reduce complexity below limit.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-02-03 18:21:44 +00:00
Daniel Nephin
aa5ff05463 Fix merging of lists with multiple files.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-02-03 18:21:44 +00:00
Ryan Taylor Long
3b7471ae84 Add depends_on to ALLOWED_KEYS
This ensures and already existing `depends_on` is not deleted when the service on which it is defined also employs `extends`.

Signed-off-by: Ryan Taylor Long <ryan@rtlong.com>
2016-02-03 18:21:44 +00:00
Daniel Nephin
7f009aeeb9 Move command reference to overview.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-02-03 18:21:44 +00:00
Daniel Nephin
44c7d080bd Rename the old environment variable page to link environment variables.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-02-03 18:21:43 +00:00
Daniel Nephin
32bd760526 Cleanup command-line reference docs by renaming overview to envvars.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-02-03 18:21:43 +00:00
Daniel Nephin
009dbbe971 Use the same capitalization for all menu items in the docs.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-02-03 18:21:43 +00:00
Daniel Nephin
6b59ba0c31 Re-order compose docs so that quickstart guides come before other documentation.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-02-03 18:21:43 +00:00
Daniel Nephin
61d00ebee4 Extract volume init and removal from project.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-02-03 18:21:43 +00:00
Daniel Nephin
3b1276bd44 Include networks in the config_hash.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-02-03 18:21:43 +00:00
Daniel Nephin
3e8a4a5dc3 Don't initialize networks that aren't used by any services.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-02-03 18:21:43 +00:00
Daniel Nephin
0c87e0b18f Refactor project network initlization.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-02-03 18:21:43 +00:00
Aanand Prasad
8024f2f09e Tweak and test warning shown when version is a dict
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-02-03 18:21:43 +00:00
Aanand Prasad
f081376067 Improve error messages for invalid versions
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-02-03 18:21:42 +00:00
Aanand Prasad
ce0d469c18 Make 'version' a string
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-02-03 18:21:42 +00:00
Aanand Prasad
b2ee08f439 Remove redundant check - self.config should never be None
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-02-03 18:21:42 +00:00
Aanand Prasad
e9ba06ed4b Normalise/fix config field designators in validation messages
- Instead of "Service 'web' configuration key 'image'", just say
  "web.image"

- Fix the "Service 'services'" bug in the v2 file format

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-02-03 18:21:42 +00:00
Aanand Prasad
9e9b36460c Convert validation error tests to pytest style
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-02-03 18:21:42 +00:00
Harald Albers
25df0d8147 bash completion for docker-compose create
Signed-off-by: Harald Albers <github@albersweb.de>
2016-02-01 15:53:22 +00:00
Steve Durrheimer
c98c617c30 Add zsh completion for 'docker-compose create'
Signed-off-by: Steve Durrheimer <s.durrheimer@gmail.com>
2016-02-01 15:53:22 +00:00
Aanand Prasad
d3cd9213c1 Fix rebase-bump-commit script
Trim whitespace from wc's output before constructing arguments to `git rebase`

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-02-01 15:51:16 +00:00
Daniel Nephin
bbaae11a0f Fix race condition with up and setting signal handlers.
Also print stdout on wait_for_container().

Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-02-01 15:51:16 +00:00
Harald Albers
e925b8272b Fix computation of service list in bash completion
The previous approach assumed that the service list could be extracted
from a single file. It did not follow extends and overrides.

Signed-off-by: Harald Albers <github@albersweb.de>
2016-02-01 15:51:16 +00:00
Aanand Prasad
110401b6f0 Let the user specify any repo as their fork
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-02-01 15:51:16 +00:00
Aanand Prasad
8fb90bd732 Default to vim if EDITOR is not set
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-02-01 15:51:15 +00:00
Daniel Nephin
24e71db345 Don't copy over volumes that were previously host volumes, and are now container volumes.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-02-01 15:51:15 +00:00
Mary Anthony
fbe8484377 New navigation for 1.10 release
Updating with Joffrey's comments

Signed-off-by: Mary Anthony <mary@docker.com>
2016-02-01 15:51:15 +00:00
Aanand Prasad
89cca7bcb2 Extract helper methods
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-02-01 15:51:15 +00:00
Aanand Prasad
be66779fe9 Create declarations for named volumes
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-02-01 15:51:15 +00:00
Aanand Prasad
1772909fe2 Make sure version line is at the top of the file
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-02-01 15:51:15 +00:00
Aanand Prasad
86bdab64ab Make warnings a bit more readable
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-02-01 15:51:15 +00:00
Aanand Prasad
36a10f8dd5 Update for links, external_links, network_mode
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-02-01 15:51:15 +00:00
Aanand Prasad
9249ec62c2 Add note about named volumes to upgrade guide
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-02-01 15:51:15 +00:00
Aanand Prasad
4537ec70cc Remove outdated warnings about links from docs
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-01-27 13:33:38 +00:00
Aanand Prasad
695c692be6 Bump 1.6.0-rc2
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-01-27 01:42:21 +00:00
Aanand Prasad
297d20f085 Remove ability to join bridge network + user-defined networks
Containers connected to the bridge network can't have aliases, so it's
simpler to rule that they can *either* be connected to the bridge
network (via `network_mode: bridge`) *or* be connected to user-defined
networks (via `networks` or the default network).

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-01-27 01:42:21 +00:00
Joffrey F
16ef3d0eb8 Bump docker-py version to latest RC
Signed-off-by: Joffrey F <joffrey@docker.com>
2016-01-27 01:42:21 +00:00
Aanand Prasad
d765a3fb91 Add back external links in v2
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-01-27 00:45:44 +00:00
Aanand Prasad
7f4a94514b Fix trailing whitespace in docker-compose.md
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-01-26 23:41:47 +00:00
Tobias Munk
c0fe545947 fixed documentation about traversing yml files
Signed-off-by: Tobias Munk <schmunk@usrbin.de>
2016-01-26 18:58:15 +00:00
Aanand Prasad
52e74ab7ad Rename 'net' to 'network mode' in various classes/methods
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-01-26 11:52:23 +00:00
Aanand Prasad
2b46685855 Test that net can be extended
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-01-26 11:52:23 +00:00
Aanand Prasad
2b7306967b Implement network_mode in v2
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-01-26 11:52:23 +00:00
Joffrey F
3f28472ebc Move named volumes matching to config validation phase
Signed-off-by: Joffrey F <joffrey@docker.com>
2016-01-26 11:52:23 +00:00
Joffrey F
3da25aa463 is_named_volume also tests for home paths ~
Fix bug with VolumeSpec not being updated
Fix integration test

Signed-off-by: Joffrey F <joffrey@docker.com>
2016-01-26 11:52:23 +00:00
Joffrey F
a66bf72199 Match named volumes in service definitions with declared volumes
Raise ConfigurationError for undeclared named volumes
Test new behavior

Signed-off-by: Joffrey F <joffrey@docker.com>
2016-01-26 11:52:23 +00:00
Aanand Prasad
883227c4d8 Alias containers by short id
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-01-26 11:52:23 +00:00
Dimitar Bonev
5545c55ecc Network fields schema validation
Signed-off-by: Dimitar Bonev <dimitar.bonev@gmail.com>
2016-01-26 11:52:22 +00:00
Joffrey F
227fa5c0de Use latest docker-py rc
Signed-off-by: Joffrey F <joffrey@docker.com>
2016-01-26 11:52:22 +00:00
Aanand Prasad
d399b7893f Add test for logs=False
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>

Conflicts:
	compose/cli/main.py
2016-01-26 11:52:22 +00:00
Alf Lervag
833e16117e Fixes #2448
Signed-off-by: Alf Lervag <alf.lervag@bouvet.no>

Conflicts:
	compose/cli/main.py
	requirements.txt
2016-01-26 11:52:22 +00:00
Daniel Nephin
bf068a8287 Add stop signal to the docs.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-01-26 11:52:22 +00:00
Aanand Prasad
b1ebf5ce17 Fix interactive run with networking
Make sure we connect the container to all required networks *after*
starting the container and *before* hijacking the terminal.

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-01-26 11:52:22 +00:00
Aanand Prasad
836ec70979 Stop connecting to all networks on container creation
This relies on an Engine behaviour which is a bug, not an intentional
feature - we have to connect to networks one at a time

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-01-26 11:52:22 +00:00
Aanand Prasad
6ca410fd6b Fix 'run' behaviour with networks
- Test that one-off containers join all networks
- Don't set any aliases

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-01-26 11:52:22 +00:00
Joffrey F
38a6d04852 Update documentation for external param
Signed-off-by: Joffrey F <joffrey@docker.com>

Conflicts:
	docs/compose-file.md
2016-01-26 11:52:22 +00:00
Aanand Prasad
0952c1bb51 Add links to networks key references
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-01-26 11:52:21 +00:00
Aanand Prasad
18a1829db0 Update documentation for links
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-01-26 11:52:21 +00:00
Aanand Prasad
e40de088f3 Document depends_on
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-01-26 11:52:21 +00:00
Aanand Prasad
6fe54f5c24 Update Compose file documentation for version 2
- Explain each version in its own section
- Explain how to upgrade from version 1 to 2
- Note which keys are restricted to particular versions
- A few corrections to the docs for version-specific keys

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-01-26 11:52:21 +00:00
Aanand Prasad
5cfd947f38 Stop and remove containers in parallel when scaling down
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-01-26 11:52:21 +00:00
Aanand Prasad
513a6b35cc Fix scale when containers exit immediately
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-01-26 11:52:21 +00:00
Aanand Prasad
cbec6f8834 Support links in v2 files
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-01-26 11:52:21 +00:00
Aanand Prasad
76bc06b729 Fix Windows build failures when installing dependencies from git
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-01-26 11:52:21 +00:00
Jure Žvelc
c9ef1fa32f Fix for extending services written in v2 format.
Signed-off-by: Jure Žvelc <jzvelc@gmail.com>
2016-01-26 11:52:20 +00:00
Daniel Nephin
c52eed66b7 Update tests in sort_services_test.py to use pytest.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-01-26 11:52:20 +00:00
Daniel Nephin
3f65bdcf46 Move ulimits validation to validation.py and improve the error message.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-01-26 11:52:20 +00:00
Daniel Nephin
6e5c312768 Implement depends_on to define an order for services in the v2 format.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-01-26 11:52:20 +00:00
Daniel Nephin
33bb7c4e02 Add migration script.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-01-26 11:52:20 +00:00
Aanand Prasad
bb377d3fe6 Fix "name is reserved" with Engine 1.10 RC1
Ensure link aliases are unique (this deduping was previously performed
on the server).

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-01-26 11:52:20 +00:00
Aanand Prasad
7442b416e8 Test that you can set the default network to be external
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-01-26 11:52:20 +00:00
Aanand Prasad
fbee4ce4b3 Catch TLSParameterErrors from docker-py
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-01-26 11:52:20 +00:00
Steve Durrheimer
8398382b65 Add zsh completion for 'docker-compose up --abort-on-container-exit'
Signed-off-by: Steve Durrheimer <s.durrheimer@gmail.com>
2016-01-26 11:52:19 +00:00
Steve Durrheimer
7dd5fd5763 Add zsh completion for 'docker-compose down'
Signed-off-by: Steve Durrheimer <s.durrheimer@gmail.com>
2016-01-26 11:52:19 +00:00
Steve Durrheimer
d5f3826ec7 Fix zsh completion to ensure we have enough commands to store in the cache
Signed-off-by: Steve Durrheimer <s.durrheimer@gmail.com>
2016-01-26 11:52:19 +00:00
Daniel Nephin
20c936a251 Fix some bugs in release scripts.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-01-26 11:52:19 +00:00
Daniel Nephin
d2556a1347 Bump 1.6.0-rc1
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-01-18 14:50:56 -05:00
Aanand Prasad
237f134a00 Allow custom ipam config
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-01-18 14:50:56 -05:00
Aanand Prasad
d4720f85ef Update networking docs
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-01-18 14:03:52 -05:00
Aanand Prasad
64fc2b85cb Allow overriding of config for the default network
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-01-18 12:14:12 -05:00
Aanand Prasad
a22d248390 Quote network names in error messages
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2016-01-18 12:14:12 -05:00
Daniel Nephin
fffedfc87b Test against 1.10rc1.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-01-16 11:35:04 -05:00
Daniel Nephin
34fd042dbf Increase the timeout for all acceptance tests.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-01-15 18:12:57 -05:00
Daniel Nephin
89e31f7a8d Validate that an extended config file has the same version as the base.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2016-01-15 18:12:56 -05:00
Daniel Nephin
b446c09735 Merge remote-tracking branch 'docker/release' into bump-1.6.0-rc1 2016-01-15 17:02:06 -05:00
Daniel Nephin
8f48fa4747 Merge pull request #2491 from dnephin/bump-1.5.2
WIP: Bump 1.5.2
2015-12-03 17:11:51 -08:00
Daniel Nephin
7240ff35ee Bump 1.5.2
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-12-03 11:18:32 -08:00
Daniel Nephin
aaf66e3485 FAQ document for Compose
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-12-03 11:05:06 -08:00
Aanand Prasad
96f4a42a35 Validate the 'expose' option
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-12-03 11:05:05 -08:00
Aanand Prasad
e6fbca42a1 Split out ports validation tests into type, uniqueness, format
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-12-03 11:05:05 -08:00
Aanand Prasad
527bf3b023 Fix ports validation message
- The `raises` kwarg to the `cls_check` decorator was being used
  incorrectly (it should be an exception class, not an object).

- We need to check for `error.cause` and get the message out of the
  exception object.

NB: The particular case where validation fails in the case of `ports` is
only when ranges don't match in length - no further validation is
currently performed client-side.

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-12-03 11:05:05 -08:00
Aanand Prasad
ab36c9c6cd Refactor ports section of fields schema
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-12-03 11:05:05 -08:00
Aanand Prasad
e67419065a Fix ports validation test
We were essentially only testing that *at least one* of the invalid
values fails the validation check, rather than that *all* of them fail.

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-12-03 11:05:05 -08:00
Daniel Nephin
69e956ce8b Add integration test and docs for build with a git url.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-12-03 11:05:05 -08:00
Jonas Eckerström
0dbd99bad2 Added support for url buid paths
Signed-off-by: Jonas Eckerström <jonaseck@gmail.com>
2015-12-03 11:05:05 -08:00
Daniel Nephin
fa975d7fbe Properly resolve environment from all sources.
Split env resolving into two phases. The first phase is to expand the paths
of env_files, which is done before merging extends. Once all files are merged
together, the final phase is to read the env_files and use them as the base
for environment variables.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-12-03 11:05:01 -08:00
Daniel Nephin
81f0e72bd2 Move service sorting to config package.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-12-02 17:31:48 -08:00
Daniel Nephin
da27f8e7e2 Remove unnecessary intermediate variables in get_container_host_config.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-12-02 17:31:48 -08:00
Daniel Nephin
8572d50903 Move volume parsing to config.types module
This removes the last of the old service.ConfigError

Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-12-02 17:31:48 -08:00
Daniel Nephin
5d39813e1b Fixes #2008 - re-use list_or_dict schema for all the types
At the same time, moves extra_hosts validation to the config module.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-12-02 17:31:48 -08:00
Daniel Nephin
b19315b57e Move restart spec to the config.types module.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-12-02 17:31:48 -08:00
Daniel Nephin
e549875e89 Move parsing of volumes_from to the last step of config parsing.
Includes creating a new compose.config.types module for all the domain objects.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-12-02 17:31:48 -08:00
Daniel Nephin
7e21b05f05 Remove project name validation
project name is already normalized to a valid name before creating a service.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-12-02 17:31:48 -08:00
Daniel Nephin
bea2072b95 Add the git sha to version output
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-12-02 17:31:48 -08:00
Daniel Nephin
3b6cc7a7bb Add missing assert and autospec.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-12-02 17:31:48 -08:00
Daniel Nephin
a264470cc0 Make sure we always have the latest busybox image, so that build --pull tests don't flake.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-12-02 17:31:48 -08:00
Daniel Nephin
844e2c3d26 Fix use case link in readme.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-12-02 17:31:48 -08:00
Daniel Nephin
210a14cf28 Add note about required pip version.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-12-02 17:31:48 -08:00
Brandon Burton
9ce4024951 Fixing matrix include so os: linux goes to trusty
Signed-off-by: Brandon Burton <brandon@inatree.org>
2015-12-02 17:31:48 -08:00
Daniel Nephin
8fb6fb7b19 Fix env_file and environment when used with extends.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-12-02 17:31:48 -08:00
Daniel Nephin
83760d0e9e Handle both SIGINT and SIGTERM for docker-compose run.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-12-02 17:31:48 -08:00
Daniel Nephin
be5b7b6f0e Handle both SIGINT and SIGTERM for docker-compose up.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-12-02 17:31:48 -08:00
Daniel Nephin
e5a02d3052 Fix extra warnings on masked volumes.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-12-02 17:31:48 -08:00
Stéphane Seguin
3a395892fc Fix restart with stopped containers. Fixes #1814
Signed-off-by: Stéphane Seguin <stephseguin93@gmail.com>
2015-12-02 17:31:48 -08:00
Daniel Nephin
09f6a876cf Fixes #2398 - the build progress stream can contain empty json objects.
Previously these empty objects would hit a bug in splitting objects causing it crash.
With this fix the empty objects are returned properly.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-12-02 17:31:48 -08:00
Stefan Scherer
0117148a36 Use uname to build target name for different platforms
Signed-off-by: Stefan Scherer <scherer_stefan@icloud.com>
2015-12-02 17:31:48 -08:00
Simon van der Veldt
8f70c8cdeb run.sh script: Also pass DOCKER_TLS_VERIFY and DOCKER_CERT_PATH env vars to compose container
Signed-off-by: Simon van der Veldt <simon.vanderveldt@gmail.com>
2015-12-02 17:31:48 -08:00
Daniel Nephin
16a74f3797 Fix texttable dep. 0.8.2 was removed from pypi.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-12-02 17:31:48 -08:00
Viranch Mehta
c42918ec7c Fix specifies_host_port() to handle port binding with host IP but no host port
Signed-off-by: Viranch Mehta <viranch.mehta@gmail.com>
2015-12-02 17:31:48 -08:00
Mazz Mosley
d28b2027b8 Clarify dockerfile requires build key
Credit to @funkyfuture for the first PR addressing the clarification.
https://github.com/docker/compose/pull/1767

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-12-02 17:31:48 -08:00
Mazz Mosley
8d816fc2f3 Add cross references for env/cli
Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-12-02 17:31:48 -08:00
Daniel Nephin
f476436027 Merge remote-tracking branch 'docker/release' into bump-1.5.2 2015-12-02 16:56:55 -08:00
Daniel Nephin
fae20305ec Merge pull request #2384 from dnephin/bump-1.5.1
**WIP** Bump 1.5.1
2015-11-12 17:29:43 -05:00
Daniel Nephin
4628e93fb2 Bump 1.5.1
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-12 15:02:12 -05:00
Daniel Nephin
82086a4e92 Remove name field from the list of ALLOWED_KEYS
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-12 15:02:12 -05:00
Daniel Nephin
96e9b47059 Inclide the filename in validation errors.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-12 15:02:12 -05:00
Daniel Nephin
34166ef5a4 Refactor process_errors into smaller functions
So that it passed new max-complexity requirement

Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-12 15:02:12 -05:00
Daniel Nephin
285e52cc7c Add ids to config schemas
Also enforce a max complexity for functions and add some new tests for config.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-12 15:02:12 -05:00
Joffrey F
d52c969f94 Add test for environment variable dashes support
Signed-off-by: Joffrey F <joffrey@docker.com>
2015-11-12 13:54:41 -05:00
Joffrey F
63c3e6f58c Allow dashes in environment variable names
See http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html
Environment variable names used by the utilities in the Shell and
Utilities volume of POSIX.1-2008 consist solely of uppercase letters,
digits, and the <underscore> ( '_' ) from the characters defined in
Portable Character Set and do not begin with a digit. Other characters may
be permitted by an implementation; applications shall tolerate the
presence of such names.

Signed-off-by: Joffrey F <joffrey@docker.com>
2015-11-12 13:54:41 -05:00
Daniel Nephin
0ab76bb8bc Add a test for invalid field 'name', and fix an existing test for invalid service names.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-12 13:54:41 -05:00
Daniel Nephin
7fc577c31d Remove name from config schema.
Refactors config validation of a service to use a ServiceConfig data object.
Instead of passing around a bunch of related scalars, we can use the
ServiceConfig object as a parameter to most of the service validation functions.

This allows for a fix to the config schema, where the name is a field in the
schema, but not actually in the configuration. My passing the name around as
part of the ServiceConfig object, we don't need to add it to the config options.
Fixes #2299

validate_against_service_schema() is moved from a conditional branch in
ServiceExtendsResolver() to happen as one of the last steps after all
configuration is merged. This schema only contains constraints which only need
to be true at the very end of merging.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-12 13:54:41 -05:00
Daniel Nephin
3a43110f06 Fix a bug in ExtendsResolver where the service name of the extended service was wrong.
This bug can be seen by the change to the test case. When the extended service
uses a different name, the error was reported incorrectly.

By fixing this bug we can simplify self.signature and self.detect_cycles to
always use self.service_name.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-12 13:54:41 -05:00
Daniel Nephin
87d79d4d99 Rename ServiceLoader to ServiceExtendsResolver
ServiceLoader has evolved to be not really all that related to "loading" a
service. It's responsibility is more to do with handling the `extends`
field, which is only part of loading.  The class and its primary method
(make_service_dict()) were renamed to better reflect their responsibility.

As part of that change process_container_options() was removed from
make_service_dict() and renamed to process_service().  It contains logic for
handling the non-extends options.

This change allows us to remove the hacks from testcase.py and only call
the functions we need to format a service dict correctly for integration tests.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-12 13:54:41 -05:00
Daniel Nephin
83581c3a0f Validate additional files before merging them.
Consolidates all the top level config handling into `process_config_file` which
is now used for both files and merge sources.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-12 13:54:41 -05:00
Joffrey F
ba90f55075 Reorganize conditional branches to improve readability
Signed-off-by: Joffrey F <joffrey@docker.com>
2015-11-12 13:54:41 -05:00
Yves Peter
3313dcb1ce Fixes #1490 progress_stream would print a lot of new lines on "docker-compose pull" if there's no tty.
Signed-off-by: Yves Peter <ypdraw@gmail.com>
2015-11-12 13:54:41 -05:00
Daniel Nephin
92d56fab47 Add a warning when the host volume config is being ignored.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-12 13:54:41 -05:00
Daniel Nephin
1208f92d9c Update doc wording for ulimits.
and move tests to the correct module

Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-12 13:54:41 -05:00
Kevin Greene
8444551373 Added ulimits functionality to docker compose
Signed-off-by: Kevin Greene <kevin@spantree.net>
2015-11-12 13:54:41 -05:00
Daniel Nephin
73ebd7e560 Only create the default network if at least one service needs it.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-12 13:54:41 -05:00
Daniel Nephin
0a96f86f74 Cleanup workaround in testcase.py
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-12 13:54:41 -05:00
Daniel Nephin
36176befb0 Fix #1549 - flush after each line of logs.
Includes some refactoring of log_printer_test to support checking for flush(), and so that each test calls the unit-under-test directly, instead of through a helper function.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-12 13:54:41 -05:00
Daniel Nephin
de08da278d Re-order flags in bash completion
and remove unnecessary variables from build command.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-12 13:54:41 -05:00
Adrian Budau
4c2eb17ccd Added --force-rm to compose build.
It's a flag passed to docker build that removes the intermediate
containers left behind on fail builds.

Signed-off-by: Adrian Budau <budau.adi@gmail.com>
2015-11-12 13:54:41 -05:00
Daniel Nephin
8fb44db92b Cleanup some unit tests and whitespace.
Remove some unnecessary newlines.
Remove a unittest that was attempting to test behaviour that was removed a while ago, so isn't testing anything.
Updated some unit tests to use mocks instead of a custom fake.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-12 13:54:41 -05:00
Daniel Nephin
4105c3017c Move cli tests to a new testing package.
These cli tests are now a different kind of that that run the compose binary. They are not the same as integration tests that test some internal interface.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-12 13:54:40 -05:00
Daniel Nephin
7f2f4eef48 Update cli tests to use subprocess.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-12 13:54:40 -05:00
Joffrey F
666c3cb1c7 Use exit code 1 when encountering a ReadTimeout
Signed-off-by: Joffrey F <joffrey@docker.com>
2015-11-12 13:54:40 -05:00
Daniel Nephin
886134c1f3 Recreate dependents when a dependency is created (not just when it's recreated).
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-12 13:54:40 -05:00
Daniel Nephin
ba61a6c5fb Don't set the hostname to the service name with networking.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-12 13:54:40 -05:00
Daniel Nephin
3f14df374f Handle non-utf8 unicode without raising an error.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-12 13:54:40 -05:00
Daniel Nephin
e6755d1e7c Use VolumeSpec instead of re-parsing the volume string.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-12 13:54:40 -05:00
Daniel Nephin
c4f59e731d Make working_dir consistent in the config package.
- make it a positional arg, since it's required
- make it the first argument for all functions that require it
- remove an unnecessary one-line function that was only called in one place

Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-12 13:54:39 -05:00
Daniel Nephin
805ed344c0 Refactor ServiceLoader to be immutable.
Mutable objects are harder to debug and harder to reason about. ServiceLoader was almost immutable. There was just a single function which set fields for a second function. Instead of mutating the object, we can pass those values as parameters to the next function.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-12 13:54:39 -05:00
Daniel Nephin
a5959d9be2 Some minor style cleanup
- fixed a docstring to make it PEP257 compliant
- wrapped some long lines
- used a more specific error

Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-12 13:54:39 -05:00
Mazz Mosley
0375dccf64 Handle non-ascii chars in volume directories
Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-11-12 13:54:39 -05:00
Daniel Nephin
3c4bb5358e Upgrade pyyaml to 3.11
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-12 13:54:39 -05:00
Daniel Nephin
e317d2db9d Remove service.start_container()
It has been an unnecessary wrapper around container.start() for a little while now, so we can call it directly.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-12 13:54:39 -05:00
Joffrey F
3daecfa8e4 Update service config_dict computation to include volumes_from mode
Ensure config_hash is updated when volumes_from mode is changed, and
service is recreated on next up as a result.

Signed-off-by: Joffrey F <joffrey@docker.com>
2015-11-12 13:54:39 -05:00
Aanand Prasad
cf93362368 Fix parallel output
We were outputting an extra line, which in *some* cases, on *some*
terminals, was causing the output of parallel actions to get messed up.

In particular, it would happen when the terminal had just been cleared
or hadn't yet filled up with a screen's worth of text.

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-11-12 13:54:39 -05:00
Daniel Nephin
23d4eda2a5 Fix service recreate when image changes to build.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-12 13:54:39 -05:00
Daniel Nephin
718ae13ae1 Move config hash tests to service_test.py
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-12 13:54:39 -05:00
Daniel Nephin
cddbe9fbf1 Merge remote-tracking branch 'docker/release' into bump-1.5.1 2015-11-12 13:03:47 -05:00
Aanand Prasad
9c8173dbfd Merge pull request #2309 from dnephin/bump-1.5.0
WIP: Bump 1.5.0
2015-11-03 18:31:56 +00:00
Daniel Nephin
77ff37a853 Bump 1.5.0
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-03 12:36:30 -05:00
Daniel Nephin
40341674bd Re-order extends docs.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-03 12:36:30 -05:00
Daniel Nephin
62ebdce5a9 Replace composition with Compose app.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-03 12:36:30 -05:00
Daniel Nephin
58de4e0c26 Document using multiple Compose files use cases.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-03 12:36:30 -05:00
Daniel Nephin
dbd6c62b70 Changes to production.md for working with multiple Compose files.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-03 12:36:30 -05:00
Daniel Nephin
887c6753f8 Support a volume to the docs directory and add --watch, so docs can be refreshed.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-03 12:36:30 -05:00
Daniel Nephin
bd35896892 Remove duplication from extends docs.
Start restructuring extends docs in preparation for adding documentation about using multiple compose files.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-03 12:36:30 -05:00
Daniel Nephin
9286e62449 On a test failure only show the last 100 lines of daemon output.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-03 11:54:59 -05:00
Daniel Nephin
621d1a5167 Fix networking tests to work with new API in engine rc4 (https://github.com/docker/docker/pull/17536)
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-03 11:54:59 -05:00
Daniel Nephin
83714fbac2 Touch up intro paragraph with feedback from @moxiegirl.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-03 11:33:23 -05:00
Daniel Nephin
413921a287 Add another feature to the docs - multiple environments per host.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-03 11:33:23 -05:00
Daniel Nephin
7ee36829ac Update intro docs based on feedback.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-03 11:33:23 -05:00
Daniel Nephin
bfb46b37d3 Updates to gettingstarted guide from PR feedback.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-03 11:33:23 -05:00
Daniel Nephin
09d2bdbb21 Flush out features and use cases.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-03 11:33:23 -05:00
Daniel Nephin
8733d09a9c Extract the getting started guide from the index page.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-03 11:33:23 -05:00
Daniel Nephin
e524cce222 Add missing title to compose file reference.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-11-03 11:33:23 -05:00
Daniel Nephin
be6b811c4e Bump 1.5.0rc3
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-10-30 15:53:17 -04:00
Daniel Nephin
bdb9a280bc Make storage driver configurable in CI
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-10-30 15:53:16 -04:00
Daniel Nephin
73ca4eb599 On error print daemon logs
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-10-30 15:46:57 -04:00
Daniel Nephin
569ccbadec Convert the README to rst and fix the logo url before packaging it up for pypi.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-10-30 15:46:57 -04:00
Daniel Nephin
ed1b584c42 Fix release script notes about software and typos.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-10-30 15:46:57 -04:00
Daniel Nephin
2f2e946907 Don't set a default network driver, let the server decide.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-10-30 15:36:15 -04:00
Daniel Nephin
d392f70cc6 Fixes #1843, #1936 - chown files back to host user in django example.
Also add a missing 'touch Gemfile.lock' to fix the rails tutorial.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-10-30 15:36:15 -04:00
Daniel Nephin
db164cefd3 Remove the duplicate 'Warning' prefix now that the logger adds the prefix.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-10-30 15:36:15 -04:00
Daniel Nephin
4d613d3ba7 Use colors when logging warnings or errors, so they are more obvious.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-10-30 15:36:15 -04:00
Joffrey F
1f26841e23 Integration test for run command with networking enabled
Signed-off-by: Joffrey F <joffrey@docker.com>
2015-10-30 15:36:15 -04:00
Joffrey F
9370cb033c Ensure network exists when calling run before up
Otherwise the daemon will error out because the network doesn't exist
yet.

Signed-off-by: Joffrey F <joffrey@docker.com>
2015-10-30 15:36:15 -04:00
Mazz Mosley
ab0ddb593f Clarify the command is an example
Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-10-29 15:01:33 -04:00
Daniel Nephin
f67503d9fd Logs are available for all log drivers except for none.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-10-29 15:01:33 -04:00
Daniel Nephin
ce729b0721 Update docs about networking for current release.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-10-29 15:01:33 -04:00
Daniel Nephin
8156cdc56e Disable a test against docker 1.8.3 because it fails due to a bug in docker engine.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-10-29 15:01:33 -04:00
Daniel Nephin
8cc8e61474 Bump 1.5.0rc2
Signed-off-by: Daniel Nephin <dnephin@docker.com>

Fill out 1.5.0 release notes

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-10-27 19:48:54 -04:00
Sven Dowideit
29b0ffe5e9 Possible link fixes
Signed-off-by: Sven Dowideit <SvenDowideit@home.org.au>
2015-10-27 19:33:28 -04:00
Mazz Mosley
a772a0d7d7 Remove redundant try/except
Code cleanup. We no longer need this as the api returns a 304 for any
stopped containers, which doesn't raise an error.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-10-27 19:33:28 -04:00
Mazz Mosley
6f0096c87b Move rename functionality into Container
Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-10-27 19:33:28 -04:00
Mazz Mosley
da41ed22f9 Fix tests
Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-10-27 19:33:28 -04:00
Mazz Mosley
d6fa8596d2 Attach to a container's log_stream before they're started
So we're not displaying output of all previous logs for a container, we attach,
if possible, to a container before the container is started.

LogPrinter checks if a container has a log_stream already attached and
print from that rather than always attempting to attach one itself.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-10-27 19:33:28 -04:00
Daniel Nephin
a9b4fe768d Fix running one-off containers with --x-networking by disabling linking to self.
docker create fails if networking and links are used together.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-10-27 19:33:28 -04:00
Daniel Nephin
88e53e177d Upgrade pyinstaller to 3.0
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-10-26 13:05:41 -04:00
Daniel Nephin
e168fd03ca Fix unicode in environment variables for python2.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-10-26 13:05:41 -04:00
Daniel Nephin
95a23eb682 Change version check from engine version to api version.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-10-26 13:05:40 -04:00
Daniel Nephin
f5ad363143 Use inspect network to query for an existing network.
And more tests for get_network()

Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-10-26 13:05:40 -04:00
Daniel Nephin
f290faf4ba Minor refactor to use guard and replace instead of split+join
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-10-21 11:29:49 -04:00
Daniel Nephin
725088a18b Force windows drives to be lowercase.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-10-21 11:29:49 -04:00
Daniel Nephin
bf672ec340 Fixes #2205 - extends must be copied from override file.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-10-20 11:27:11 -04:00
Mazz Mosley
0e4f9c9a66 Environment keys can contain empty values
Environment keys that contain no value, get populated with values taken
from the environment not from the build phase but from running the command `up`.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-10-19 15:16:19 -04:00
Mazz Mosley
5fdb75b541 Improve error message for type constraints
It was missing a space between the different types, when there
were 3 possible type values.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-10-19 15:16:19 -04:00
Daniel Nephin
24d4a1045a Fixes #2203 - properly validate files when multiple files are used.
Remove the single-use decorators so the functionality can be used directly as a function.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-10-19 15:16:19 -04:00
Daniel Nephin
514f0650b2 Give the user a better error message (without a stack trace) when there is a yaml error.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-10-19 12:02:40 -04:00
Steve Durrheimer
20d34c8b14 Add zsh completion for 'docker-compose --x-networking --x-network-driver'
Signed-off-by: Steve Durrheimer <s.durrheimer@gmail.com>
2015-10-19 10:38:22 -04:00
Harald Albers
6f45eb7959 bash completion for networking options
Signed-off-by: Harald Albers <github@albersweb.de>
2015-10-19 10:11:35 -04:00
Mazz Mosley
49b98fa111 Attempt to document escaping env vars
People are likely to run into their env vars being set to empty strings,
if they're not aware that they need to escape them for Compose to not
interpolate them.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-10-16 15:52:32 -04:00
Cameron Eagans
6048630a11 docker-compose pull SERVICE should not pull SERVICE's dependencies
Signed-off-by: Cameron Eagans <me@cweagans.net>
2015-10-16 12:59:57 -04:00
Aanand Prasad
46de4411a7 Revert networking-related changes to getting started guides
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-10-16 12:59:57 -04:00
Karol Duleba
883f251e7d Docs for shorthand notation of extends. Issue #1989
Signed-off-by: Karol Duleba <mr.fuxi@gmail.com>
2015-10-16 12:59:57 -04:00
Daniel Nephin
284cda087e Add missing merge for release branch.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-10-16 12:59:57 -04:00
Daniel Nephin
b2f9c182f3 Fix some release docs.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-10-16 12:59:57 -04:00
Daniel Nephin
558098d322 Add a script to generate contributor list.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-10-16 12:59:57 -04:00
Per Persson
6571e079b9 Remove incorrectly placed comment
I'm not sure if it should be there at all, but at least it should hardly be where it currently is located.

Signed-off-by: Per Persson <per.persson@exertisztorm.com>
2015-10-16 12:59:57 -04:00
Tim Butler
49ca23c034 Fix link to Release Process doc in README.md
Signed-off-by: Tim Butler <tim.butler.au@gmail.com>
2015-10-16 12:59:57 -04:00
Daniel Nephin
709bd9c363 Bump 1.5.0rc1
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-10-14 12:59:31 -04:00
Daniel Nephin
8cecf2e02d Merge remote-tracking branch 'docker/release' into bump-1.5.0rc1 2015-10-14 12:59:25 -04:00
Daniel Nephin
d59c759cdd Merge pull request #2082 from dnephin/bump-1.4.2
[WIP] Release 1.4.2
2015-09-22 14:17:04 -04:00
Daniel Nephin
7b5d5fcd58 Bump 1.4.2
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-09-22 12:26:51 -04:00
Daniel Nephin
d01f712376 Fix a test case that assumes busybox image id.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-09-22 12:26:51 -04:00
Daniel Nephin
ac75d35927 Fix #1961 - docker-compose up should attach to all containers with no service names are specified, and add tests.
Signed-off-by: Daniel Nephin <dnephin@gmail.com>
2015-09-22 11:09:59 -04:00
Daniel Nephin
605d7f26e7 Merge pull request #2027 from dnephin/bump-1.4.1
Bump 1.4.1
2015-09-15 17:47:09 -04:00
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
aa7c7cdf93 Merge pull request #1763 from aanand/bump-1.4.0
Bump 1.4.0
2015-08-11 18:38:49 +01: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
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
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
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
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
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
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
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
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
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
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
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
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
ef6555f084 Merge branch 'release' into bump-1.3.0 2015-05-26 17:45:28 +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
ed549155b3 Merge pull request #1159 from aanand/bump-1.2.0
Bump 1.2.0
2015-04-16 17:46:47 +01:00
Aanand Prasad
39ae91c81c Bump 1.2.0
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-04-16 13:53:27 +01:00
Daniel Nephin
b6acb3cd8c Merge pull request #1278 from albers/completion-run-user
Add bash completion for docker-compose run --user
(cherry picked from commit 3cd116b99d)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-04-16 13:50:33 +01:00
Ben Firshman
a467a8a094 Merge pull request #1261 from aanand/fix-vars-in-volume-paths
Fix vars in volume paths
(cherry picked from commit 4926f8aef6)

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

Conflicts:
	tests/unit/service_test.py
2015-04-09 15:26:10 +01:00
Aanand Prasad
78227c3c06 Merge pull request #1202 from aanand/jenkins-script
WIP: Jenkins script
(cherry picked from commit 853ce255ea)
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-04-01 11:00:51 -04:00
Daniel Nephin
e4e802d1f8 Merge pull request #1213 from moysesb/relative_build
Make value of 'build:' relative to the yml file.
(cherry picked from commit 0f70b8638f)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-04-01 11:00:48 -04:00
Aanand Prasad
b24a60ba9f Merge pull request #1226 from aanand/merge-multi-value-options
Merge multi-value options when extending
(cherry picked from commit e708f4f59d)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-04-01 11:00:45 -04:00
Aanand Prasad
461b600068 Merge pull request #1225 from aanand/fix-1222
When extending, `build` replaces `image` and vice versa
(cherry picked from commit 6dbe321a45)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-04-01 11:00:40 -04:00
91 changed files with 4360 additions and 1640 deletions

View file

@ -1,6 +1,178 @@
Change log
==========
1.6.1 (2016-02-23)
------------------
Bug Fixes
- Fixed a bug where recreating a container multiple times would cause the
new container to be started without the previous volumes.
- Fixed a bug where Compose would set the value of unset environment variables
to an empty string, instead of a key without a value.
- Provide a better error message when Compose requires a more recent version
of the Docker API.
- Add a missing config field `network.aliases` which allows setting a network
scoped alias for a service.
- Fixed a bug where `run` would not start services listed in `depends_on`.
- Fixed a bug where `networks` and `network_mode` where not merged when using
extends or multiple Compose files.
- Fixed a bug with service aliases where the short container id alias was
only contained 10 characters, instead of the 12 characters used in previous
versions.
- Added a missing log message when creating a new named volume.
- Fixed a bug where `build.args` was not merged when using `extends` or
multiple Compose files.
- Fixed some bugs with config validation when null values or incorrect types
were used instead of a mapping.
- Fixed a bug where a `build` section without a `context` would show a stack
trace instead of a helpful validation message.
- Improved compatibility with swarm by only setting a container affinity to
the previous instance of a services' container when the service uses an
anonymous container volume. Previously the affinity was always set on all
containers.
- Fixed the validation of some `driver_opts` would cause an error if a number
was used instead of a string.
- Some improvements to the `run.sh` script used by the Compose container install
option.
- Fixed a bug with `up --abort-on-container-exit` where Compose would exit,
but would not stop other containers.
- Corrected the warning message that is printed when a boolean value is used
as a value in a mapping.
1.6.0 (2016-01-15)
------------------
Major Features:
- Compose 1.6 introduces a new format for `docker-compose.yml` which lets
you define networks and volumes in the Compose file as well as services. It
also makes a few changes to the structure of some configuration options.
You don't have to use it - your existing Compose files will run on Compose
1.6 exactly as they do today.
Check the upgrade guide for full details:
https://docs.docker.com/compose/compose-file#upgrading
- Support for networking has exited experimental status and is the recommended
way to enable communication between containers.
If you use the new file format, your app will use networking. If you aren't
ready yet, just leave your Compose file as it is and it'll continue to work
just the same.
By default, you don't have to configure any networks. In fact, using
networking with Compose involves even less configuration than using links.
Consult the networking guide for how to use it:
https://docs.docker.com/compose/networking
The experimental flags `--x-networking` and `--x-network-driver`, introduced
in Compose 1.5, have been removed.
- You can now pass arguments to a build if you're using the new file format:
build:
context: .
args:
buildno: 1
- You can now specify both a `build` and an `image` key if you're using the
new file format. `docker-compose build` will build the image and tag it with
the name you've specified, while `docker-compose pull` will attempt to pull
it.
- There's a new `events` command for monitoring container events from
the application, much like `docker events`. This is a good primitive for
building tools on top of Compose for performing actions when particular
things happen, such as containers starting and stopping.
- There's a new `depends_on` option for specifying dependencies between
services. This enforces the order of startup, and ensures that when you run
`docker-compose up SERVICE` on a service with dependencies, those are started
as well.
New Features:
- Added a new command `config` which validates and prints the Compose
configuration after interpolating variables, resolving relative paths, and
merging multiple files and `extends`.
- Added a new command `create` for creating containers without starting them.
- Added a new command `down` to stop and remove all the resources created by
`up` in a single command.
- Added support for the `cpu_quota` configuration option.
- Added support for the `stop_signal` configuration option.
- Commands `start`, `restart`, `pause`, and `unpause` now exit with an
error status code if no containers were modified.
- Added a new `--abort-on-container-exit` flag to `up` which causes `up` to
stop all container and exit once the first container exits.
- Removed support for `FIG_FILE`, `FIG_PROJECT_NAME`, and no longer reads
`fig.yml` as a default Compose file location.
- Removed the `migrate-to-labels` command.
- Removed the `--allow-insecure-ssl` flag.
Bug Fixes:
- Fixed a validation bug that prevented the use of a range of ports in
the `expose` field.
- Fixed a validation bug that prevented the use of arrays in the `entrypoint`
field if they contained duplicate entries.
- Fixed a bug that caused `ulimits` to be ignored when used with `extends`.
- Fixed a bug that prevented ipv6 addresses in `extra_hosts`.
- Fixed a bug that caused `extends` to be ignored when included from
multiple Compose files.
- Fixed an incorrect warning when a container volume was defined in
the Compose file.
- Fixed a bug that prevented the force shutdown behaviour of `up` and
`logs`.
- Fixed a bug that caused `None` to be printed as the network driver name
when the default network driver was used.
- Fixed a bug where using the string form of `dns` or `dns_search` would
cause an error.
- Fixed a bug where a container would be reported as "Up" when it was
in the restarting state.
- Fixed a confusing error message when DOCKER_CERT_PATH was not set properly.
- Fixed a bug where attaching to a container would fail if it was using a
non-standard logging driver (or none at all).
1.5.2 (2015-12-03)
------------------

View file

@ -6,11 +6,11 @@ Compose is a tool for defining and running multi-container Docker applications.
With Compose, you use a Compose file to configure your application's services.
Then, using a single command, you create and start all the services
from your configuration. To learn more about all the features of Compose
see [the list of features](docs/index.md#features).
see [the list of features](https://github.com/docker/compose/blob/release/docs/overview.md#features).
Compose is great for development, testing, and staging environments, as well as
CI workflows. You can learn more about each case in
[Common Use Cases](docs/index.md#common-use-cases).
[Common Use Cases](https://github.com/docker/compose/blob/release/docs/overview.md#common-use-cases).
Using Compose is basically a three-step process.
@ -34,7 +34,7 @@ A `docker-compose.yml` looks like this:
image: redis
For more information about the Compose file, see the
[Compose file reference](docs/compose-file.md)
[Compose file reference](https://github.com/docker/compose/blob/release/docs/compose-file.md)
Compose has commands for managing the whole lifecycle of your application:

View file

@ -1,39 +1 @@
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, 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.
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.
Building
--------
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.
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 build -t myusername/web .
$ docker push myusername/web
$ cat docker-compose.yml
web:
image: myusername/web
$ docker-compose up -d
$ docker-compose scale web=3
Scheduling
----------
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.
environment:
# Schedule containers on a node that has the 'storage' label set to 'ssd'
- "constraint:storage==ssd"
# Schedule containers where the 'redis' image is already pulled
- "affinity:image==redis"
For the full set of available filters and expressions, see the [Swarm documentation](https://docs.docker.com/swarm/scheduler/filter/).
This file has moved to: https://docs.docker.com/compose/swarm/

View file

@ -1,4 +1,4 @@
from __future__ import absolute_import
from __future__ import unicode_literals
__version__ = '1.6.0dev'
__version__ = '1.6.1'

View file

@ -92,7 +92,7 @@ def get_project_name(working_dir, project_name=None):
return re.sub(r'[^a-z0-9]', '', name.lower())
project_name = project_name or os.environ.get('COMPOSE_PROJECT_NAME')
if project_name is not None:
if project_name:
return normalize_name(project_name)
project = os.path.basename(os.path.abspath(working_dir))

View file

@ -5,9 +5,11 @@ import logging
import os
from docker import Client
from docker.errors import TLSParameterError
from docker.utils import kwargs_from_env
from ..const import HTTP_TIMEOUT
from .errors import UserError
log = logging.getLogger(__name__)
@ -20,8 +22,16 @@ def docker_client(version=None):
if 'DOCKER_CLIENT_TIMEOUT' in os.environ:
log.warn('The DOCKER_CLIENT_TIMEOUT environment variable is deprecated. Please use COMPOSE_HTTP_TIMEOUT instead.')
kwargs = kwargs_from_env(assert_hostname=False)
try:
kwargs = kwargs_from_env(assert_hostname=False)
except TLSParameterError:
raise UserError(
'TLS configuration is invalid - make sure your DOCKER_TLS_VERIFY and DOCKER_CERT_PATH are set correctly.\n'
'You might need to run `eval "$(docker-machine env default)"`')
if version:
kwargs['version'] = version
kwargs['timeout'] = HTTP_TIMEOUT
return Client(**kwargs)

View file

@ -2,6 +2,7 @@ from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals
import contextlib
import json
import logging
import re
@ -18,6 +19,7 @@ from ..config import config
from ..config import ConfigurationError
from ..config import parse_environment
from ..config.serialize import serialize_config
from ..const import API_VERSION_TO_ENGINE_VERSION
from ..const import DEFAULT_TIMEOUT
from ..const import HTTP_TIMEOUT
from ..const import IS_WINDOWS_PLATFORM
@ -41,7 +43,7 @@ from .utils import yesno
if not IS_WINDOWS_PLATFORM:
import dockerpty
from dockerpty.pty import PseudoTerminal, RunOperation
log = logging.getLogger(__name__)
console_handler = logging.StreamHandler(sys.stderr)
@ -53,7 +55,7 @@ def main():
command = TopLevelCommand()
command.sys_dispatch()
except KeyboardInterrupt:
log.error("\nAborting.")
log.error("Aborting.")
sys.exit(1)
except (UserError, NoSuchService, ConfigurationError) as e:
log.error(e.msg)
@ -63,7 +65,7 @@ def main():
log.error("No such command: %s\n\n%s", e.command, commands)
sys.exit(1)
except APIError as e:
log.error(e.explanation)
log_api_error(e)
sys.exit(1)
except BuildError as e:
log.error("Service '%s' failed to build: %s" % (e.service.name, e.reason))
@ -83,6 +85,22 @@ def main():
sys.exit(1)
def log_api_error(e):
if 'client is newer than server' in e.explanation:
# we need JSON formatted errors. In the meantime...
# TODO: fix this by refactoring project dispatch
# http://github.com/docker/compose/pull/2832#commitcomment-15923800
client_version = e.explanation.split('client API version: ')[1].split(',')[0]
log.error(
"The engine version is lesser than the minimum required by "
"compose. Your current project requires a Docker Engine of "
"version {version} or superior.".format(
version=API_VERSION_TO_ENGINE_VERSION[client_version]
))
else:
log.error(e.explanation)
def setup_logging():
root_logger = logging.getLogger()
root_logger.addHandler(console_handler)
@ -629,18 +647,24 @@ class TopLevelCommand(DocoptCommand):
if detached and cascade_stop:
raise UserError("--abort-on-container-exit and -d cannot be combined.")
to_attach = project.up(
service_names=service_names,
start_deps=start_deps,
strategy=convergence_strategy_from_opts(options),
do_build=not options['--no-build'],
timeout=timeout,
detached=detached
)
with up_shutdown_context(project, service_names, timeout, detached):
to_attach = project.up(
service_names=service_names,
start_deps=start_deps,
strategy=convergence_strategy_from_opts(options),
do_build=not options['--no-build'],
timeout=timeout,
detached=detached)
if not detached:
if detached:
return
log_printer = build_log_printer(to_attach, service_names, monochrome, cascade_stop)
attach_to_logs(project, log_printer, service_names, timeout)
print("Attaching to", list_containers(log_printer.containers))
log_printer.run()
if cascade_stop:
print("Aborting on container exit...")
project.stop(service_names=service_names, timeout=timeout)
def version(self, project, options):
"""
@ -683,14 +707,14 @@ def image_type_from_opt(flag, value):
def run_one_off_container(container_options, project, service, options):
if not options['--no-deps']:
deps = service.get_linked_service_names()
deps = service.get_dependency_names()
if deps:
project.up(
service_names=deps,
start_deps=True,
strategy=ConvergenceStrategy.never)
project.initialize_networks()
project.initialize()
container = service.create_container(
quiet=True,
@ -709,8 +733,16 @@ def run_one_off_container(container_options, project, service, options):
signals.set_signal_handler_to_shutdown()
try:
try:
dockerpty.start(project.client, container.id, interactive=not options['-T'])
service.connect_container_to_networks(container)
operation = RunOperation(
project.client,
container.id,
interactive=not options['-T'],
logs=False,
)
pty = PseudoTerminal(project.client, operation)
sockets = pty.sockets()
service.start_container(container)
pty.start(sockets)
exit_code = container.wait()
except signals.ShutdownException:
project.client.stop(container.id)
@ -733,13 +765,16 @@ def build_log_printer(containers, service_names, monochrome, cascade_stop):
return LogPrinter(containers, monochrome=monochrome, cascade_stop=cascade_stop)
def attach_to_logs(project, log_printer, service_names, timeout):
print("Attaching to", list_containers(log_printer.containers))
signals.set_signal_handler_to_shutdown()
@contextlib.contextmanager
def up_shutdown_context(project, service_names, timeout, detached):
if detached:
yield
return
signals.set_signal_handler_to_shutdown()
try:
try:
log_printer.run()
yield
except signals.ShutdownException:
print("Gracefully stopping... (press Ctrl+C again to force)")
project.stop(service_names=service_names, timeout=timeout)

View file

@ -14,22 +14,31 @@ import six
import yaml
from cached_property import cached_property
from ..const import COMPOSEFILE_VERSIONS
from ..const import COMPOSEFILE_V1 as V1
from ..const import COMPOSEFILE_V2_0 as V2_0
from ..utils import build_string_dict
from .errors import CircularReference
from .errors import ComposeFileNotFound
from .errors import ConfigurationError
from .errors import VERSION_EXPLANATION
from .interpolation import interpolate_environment_variables
from .sort_services import get_service_name_from_net
from .sort_services import get_container_name_from_network_mode
from .sort_services import get_service_name_from_network_mode
from .sort_services import sort_service_dicts
from .types import parse_extra_hosts
from .types import parse_restart_spec
from .types import ServiceLink
from .types import VolumeFromSpec
from .types import VolumeSpec
from .validation import match_named_volumes
from .validation import validate_against_fields_schema
from .validation import validate_against_service_schema
from .validation import validate_config_section
from .validation import validate_depends_on
from .validation import validate_extends_file_path
from .validation import validate_network_mode
from .validation import validate_top_level_object
from .validation import validate_top_level_service_objects
from .validation import validate_ulimits
DOCKER_CONFIG_KEYS = [
@ -78,9 +87,8 @@ ALLOWED_KEYS = DOCKER_CONFIG_KEYS + [
'build',
'container_name',
'dockerfile',
'expose',
'external_links',
'logging',
'network_mode',
]
DOCKER_VALID_URL_PREFIXES = (
@ -98,6 +106,7 @@ SUPPORTED_FILENAMES = [
DEFAULT_OVERRIDE_FILENAME = 'docker-compose.override.yml'
log = logging.getLogger(__name__)
@ -124,24 +133,48 @@ class ConfigFile(namedtuple('_ConfigFile', 'filename config')):
@cached_property
def version(self):
if self.config is None:
return 1
version = self.config.get('version', 1)
if 'version' not in self.config:
return V1
version = self.config['version']
if isinstance(version, dict):
log.warn("Unexpected type for field 'version', in file {} assuming "
"version is the name of a service, and defaulting to "
"Compose file version 1".format(self.filename))
return 1
log.warn('Unexpected type for "version" key in "{}". Assuming '
'"version" is the name of a service, and defaulting to '
'Compose file version 1.'.format(self.filename))
return V1
if not isinstance(version, six.string_types):
raise ConfigurationError(
'Version in "{}" is invalid - it should be a string.'
.format(self.filename))
if version == '1':
raise ConfigurationError(
'Version in "{}" is invalid. {}'
.format(self.filename, VERSION_EXPLANATION))
if version == '2':
version = V2_0
if version != V2_0:
raise ConfigurationError(
'Version in "{}" is unsupported. {}'
.format(self.filename, VERSION_EXPLANATION))
return version
def get_service(self, name):
return self.get_service_dicts()[name]
def get_service_dicts(self):
return self.config if self.version == 1 else self.config.get('services', {})
return self.config if self.version == V1 else self.config.get('services', {})
def get_volumes(self):
return {} if self.version == 1 else self.config.get('volumes', {})
return {} if self.version == V1 else self.config.get('volumes', {})
def get_networks(self):
return {} if self.version == 1 else self.config.get('networks', {})
return {} if self.version == V1 else self.config.get('networks', {})
class Config(namedtuple('_Config', 'version services volumes networks')):
@ -188,10 +221,10 @@ def find(base_dir, filenames):
[ConfigFile.from_filename(f) for f in filenames])
def validate_config_version(config_details):
main_file = config_details.config_files[0]
def validate_config_version(config_files):
main_file = config_files[0]
validate_top_level_object(main_file)
for next_file in config_details.config_files[1:]:
for next_file in config_files[1:]:
validate_top_level_object(next_file)
if main_file.version != next_file.version:
@ -203,10 +236,6 @@ def validate_config_version(config_details):
next_file.filename,
next_file.version))
if main_file.version not in COMPOSEFILE_VERSIONS:
raise ConfigurationError(
'Invalid Compose file version: {0}'.format(main_file.version))
def get_default_config_files(base_dir):
(candidates, path) = find_candidates_in_parent_dirs(SUPPORTED_FILENAMES, base_dir)
@ -254,7 +283,7 @@ def load(config_details):
Return a fully interpolated, extended and validated configuration.
"""
validate_config_version(config_details)
validate_config_version(config_details.config_files)
processed_files = [
process_config_file(config_file)
@ -263,13 +292,21 @@ def load(config_details):
config_details = config_details._replace(config_files=processed_files)
main_file = config_details.config_files[0]
volumes = load_mapping(config_details.config_files, 'get_volumes', 'Volume')
networks = load_mapping(config_details.config_files, 'get_networks', 'Network')
volumes = load_mapping(
config_details.config_files, 'get_volumes', 'Volume'
)
networks = load_mapping(
config_details.config_files, 'get_networks', 'Network'
)
service_dicts = load_services(
config_details.working_dir,
main_file.filename,
[file.get_service_dicts() for file in config_details.config_files],
main_file.version)
main_file,
[file.get_service_dicts() for file in config_details.config_files])
if main_file.version != V1:
for service_dict in service_dicts:
match_named_volumes(service_dict, volumes)
return Config(main_file.version, service_dicts, volumes, networks)
@ -300,24 +337,30 @@ def load_mapping(config_files, get_func, entity_type):
mapping[name] = config
if 'driver_opts' in config:
config['driver_opts'] = build_string_dict(
config['driver_opts']
)
return mapping
def load_services(working_dir, filename, service_configs, version):
def load_services(working_dir, config_file, service_configs):
def build_service(service_name, service_dict, service_names):
service_config = ServiceConfig.with_abs_paths(
working_dir,
filename,
config_file.filename,
service_name,
service_dict)
resolver = ServiceExtendsResolver(service_config, version)
resolver = ServiceExtendsResolver(service_config, config_file)
service_dict = process_service(resolver.run())
validate_service(service_dict, service_config.name, version)
service_config = service_config._replace(config=service_dict)
validate_service(service_config, service_names, config_file.version)
service_dict = finalize_service(
service_config._replace(config=service_dict),
service_config,
service_names,
version)
config_file.version)
return service_dict
def build_services(service_config):
@ -333,7 +376,7 @@ def load_services(working_dir, filename, service_configs, version):
name: merge_service_dicts_from_files(
base.get(name, {}),
override.get(name, {}),
version)
config_file.version)
for name in all_service_names
}
@ -344,27 +387,36 @@ def load_services(working_dir, filename, service_configs, version):
return build_services(service_config)
def interpolate_config_section(filename, config, section):
validate_config_section(filename, config, section)
return interpolate_environment_variables(config, section)
def process_config_file(config_file, service_name=None):
service_dicts = config_file.get_service_dicts()
validate_top_level_service_objects(config_file.filename, service_dicts)
services = interpolate_config_section(
config_file.filename,
config_file.get_service_dicts(),
'service')
interpolated_config = interpolate_environment_variables(service_dicts, 'service')
if config_file.version == 2:
if config_file.version == V2_0:
processed_config = dict(config_file.config)
processed_config['services'] = interpolated_config
processed_config['volumes'] = interpolate_environment_variables(
config_file.get_volumes(), 'volume')
processed_config['networks'] = interpolate_environment_variables(
config_file.get_networks(), 'network')
processed_config['services'] = services
processed_config['volumes'] = interpolate_config_section(
config_file.filename,
config_file.get_volumes(),
'volume')
processed_config['networks'] = interpolate_config_section(
config_file.filename,
config_file.get_networks(),
'network')
if config_file.version == 1:
processed_config = interpolated_config
if config_file.version == V1:
processed_config = services
config_file = config_file._replace(config=processed_config)
validate_against_fields_schema(config_file)
if service_name and service_name not in processed_config:
if service_name and service_name not in services:
raise ConfigurationError(
"Cannot extend service '{}' in {}: Service not found".format(
service_name, config_file.filename))
@ -373,11 +425,11 @@ def process_config_file(config_file, service_name=None):
class ServiceExtendsResolver(object):
def __init__(self, service_config, version, already_seen=None):
def __init__(self, service_config, config_file, already_seen=None):
self.service_config = service_config
self.working_dir = service_config.working_dir
self.already_seen = already_seen or []
self.version = version
self.config_file = config_file
@property
def signature(self):
@ -404,10 +456,13 @@ class ServiceExtendsResolver(object):
config_path = self.get_extended_config_path(extends)
service_name = extends['service']
extends_file = ConfigFile.from_filename(config_path)
validate_config_version([self.config_file, extends_file])
extended_file = process_config_file(
ConfigFile.from_filename(config_path),
extends_file,
service_name=service_name)
service_config = extended_file.config[service_name]
service_config = extended_file.get_service(service_name)
return config_path, service_config, service_name
def resolve_extends(self, extended_config_path, service_dict, service_name):
@ -417,7 +472,7 @@ class ServiceExtendsResolver(object):
extended_config_path,
service_name,
service_dict),
self.version,
self.config_file,
already_seen=self.already_seen + [self.signature])
service_config = resolver.run()
@ -425,13 +480,12 @@ class ServiceExtendsResolver(object):
validate_extended_service_dict(
other_service_dict,
extended_config_path,
service_name,
)
service_name)
return merge_service_dicts(
other_service_dict,
self.service_config.config,
self.version)
self.config_file.version)
def get_extended_config_path(self, extends_options):
"""Service we are extending either has a value for 'file' set, which we
@ -477,26 +531,28 @@ def validate_extended_service_dict(service_dict, filename, service):
"%s services with 'volumes_from' cannot be extended" % error_prefix)
if 'net' in service_dict:
if get_service_name_from_net(service_dict['net']) is not None:
if get_container_name_from_network_mode(service_dict['net']):
raise ConfigurationError(
"%s services with 'net: container' cannot be extended" % error_prefix)
if 'network_mode' in service_dict:
if get_service_name_from_network_mode(service_dict['network_mode']):
raise ConfigurationError(
"%s services with 'network_mode: service' cannot be extended" % error_prefix)
def validate_ulimits(ulimit_config):
for limit_name, soft_hard_values in six.iteritems(ulimit_config):
if isinstance(soft_hard_values, dict):
if not soft_hard_values['soft'] <= soft_hard_values['hard']:
raise ConfigurationError(
"ulimit_config \"{}\" cannot contain a 'soft' value higher "
"than 'hard' value".format(ulimit_config))
if 'depends_on' in service_dict:
raise ConfigurationError(
"%s services with 'depends_on' cannot be extended" % error_prefix)
def validate_service(service_dict, service_name, version):
def validate_service(service_config, service_names, version):
service_dict, service_name = service_config.config, service_config.name
validate_against_service_schema(service_dict, service_name, version)
validate_paths(service_dict)
if 'ulimits' in service_dict:
validate_ulimits(service_dict['ulimits'])
validate_ulimits(service_config)
validate_network_mode(service_config, service_names)
validate_depends_on(service_config, service_names)
if not service_dict.get('image') and has_uppercase(service_name):
raise ConfigurationError(
@ -556,6 +612,17 @@ def finalize_service(service_config, service_names, version):
service_dict['volumes'] = [
VolumeSpec.parse(v) for v in service_dict['volumes']]
if 'net' in service_dict:
network_mode = service_dict.pop('net')
container_name = get_container_name_from_network_mode(network_mode)
if container_name and container_name in service_names:
service_dict['network_mode'] = 'service:{}'.format(container_name)
else:
service_dict['network_mode'] = network_mode
if 'networks' in service_dict:
service_dict['networks'] = parse_networks(service_dict['networks'])
if 'restart' in service_dict:
service_dict['restart'] = parse_restart_spec(service_dict['restart'])
@ -598,63 +665,94 @@ def merge_service_dicts_from_files(base, override, version):
return new_service
class MergeDict(dict):
"""A dict-like object responsible for merging two dicts into one."""
def __init__(self, base, override):
self.base = base
self.override = override
def needs_merge(self, field):
return field in self.base or field in self.override
def merge_field(self, field, merge_func, default=None):
if not self.needs_merge(field):
return
self[field] = merge_func(
self.base.get(field, default),
self.override.get(field, default))
def merge_mapping(self, field, parse_func):
if not self.needs_merge(field):
return
self[field] = parse_func(self.base.get(field))
self[field].update(parse_func(self.override.get(field)))
def merge_sequence(self, field, parse_func):
def parse_sequence_func(seq):
return to_mapping((parse_func(item) for item in seq), 'merge_field')
if not self.needs_merge(field):
return
merged = parse_sequence_func(self.base.get(field, []))
merged.update(parse_sequence_func(self.override.get(field, [])))
self[field] = [item.repr() for item in merged.values()]
def merge_scalar(self, field):
if self.needs_merge(field):
self[field] = self.override.get(field, self.base.get(field))
def merge_service_dicts(base, override, version):
d = {}
md = MergeDict(base, override)
def merge_field(field, merge_func, default=None):
if field in base or field in override:
d[field] = merge_func(
base.get(field, default),
override.get(field, default))
def merge_mapping(mapping, parse_func):
if mapping in base or mapping in override:
merged = parse_func(base.get(mapping, None))
merged.update(parse_func(override.get(mapping, None)))
d[mapping] = merged
merge_mapping('environment', parse_environment)
merge_mapping('labels', parse_labels)
merge_mapping('ulimits', parse_ulimits)
md.merge_mapping('environment', parse_environment)
md.merge_mapping('labels', parse_labels)
md.merge_mapping('ulimits', parse_ulimits)
md.merge_mapping('networks', parse_networks)
md.merge_sequence('links', ServiceLink.parse)
for field in ['volumes', 'devices']:
merge_field(field, merge_path_mappings)
md.merge_field(field, merge_path_mappings)
for field in ['ports', 'expose', 'external_links']:
merge_field(field, operator.add, default=[])
for field in [
'depends_on',
'expose',
'external_links',
'ports',
'volumes_from',
]:
md.merge_field(field, operator.add, default=[])
for field in ['dns', 'dns_search', 'env_file']:
merge_field(field, merge_list_or_string)
md.merge_field(field, merge_list_or_string)
for field in set(ALLOWED_KEYS) - set(d):
if field in base or field in override:
d[field] = override.get(field, base.get(field))
for field in set(ALLOWED_KEYS) - set(md):
md.merge_scalar(field)
if version == 1:
legacy_v1_merge_image_or_build(d, base, override)
else:
merge_build(d, base, override)
if version == V1:
legacy_v1_merge_image_or_build(md, base, override)
elif md.needs_merge('build'):
md['build'] = merge_build(md, base, override)
return d
return dict(md)
def merge_build(output, base, override):
build = {}
def to_dict(service):
build_config = service.get('build', {})
if isinstance(build_config, six.string_types):
return {'context': build_config}
return build_config
if 'build' in base:
if isinstance(base['build'], six.string_types):
build['context'] = base['build']
else:
build.update(base['build'])
if 'build' in override:
if isinstance(override['build'], six.string_types):
build['context'] = override['build']
else:
build.update(override['build'])
if build:
output['build'] = build
md = MergeDict(to_dict(base), to_dict(override))
md.merge_scalar('context')
md.merge_scalar('dockerfile')
md.merge_mapping('args', parse_build_arguments)
return dict(md)
def legacy_v1_merge_image_or_build(output, base, override):
@ -711,6 +809,7 @@ def parse_dict_or_list(split_func, type_name, arguments):
parse_build_arguments = functools.partial(parse_dict_or_list, split_env, 'build arguments')
parse_environment = functools.partial(parse_dict_or_list, split_env, 'environment')
parse_labels = functools.partial(parse_dict_or_list, split_label, 'labels')
parse_networks = functools.partial(parse_dict_or_list, lambda k: (k, None), 'networks')
def parse_ulimits(ulimits):
@ -727,7 +826,7 @@ def resolve_env_var(key, val):
elif key in os.environ:
return key, os.environ[key]
else:
return key, ''
return key, None
def env_vars_from_file(filename):
@ -774,7 +873,7 @@ def normalize_build(service_dict, working_dir):
else:
build.update(service_dict['build'])
if 'args' in build:
build['args'] = resolve_build_args(build)
build['args'] = build_string_dict(resolve_build_args(build))
service_dict['build'] = build
@ -797,6 +896,9 @@ def validate_paths(service_dict):
build_path = build
elif isinstance(build, dict) and 'context' in build:
build_path = build['context']
else:
# We have a build section but no context, so nothing to validate
return
if (
not is_url(build_path) and
@ -869,6 +971,10 @@ def to_list(value):
return value
def to_mapping(sequence, key_field):
return {getattr(item, key_field): item for item in sequence}
def has_uppercase(name):
return any(char in string.ascii_uppercase for char in name)

View file

@ -2,6 +2,14 @@ from __future__ import absolute_import
from __future__ import unicode_literals
VERSION_EXPLANATION = (
'Either specify a version of "2" (or "2.0") and place your service '
'definitions under the `services` key, or omit the `version` key and place '
'your service definitions at the root of the file to use version 1.\n'
'For more on the Compose file format versions, see '
'https://docs.docker.com/compose/compose-file/')
class ConfigurationError(Exception):
def __init__(self, msg):
self.msg = msg

View file

@ -1,18 +1,18 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"id": "fields_schema_v2.json",
"id": "fields_schema_v2.0.json",
"properties": {
"version": {
"enum": [2]
"type": "string"
},
"services": {
"id": "#/properties/services",
"type": "object",
"patternProperties": {
"^[a-zA-Z0-9._-]+$": {
"$ref": "service_schema_v2.json#/definitions/service"
"$ref": "service_schema_v2.0.json#/definitions/service"
}
},
"additionalProperties": false
@ -41,7 +41,34 @@
"definitions": {
"network": {
"id": "#/definitions/network",
"type": "object"
"type": "object",
"properties": {
"driver": {"type": "string"},
"driver_opts": {
"type": "object",
"patternProperties": {
"^.+$": {"type": ["string", "number"]}
}
},
"ipam": {
"type": "object",
"properties": {
"driver": {"type": "string"},
"config": {
"type": "array"
}
},
"additionalProperties": false
},
"external": {
"type": ["boolean", "object"],
"properties": {
"name": {"type": "string"}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
"volume": {
"id": "#/definitions/volume",

View file

@ -21,7 +21,7 @@ def interpolate_environment_variables(config, section):
)
return dict(
(name, process_item(name, config_dict))
(name, process_item(name, config_dict or {}))
for name, config_dict in config.items()
)

View file

@ -1,6 +1,6 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "service_schema_v2.json",
"id": "service_schema_v2.0.json",
"type": "object",
@ -23,7 +23,20 @@
"properties": {
"context": {"type": "string"},
"dockerfile": {"type": "string"},
"args": {"$ref": "#/definitions/list_or_dict"}
"args": {
"oneOf": [
{"$ref": "#/definitions/list_of_strings"},
{
"type": "object",
"patternProperties": {
"^.+$": {
"type": ["string", "number"]
}
},
"additionalProperties": false
}
]
}
},
"additionalProperties": false
}
@ -42,6 +55,7 @@
"cpu_shares": {"type": ["number", "string"]},
"cpu_quota": {"type": ["number", "string"]},
"cpuset": {"type": "string"},
"depends_on": {"$ref": "#/definitions/list_of_strings"},
"devices": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
"dns": {"$ref": "#/definitions/string_or_list"},
"dns_search": {"$ref": "#/definitions/string_or_list"},
@ -82,11 +96,13 @@
]
},
"external_links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
"extra_hosts": {"$ref": "#/definitions/list_or_dict"},
"hostname": {"type": "string"},
"image": {"type": "string"},
"ipc": {"type": "string"},
"labels": {"$ref": "#/definitions/list_or_dict"},
"links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
"logging": {
"type": "object",
@ -101,13 +117,31 @@
"mac_address": {"type": "string"},
"mem_limit": {"type": ["number", "string"]},
"memswap_limit": {"type": ["number", "string"]},
"network_mode": {"type": "string"},
"networks": {
"type": "array",
"items": {"type": "string"},
"uniqueItems": true
"oneOf": [
{"$ref": "#/definitions/list_of_strings"},
{
"type": "object",
"patternProperties": {
"^[a-zA-Z0-9._-]+$": {
"oneOf": [
{
"type": "object",
"properties": {
"aliases": {"$ref": "#/definitions/list_of_strings"}
},
"additionalProperties": false
},
{"type": "null"}
]
}
},
"additionalProperties": false
}
]
},
"pid": {"type": ["string", "null"]},
"ports": {
@ -191,7 +225,12 @@
"anyOf": [
{"required": ["build"]},
{"required": ["image"]}
]
],
"properties": {
"build": {
"required": ["context"]
}
}
}
}
}

View file

@ -4,14 +4,22 @@ from __future__ import unicode_literals
from compose.config.errors import DependencyError
def get_service_name_from_net(net_config):
if not net_config:
def get_service_name_from_network_mode(network_mode):
return get_source_name_from_network_mode(network_mode, 'service')
def get_container_name_from_network_mode(network_mode):
return get_source_name_from_network_mode(network_mode, 'container')
def get_source_name_from_network_mode(network_mode, source_type):
if not network_mode:
return
if not net_config.startswith('container:'):
if not network_mode.startswith(source_type+':'):
return
_, net_name = net_config.split(':', 1)
_, net_name = network_mode.split(':', 1)
return net_name
@ -33,7 +41,8 @@ def sort_service_dicts(services):
service for service in services
if (name in get_service_names(service.get('links', [])) or
name in get_service_names_from_volumes_from(service.get('volumes_from', [])) or
name == get_service_name_from_net(service.get('net')))
name == get_service_name_from_network_mode(service.get('network_mode')) or
name in service.get('depends_on', []))
]
def visit(n):
@ -42,8 +51,10 @@ def sort_service_dicts(services):
raise DependencyError('A service can not link to itself: %s' % n['name'])
if n['name'] in n.get('volumes_from', []):
raise DependencyError('A service can not mount itself as volume: %s' % n['name'])
else:
raise DependencyError('Circular import between %s' % ' and '.join(temporary_marked))
if n['name'] in n.get('depends_on', []):
raise DependencyError('A service can not depend on itself: %s' % n['name'])
raise DependencyError('Circular dependency between %s' % ' and '.join(temporary_marked))
if n in unmarked:
temporary_marked.add(n['name'])
for m in get_service_dependents(n, services):

View file

@ -7,6 +7,7 @@ from __future__ import unicode_literals
import os
from collections import namedtuple
from compose.config.config import V1
from compose.config.errors import ConfigurationError
from compose.const import IS_WINDOWS_PLATFORM
@ -16,7 +17,7 @@ class VolumeFromSpec(namedtuple('_VolumeFromSpec', 'source mode type')):
# TODO: drop service_names arg when v1 is removed
@classmethod
def parse(cls, volume_from_config, service_names, version):
func = cls.parse_v1 if version == 1 else cls.parse_v2
func = cls.parse_v1 if version == V1 else cls.parse_v2
return func(service_names, volume_from_config)
@classmethod
@ -163,3 +164,26 @@ class VolumeSpec(namedtuple('_VolumeSpec', 'external internal mode')):
def repr(self):
external = self.external + ':' if self.external else ''
return '{ext}{v.internal}:{v.mode}'.format(ext=external, v=self)
@property
def is_named_volume(self):
return self.external and not self.external.startswith(('.', '/', '~'))
class ServiceLink(namedtuple('_ServiceLink', 'target alias')):
@classmethod
def parse(cls, link_spec):
target, _, alias = link_spec.partition(':')
if not alias:
alias = target
return cls(target, alias)
def repr(self):
if self.target == self.alias:
return self.target
return '{s.target}:{s.alias}'.format(s=self)
@property
def merge_field(self):
return self.alias

View file

@ -15,6 +15,8 @@ from jsonschema import RefResolver
from jsonschema import ValidationError
from .errors import ConfigurationError
from .errors import VERSION_EXPLANATION
from .sort_services import get_service_name_from_network_mode
log = logging.getLogger(__name__)
@ -62,46 +64,78 @@ def format_expose(instance):
@FormatChecker.cls_checks(format="bool-value-in-mapping")
def format_boolean_in_environment(instance):
"""
Check if there is a boolean in the environment and display a warning.
"""Check if there is a boolean in the mapping sections and display a warning.
Always return True here so the validation won't raise an error.
"""
if isinstance(instance, bool):
log.warn(
"There is a boolean value in the 'environment' key.\n"
"Environment variables can only be strings.\n"
"Please add quotes to any boolean values to make them string "
"(eg, 'True', 'yes', 'N').\n"
"There is a boolean value in the 'environment', 'labels', or "
"'extra_hosts' field of a service.\n"
"These sections only support string values.\n"
"Please add quotes to any boolean values to make them strings "
"(eg, 'True', 'false', 'yes', 'N', 'on', 'Off').\n"
"This warning will become an error in a future release. \r\n"
)
return True
def validate_top_level_service_objects(filename, service_dicts):
"""Perform some high level validation of the service name and value.
This validation must happen before interpolation, which must happen
before the rest of validation, which is why it's separate from the
rest of the service validation.
"""
for service_name, service_dict in service_dicts.items():
if not isinstance(service_name, six.string_types):
def match_named_volumes(service_dict, project_volumes):
service_volumes = service_dict.get('volumes', [])
for volume_spec in service_volumes:
if volume_spec.is_named_volume and volume_spec.external not in project_volumes:
raise ConfigurationError(
"In file '{}' service name: {} needs to be a string, eg '{}'".format(
filename,
service_name,
service_name))
if not isinstance(service_dict, dict):
raise ConfigurationError(
"In file '{}' service '{}' doesn\'t have any configuration options. "
"All top level keys in your docker-compose.yml must map "
"to a dictionary of configuration options.".format(
filename, service_name
'Named volume "{0}" is used in service "{1}" but no'
' declaration was found in the volumes section.'.format(
volume_spec.repr(), service_dict.get('name')
)
)
def python_type_to_yaml_type(type_):
type_name = type(type_).__name__
return {
'dict': 'mapping',
'list': 'array',
'int': 'number',
'float': 'number',
'bool': 'boolean',
'unicode': 'string',
'str': 'string',
'bytes': 'string',
}.get(type_name, type_name)
def validate_config_section(filename, config, section):
"""Validate the structure of a configuration section. This must be done
before interpolation so it's separate from schema validation.
"""
if not isinstance(config, dict):
raise ConfigurationError(
"In file '{filename}', {section} must be a mapping, not "
"{type}.".format(
filename=filename,
section=section,
type=anglicize_json_type(python_type_to_yaml_type(config))))
for key, value in config.items():
if not isinstance(key, six.string_types):
raise ConfigurationError(
"In file '{filename}', the {section} name {name} must be a "
"quoted string, i.e. '{name}'.".format(
filename=filename,
section=section,
name=key))
if not isinstance(value, (dict, type(None))):
raise ConfigurationError(
"In file '{filename}', {section} '{name}' must be a mapping not "
"{type}.".format(
filename=filename,
section=section,
name=key,
type=anglicize_json_type(python_type_to_yaml_type(value))))
def validate_top_level_object(config_file):
if not isinstance(config_file.config, dict):
raise ConfigurationError(
@ -110,6 +144,18 @@ def validate_top_level_object(config_file):
type(config_file.config)))
def validate_ulimits(service_config):
ulimit_config = service_config.config.get('ulimits', {})
for limit_name, soft_hard_values in six.iteritems(ulimit_config):
if isinstance(soft_hard_values, dict):
if not soft_hard_values['soft'] <= soft_hard_values['hard']:
raise ConfigurationError(
"Service '{s.name}' has invalid ulimit '{ulimit}'. "
"'soft' value can not be greater than 'hard' value ".format(
s=service_config,
ulimit=ulimit_config))
def validate_extends_file_path(service_name, extends_options, filename):
"""
The service to be extended must either be defined in the config key 'file',
@ -123,24 +169,50 @@ def validate_extends_file_path(service_name, extends_options, filename):
)
def get_unsupported_config_msg(service_name, error_key):
msg = "Unsupported config option for '{}' service: '{}'".format(service_name, error_key)
def validate_network_mode(service_config, service_names):
network_mode = service_config.config.get('network_mode')
if not network_mode:
return
if 'networks' in service_config.config:
raise ConfigurationError("'network_mode' and 'networks' cannot be combined")
dependency = get_service_name_from_network_mode(network_mode)
if not dependency:
return
if dependency not in service_names:
raise ConfigurationError(
"Service '{s.name}' uses the network stack of service '{dep}' which "
"is undefined.".format(s=service_config, dep=dependency))
def validate_depends_on(service_config, service_names):
for dependency in service_config.config.get('depends_on', []):
if dependency not in service_names:
raise ConfigurationError(
"Service '{s.name}' depends on service '{dep}' which is "
"undefined.".format(s=service_config, dep=dependency))
def get_unsupported_config_msg(path, error_key):
msg = "Unsupported config option for {}: '{}'".format(path_string(path), error_key)
if error_key in DOCKER_CONFIG_HINTS:
msg += " (did you mean '{}'?)".format(DOCKER_CONFIG_HINTS[error_key])
return msg
def anglicize_validator(validator):
if validator in ["array", "object"]:
return 'an ' + validator
return 'a ' + validator
def anglicize_json_type(json_type):
if json_type.startswith(('a', 'e', 'i', 'o', 'u')):
return 'an ' + json_type
return 'a ' + json_type
def is_service_dict_schema(schema_id):
return schema_id == 'fields_schema_v1.json' or schema_id == '#/properties/services'
def handle_error_for_schema_with_id(error, service_name):
def handle_error_for_schema_with_id(error, path):
schema_id = error.schema['id']
if is_service_dict_schema(schema_id) and error.validator == 'additionalProperties':
@ -164,62 +236,66 @@ def handle_error_for_schema_with_id(error, service_name):
# TODO: only applies to v1
if 'image' in error.instance and context:
return (
"Service '{}' has both an image and build path specified. "
"{} has both an image and build path specified. "
"A service can either be built to image or use an existing "
"image, not both.".format(service_name))
"image, not both.".format(path_string(path)))
if 'image' not in error.instance and not context:
return (
"Service '{}' has neither an image nor a build path "
"specified. At least one must be provided.".format(service_name))
"{} has neither an image nor a build path specified. "
"At least one must be provided.".format(path_string(path)))
# TODO: only applies to v1
if 'image' in error.instance and dockerfile:
return (
"Service '{}' has both an image and alternate Dockerfile. "
"{} has both an image and alternate Dockerfile. "
"A service can either be built to image or use an existing "
"image, not both.".format(service_name))
"image, not both.".format(path_string(path)))
if schema_id == '#/definitions/service':
if error.validator == 'additionalProperties':
if error.validator == 'additionalProperties':
if schema_id == '#/definitions/service':
invalid_config_key = parse_key_from_error_msg(error)
return get_unsupported_config_msg(service_name, invalid_config_key)
return get_unsupported_config_msg(path, invalid_config_key)
if not error.path:
return '{}\n{}'.format(error.message, VERSION_EXPLANATION)
def handle_generic_service_error(error, service_name):
config_key = " ".join("'%s'" % k for k in error.path)
def handle_generic_service_error(error, path):
msg_format = None
error_msg = error.message
if error.validator == 'oneOf':
msg_format = "Service '{}' configuration key {} {}"
error_msg = _parse_oneof_validator(error)
msg_format = "{path} {msg}"
config_key, error_msg = _parse_oneof_validator(error)
if config_key:
path.append(config_key)
elif error.validator == 'type':
msg_format = ("Service '{}' configuration key {} contains an invalid "
"type, it should be {}")
msg_format = "{path} contains an invalid type, it should be {msg}"
error_msg = _parse_valid_types_from_validator(error.validator_value)
# TODO: no test case for this branch, there are no config options
# which exercise this branch
elif error.validator == 'required':
msg_format = "Service '{}' configuration key '{}' is invalid, {}"
error_msg = ", ".join(error.validator_value)
msg_format = "{path} is invalid, {msg} is required."
elif error.validator == 'dependencies':
msg_format = "Service '{}' configuration key '{}' is invalid: {}"
config_key = list(error.validator_value.keys())[0]
required_keys = ",".join(error.validator_value[config_key])
msg_format = "{path} is invalid: {msg}"
path.append(config_key)
error_msg = "when defining '{}' you must set '{}' as well".format(
config_key,
required_keys)
elif error.cause:
error_msg = six.text_type(error.cause)
msg_format = "Service '{}' configuration key {} is invalid: {}"
msg_format = "{path} is invalid: {msg}"
elif error.path:
msg_format = "Service '{}' configuration key {} value {}"
msg_format = "{path} value {msg}"
if msg_format:
return msg_format.format(service_name, config_key, error_msg)
return msg_format.format(path=path_string(path), msg=error_msg)
return error.message
@ -228,19 +304,23 @@ def parse_key_from_error_msg(error):
return error.message.split("'")[1]
def path_string(path):
return ".".join(c for c in path if isinstance(c, six.string_types))
def _parse_valid_types_from_validator(validator):
"""A validator value can be either an array of valid types or a string of
a valid type. Parse the valid types and prefix with the correct article.
"""
if not isinstance(validator, list):
return anglicize_validator(validator)
return anglicize_json_type(validator)
if len(validator) == 1:
return anglicize_validator(validator[0])
return anglicize_json_type(validator[0])
return "{}, or {}".format(
", ".join([anglicize_validator(validator[0])] + validator[1:-1]),
anglicize_validator(validator[-1]))
", ".join([anglicize_json_type(validator[0])] + validator[1:-1]),
anglicize_json_type(validator[-1]))
def _parse_oneof_validator(error):
@ -252,53 +332,57 @@ def _parse_oneof_validator(error):
types = []
for context in error.context:
if context.validator == 'oneOf':
_, error_msg = _parse_oneof_validator(context)
return path_string(context.path), error_msg
if context.validator == 'required':
return context.message
return (None, context.message)
if context.validator == 'additionalProperties':
invalid_config_key = parse_key_from_error_msg(context)
return "contains unsupported option: '{}'".format(invalid_config_key)
return (None, "contains unsupported option: '{}'".format(invalid_config_key))
if context.path:
invalid_config_key = " ".join(
"'{}' ".format(fragment) for fragment in context.path
if isinstance(fragment, six.string_types)
return (
path_string(context.path),
"contains {}, which is an invalid type, it should be {}".format(
json.dumps(context.instance),
_parse_valid_types_from_validator(context.validator_value)),
)
return "{}contains {}, which is an invalid type, it should be {}".format(
invalid_config_key,
# Always print the json repr of the invalid value
json.dumps(context.instance),
_parse_valid_types_from_validator(context.validator_value))
if context.validator == 'uniqueItems':
return "contains non unique items, please remove duplicates from {}".format(
context.instance)
return (
None,
"contains non unique items, please remove duplicates from {}".format(
context.instance),
)
if context.validator == 'type':
types.append(context.validator_value)
valid_types = _parse_valid_types_from_validator(types)
return "contains an invalid type, it should be {}".format(valid_types)
return (None, "contains an invalid type, it should be {}".format(valid_types))
def process_errors(errors, service_name=None):
def process_errors(errors, path_prefix=None):
"""jsonschema gives us an error tree full of information to explain what has
gone wrong. Process each error and pull out relevant information and re-write
helpful error messages that are relevant.
"""
def format_error_message(error, service_name):
if not service_name and error.path:
# field_schema errors will have service name on the path
service_name = error.path.popleft()
path_prefix = path_prefix or []
def format_error_message(error):
path = path_prefix + list(error.path)
if 'id' in error.schema:
error_msg = handle_error_for_schema_with_id(error, service_name)
error_msg = handle_error_for_schema_with_id(error, path)
if error_msg:
return error_msg
return handle_generic_service_error(error, service_name)
return handle_generic_service_error(error, path)
return '\n'.join(format_error_message(error, service_name) for error in errors)
return '\n'.join(format_error_message(error) for error in errors)
def validate_against_fields_schema(config_file):
@ -315,14 +399,14 @@ def validate_against_service_schema(config, service_name, version):
config,
"service_schema_v{0}.json".format(version),
format_checker=["ports"],
service_name=service_name)
path_prefix=[service_name])
def _validate_against_schema(
config,
schema_filename,
format_checker=(),
service_name=None,
path_prefix=None,
filename=None):
config_source_dir = os.path.dirname(os.path.abspath(__file__))
@ -348,7 +432,7 @@ def _validate_against_schema(
if not errors:
return
error_msg = process_errors(errors, service_name)
error_msg = process_errors(errors, path_prefix=path_prefix)
file_msg = " in file '{}'".format(filename) if filename else ''
raise ConfigurationError("Validation failed{}, reason(s):\n{}".format(
file_msg,

View file

@ -14,9 +14,16 @@ 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'
COMPOSEFILE_VERSIONS = (1, 2)
COMPOSEFILE_V1 = '1'
COMPOSEFILE_V2_0 = '2.0'
API_VERSIONS = {
1: '1.21',
2: '1.22',
COMPOSEFILE_V1: '1.21',
COMPOSEFILE_V2_0: '1.22',
}
API_VERSION_TO_ENGINE_VERSION = {
API_VERSIONS[COMPOSEFILE_V1]: '1.9.0',
API_VERSIONS[COMPOSEFILE_V2_0]: '1.10.0'
}

View file

@ -60,7 +60,7 @@ class Container(object):
@property
def short_id(self):
return self.id[:10]
return self.id[:12]
@property
def name(self):
@ -134,7 +134,11 @@ class Container(object):
@property
def environment(self):
return dict(var.split("=", 1) for var in self.get('Config.Env') or [])
def parse_env(var):
if '=' in var:
return var.split("=", 1)
return var, None
return dict(parse_env(var) for var in self.get('Config.Env') or [])
@property
def exit_code(self):

View file

@ -4,6 +4,8 @@ from __future__ import unicode_literals
import logging
from docker.errors import NotFound
from docker.utils import create_ipam_config
from docker.utils import create_ipam_pool
from .config import ConfigurationError
@ -13,12 +15,13 @@ log = logging.getLogger(__name__)
class Network(object):
def __init__(self, client, project, name, driver=None, driver_opts=None,
external_name=None):
ipam=None, external_name=None):
self.client = client
self.project = project
self.name = name
self.driver = driver
self.driver_opts = driver_opts
self.ipam = create_ipam_config_from_dict(ipam)
self.external_name = external_name
def ensure(self):
@ -44,11 +47,11 @@ class Network(object):
data = self.inspect()
if self.driver and data['Driver'] != self.driver:
raise ConfigurationError(
'Network {} needs to be recreated - driver has changed'
'Network "{}" needs to be recreated - driver has changed'
.format(self.full_name))
if data['Options'] != (self.driver_opts or {}):
raise ConfigurationError(
'Network {} needs to be recreated - options have changed'
'Network "{}" needs to be recreated - options have changed'
.format(self.full_name))
except NotFound:
driver_name = 'the default driver'
@ -61,7 +64,10 @@ class Network(object):
)
self.client.create_network(
self.full_name, self.driver, self.driver_opts
name=self.full_name,
driver=self.driver,
options=self.driver_opts,
ipam=self.ipam,
)
def remove(self):
@ -80,3 +86,102 @@ class Network(object):
if self.external_name:
return self.external_name
return '{0}_{1}'.format(self.project, self.name)
def create_ipam_config_from_dict(ipam_dict):
if not ipam_dict:
return None
return create_ipam_config(
driver=ipam_dict.get('driver'),
pool_configs=[
create_ipam_pool(
subnet=config.get('subnet'),
iprange=config.get('ip_range'),
gateway=config.get('gateway'),
aux_addresses=config.get('aux_addresses'),
)
for config in ipam_dict.get('config', [])
],
)
def build_networks(name, config_data, client):
network_config = config_data.networks or {}
networks = {
network_name: Network(
client=client, project=name, name=network_name,
driver=data.get('driver'),
driver_opts=data.get('driver_opts'),
ipam=data.get('ipam'),
external_name=data.get('external_name'),
)
for network_name, data in network_config.items()
}
if 'default' not in networks:
networks['default'] = Network(client, name, 'default')
return networks
class ProjectNetworks(object):
def __init__(self, networks, use_networking):
self.networks = networks or {}
self.use_networking = use_networking
@classmethod
def from_services(cls, services, networks, use_networking):
service_networks = {
network: networks.get(network)
for service in services
for network in get_network_names_for_service(service)
}
unused = set(networks) - set(service_networks) - {'default'}
if unused:
log.warn(
"Some networks were defined but are not used by any service: "
"{}".format(", ".join(unused)))
return cls(service_networks, use_networking)
def remove(self):
if not self.use_networking:
return
for network in self.networks.values():
network.remove()
def initialize(self):
if not self.use_networking:
return
for network in self.networks.values():
network.ensure()
def get_network_aliases_for_service(service_dict):
if 'network_mode' in service_dict:
return {}
networks = service_dict.get('networks', {'default': None})
return dict(
(net, (config or {}).get('aliases', []))
for net, config in networks.items()
)
def get_network_names_for_service(service_dict):
return get_network_aliases_for_service(service_dict).keys()
def get_networks(service_dict, network_definitions):
networks = {}
for name, aliases in get_network_aliases_for_service(service_dict).items():
network = network_definitions.get(name)
if network:
networks[network.full_name] = aliases
else:
raise ConfigurationError(
'Service "{}" uses an undefined network "{}"'
.format(service_dict['name'], name))
return networks

View file

@ -6,25 +6,28 @@ import logging
from functools import reduce
from docker.errors import APIError
from docker.errors import NotFound
from . import parallel
from .config import ConfigurationError
from .config.sort_services import get_service_name_from_net
from .config.config import V1
from .config.sort_services import get_container_name_from_network_mode
from .config.sort_services import get_service_name_from_network_mode
from .const import DEFAULT_TIMEOUT
from .const import IMAGE_EVENTS
from .const import LABEL_ONE_OFF
from .const import LABEL_PROJECT
from .const import LABEL_SERVICE
from .container import Container
from .network import Network
from .service import ContainerNet
from .network import build_networks
from .network import get_networks
from .network import ProjectNetworks
from .service import ContainerNetworkMode
from .service import ConvergenceStrategy
from .service import Net
from .service import NetworkMode
from .service import Service
from .service import ServiceNet
from .service import ServiceNetworkMode
from .utils import microseconds_from_time_nano
from .volume import Volume
from .volume import ProjectVolumes
log = logging.getLogger(__name__)
@ -34,15 +37,12 @@ class Project(object):
"""
A collection of services.
"""
def __init__(self, name, services, client, networks=None, volumes=None,
use_networking=False, network_driver=None):
def __init__(self, name, services, client, networks=None, volumes=None):
self.name = name
self.services = services
self.client = client
self.use_networking = use_networking
self.network_driver = network_driver
self.networks = networks or []
self.volumes = volumes or []
self.volumes = volumes or ProjectVolumes({})
self.networks = networks or ProjectNetworks({}, False)
def labels(self, one_off=False):
return [
@ -55,60 +55,47 @@ class Project(object):
"""
Construct a Project from a config.Config object.
"""
use_networking = (config_data.version and config_data.version >= 2)
project = cls(name, [], client, use_networking=use_networking)
custom_networks = []
if config_data.networks:
for network_name, data in config_data.networks.items():
custom_networks.append(
Network(
client=client, project=name, name=network_name,
driver=data.get('driver'),
driver_opts=data.get('driver_opts'),
external_name=data.get('external_name'),
)
)
use_networking = (config_data.version and config_data.version != V1)
networks = build_networks(name, config_data, client)
project_networks = ProjectNetworks.from_services(
config_data.services,
networks,
use_networking)
volumes = ProjectVolumes.from_config(name, config_data, client)
project = cls(name, [], client, project_networks, volumes)
for service_dict in config_data.services:
service_dict = dict(service_dict)
if use_networking:
networks = get_networks(
service_dict,
custom_networks + [project.default_network])
net = Net(networks[0]) if networks else Net("none")
links = []
service_networks = get_networks(service_dict, networks)
else:
networks = []
net = project.get_net(service_dict)
links = project.get_links(service_dict)
service_networks = {}
service_dict.pop('networks', None)
links = project.get_links(service_dict)
network_mode = project.get_network_mode(
service_dict, list(service_networks.keys())
)
volumes_from = get_volumes_from(project, service_dict)
if config_data.version != V1:
service_dict['volumes'] = [
volumes.namespace_spec(volume_spec)
for volume_spec in service_dict.get('volumes', [])
]
project.services.append(
Service(
service_dict.pop('name'),
client=client,
project=name,
use_networking=use_networking,
networks=networks,
networks=service_networks,
links=links,
net=net,
network_mode=network_mode,
volumes_from=volumes_from,
**service_dict))
project.networks += custom_networks
if project.uses_default_network():
project.networks.append(project.default_network)
if config_data.volumes:
for vol_name, data in config_data.volumes.items():
project.volumes.append(
Volume(
client=client, project=name, name=vol_name,
driver=data.get('driver'),
driver_opts=data.get('driver_opts'),
external_name=data.get('external_name')
)
)
**service_dict)
)
return project
@ -188,27 +175,27 @@ class Project(object):
del service_dict['links']
return links
def get_net(self, service_dict):
net = service_dict.pop('net', None)
if not net:
return Net(None)
def get_network_mode(self, service_dict, networks):
network_mode = service_dict.pop('network_mode', None)
if not network_mode:
if self.networks.use_networking:
return NetworkMode(networks[0]) if networks else NetworkMode('none')
return NetworkMode(None)
net_name = get_service_name_from_net(net)
if not net_name:
return Net(net)
service_name = get_service_name_from_network_mode(network_mode)
if service_name:
return ServiceNetworkMode(self.get_service(service_name))
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))
container_name = get_container_name_from_network_mode(network_mode)
if container_name:
try:
return ContainerNetworkMode(Container.from_id(self.client, container_name))
except APIError:
raise ConfigurationError(
"Service '{name}' uses the network stack of container '{dep}' which "
"does not exist.".format(name=service_dict['name'], dep=container_name))
return NetworkMode(network_mode)
def start(self, service_names=None, **options):
containers = []
@ -236,49 +223,13 @@ class Project(object):
def remove_stopped(self, service_names=None, **options):
parallel.parallel_remove(self.containers(service_names, stopped=True), options)
def initialize_volumes(self):
try:
for volume in self.volumes:
if volume.external:
log.debug(
'Volume {0} declared as external. No new '
'volume will be created.'.format(volume.name)
)
if not volume.exists():
raise ConfigurationError(
'Volume {name} declared as external, but could'
' not be found. Please create the volume manually'
' using `{command}{name}` and try again.'.format(
name=volume.full_name,
command='docker volume create --name='
)
)
continue
volume.create()
except NotFound:
raise ConfigurationError(
'Volume %s specifies nonexistent driver %s' % (volume.name, volume.driver)
)
except APIError as e:
if 'Choose a different volume name' in str(e):
raise ConfigurationError(
'Configuration for volume {0} specifies driver {1}, but '
'a volume with the same name uses a different driver '
'({3}). If you wish to use the new configuration, please '
'remove the existing volume "{2}" first:\n'
'$ docker volume rm {2}'.format(
volume.name, volume.driver, volume.full_name,
volume.inspect()['Driver']
)
)
def down(self, remove_image_type, include_volumes):
self.stop()
self.remove_stopped(v=include_volumes)
self.remove_networks()
self.networks.remove()
if include_volumes:
self.remove_volumes()
self.volumes.remove()
self.remove_images(remove_image_type)
@ -286,33 +237,6 @@ class Project(object):
for service in self.get_services():
service.remove_image(remove_image_type)
def remove_networks(self):
if not self.use_networking:
return
for network in self.networks:
network.remove()
def remove_volumes(self):
for volume in self.volumes:
volume.remove()
def initialize_networks(self):
if not self.use_networking:
return
for network in self.networks:
network.ensure()
def uses_default_network(self):
return any(
self.default_network.full_name in service.networks
for service in self.services
)
@property
def default_network(self):
return Network(client=self.client, project=self.name, name='default')
def restart(self, service_names=None, **options):
containers = self.containers(service_names, stopped=True)
parallel.parallel_restart(containers, options)
@ -378,13 +302,12 @@ class Project(object):
timeout=DEFAULT_TIMEOUT,
detached=False):
services = self.get_services_without_duplicate(service_names, include_deps=start_deps)
self.initialize()
services = self.get_services_without_duplicate(
service_names,
include_deps=start_deps)
plans = self._get_convergence_plans(services, strategy)
self.initialize_networks()
self.initialize_volumes()
return [
container
for service in services
@ -396,6 +319,10 @@ class Project(object):
)
]
def initialize(self):
self.networks.initialize()
self.volumes.initialize()
def _get_convergence_plans(self, services, strategy):
plans = {}
@ -455,22 +382,6 @@ class Project(object):
return acc + dep_services
def get_networks(service_dict, network_definitions):
networks = []
for name in service_dict.pop('networks', ['default']):
if name in ['bridge', 'host']:
networks.append(name)
else:
matches = [n for n in network_definitions if n.name == name]
if matches:
networks.append(matches[0].full_name)
else:
raise ConfigurationError(
'Service "{}" uses an undefined network "{}"'
.format(service_dict['name'], name))
return networks
def get_volumes_from(project, service_dict):
volumes_from = service_dict.pop('volumes_from', None)
if not volumes_from:

View file

@ -27,9 +27,7 @@ from .const import LABEL_SERVICE
from .const import LABEL_VERSION
from .container import Container
from .parallel import parallel_execute
from .parallel import parallel_remove
from .parallel import parallel_start
from .parallel import parallel_stop
from .progress_stream import stream_output
from .progress_stream import StreamOutputError
from .utils import json_hash
@ -49,7 +47,6 @@ DOCKER_START_KEYS = [
'extra_hosts',
'ipc',
'read_only',
'net',
'log_driver',
'log_opt',
'mem_limit',
@ -115,7 +112,7 @@ class Service(object):
use_networking=False,
links=None,
volumes_from=None,
net=None,
network_mode=None,
networks=None,
**options
):
@ -125,8 +122,8 @@ class Service(object):
self.use_networking = use_networking
self.links = links or []
self.volumes_from = volumes_from or []
self.net = net or Net(None)
self.networks = networks or []
self.network_mode = network_mode or NetworkMode(None)
self.networks = networks or {}
self.options = options
def containers(self, stopped=False, one_off=False, filters={}):
@ -180,6 +177,10 @@ class Service(object):
service.start_container(container)
return container
def stop_and_remove(container):
container.stop(timeout=timeout)
container.remove()
running_containers = self.containers(stopped=False)
num_running = len(running_containers)
@ -225,14 +226,17 @@ class Service(object):
if desired_num < num_running:
num_to_stop = num_running - desired_num
sorted_running_containers = sorted(
running_containers,
key=attrgetter('number'))
parallel_stop(
sorted_running_containers[-num_to_stop:],
dict(timeout=timeout))
parallel_remove(self.containers(stopped=True), {})
parallel_execute(
sorted_running_containers[-num_to_stop:],
stop_and_remove,
lambda c: c.name,
"Stopping and removing",
)
def create_container(self,
one_off=False,
@ -420,16 +424,23 @@ class Service(object):
return self.start_container(container)
def start_container(self, container):
container.start()
self.connect_container_to_networks(container)
container.start()
return container
def connect_container_to_networks(self, container):
for network in self.networks:
log.debug('Connecting "{}" to "{}"'.format(container.name, network))
connected_networks = container.get('NetworkSettings.Networks')
for network, aliases in self.networks.items():
if network in connected_networks:
self.client.disconnect_container_from_network(
container.id, network)
self.client.connect_container_to_network(
container.id, network,
aliases=[self.name])
aliases=list(self._get_aliases(container).union(aliases)),
links=self._get_links(False),
)
def remove_duplicate_containers(self, timeout=DEFAULT_TIMEOUT):
for c in self.duplicate_containers():
@ -460,7 +471,8 @@ class Service(object):
'options': self.options,
'image_id': self.image()['Id'],
'links': self.get_link_names(),
'net': self.net.id,
'net': self.network_mode.id,
'networks': list(self.networks.keys()),
'volumes_from': [
(v.source.name, v.mode)
for v in self.volumes_from if isinstance(v.source, Service)
@ -468,10 +480,11 @@ class Service(object):
}
def get_dependency_names(self):
net_name = self.net.service_name
net_name = self.network_mode.service_name
return (self.get_linked_service_names() +
self.get_volumes_from_names() +
([net_name] if net_name else []))
([net_name] if net_name else []) +
self.options.get('depends_on', []))
def get_linked_service_names(self):
return [service.name for (service, _) in self.links]
@ -498,28 +511,38 @@ class Service(object):
numbers = [c.number for c in containers]
return 1 if not numbers else max(numbers) + 1
def _get_links(self, link_to_self):
if self.use_networking:
return []
def _get_aliases(self, container):
if container.labels.get(LABEL_ONE_OFF) == "True":
return set()
return {self.name, container.short_id}
def _get_links(self, link_to_self):
links = {}
links = []
for service, link_name in self.links:
for container in service.containers():
links.append((container.name, link_name or service.name))
links.append((container.name, container.name))
links.append((container.name, container.name_without_project))
links[link_name or service.name] = container.name
links[container.name] = container.name
links[container.name_without_project] = container.name
if link_to_self:
for container in self.containers():
links.append((container.name, self.name))
links.append((container.name, container.name))
links.append((container.name, container.name_without_project))
links[self.name] = container.name
links[container.name] = container.name
links[container.name_without_project] = container.name
for external_link in self.options.get('external_links') or []:
if ':' not in external_link:
link_name = external_link
else:
external_link, link_name = external_link.split(':')
links.append((external_link, link_name))
return links
links[link_name] = external_link
return [
(alias, container_name)
for (container_name, alias) in links.items()
]
def _get_volumes_from(self):
return [build_volume_from(spec) for spec in self.volumes_from]
@ -568,20 +591,19 @@ class Service(object):
ports.append(port)
container_options['ports'] = ports
override_options['binds'] = merge_volume_bindings(
container_options.get('volumes') or [],
previous_container)
if 'volumes' in container_options:
container_options['volumes'] = dict(
(v.internal, {}) for v in container_options['volumes'])
container_options['environment'] = merge_environment(
self.options.get('environment'),
override_options.get('environment'))
if previous_container:
container_options['environment']['affinity:container'] = ('=' + previous_container.id)
binds, affinity = merge_volume_bindings(
container_options.get('volumes') or [],
previous_container)
override_options['binds'] = binds
container_options['environment'].update(affinity)
if 'volumes' in container_options:
container_options['volumes'] = dict(
(v.internal, {}) for v in container_options['volumes'])
container_options['image'] = self.image_name
@ -599,8 +621,8 @@ class Service(object):
override_options,
one_off=one_off)
container_options['networking_config'] = self._get_container_networking_config()
container_options['environment'] = format_environment(
container_options['environment'])
return container_options
def _get_container_host_config(self, override_options, one_off=False):
@ -615,7 +637,7 @@ class Service(object):
binds=options.get('binds'),
volumes_from=self._get_volumes_from(),
privileged=options.get('privileged', False),
network_mode=self.net.mode,
network_mode=self.network_mode.mode,
devices=options.get('devices'),
dns=options.get('dns'),
dns_search=options.get('dns_search'),
@ -635,13 +657,6 @@ class Service(object):
cpu_quota=options.get('cpu_quota'),
)
def _get_container_networking_config(self):
return self.client.create_networking_config({
network_name: self.client.create_endpoint_config(aliases=[self.name])
for network_name in self.networks
if network_name not in ['host', 'bridge']
})
def build(self, no_cache=False, pull=False, force_rm=False):
log.info('Building %s' % self.name)
@ -760,22 +775,22 @@ class Service(object):
log.error(six.text_type(e))
class Net(object):
class NetworkMode(object):
"""A `standard` network mode (ex: host, bridge)"""
service_name = None
def __init__(self, net):
self.net = net
def __init__(self, network_mode):
self.network_mode = network_mode
@property
def id(self):
return self.net
return self.network_mode
mode = id
class ContainerNet(object):
class ContainerNetworkMode(object):
"""A network mode that uses a container's network stack."""
service_name = None
@ -792,7 +807,7 @@ class ContainerNet(object):
return 'container:' + self.container.id
class ServiceNet(object):
class ServiceNetworkMode(object):
"""A network mode that uses a service's network stack."""
def __init__(self, service):
@ -861,18 +876,23 @@ def merge_volume_bindings(volumes, previous_container):
"""Return a list of volume bindings for a container. Container data volumes
are replaced by those from the previous container.
"""
affinity = {}
volume_bindings = dict(
build_volume_binding(volume)
for volume in volumes
if volume.external)
if previous_container:
data_volumes = get_container_data_volumes(previous_container, volumes)
warn_on_masked_volume(volumes, data_volumes, previous_container.service)
old_volumes = get_container_data_volumes(previous_container, volumes)
warn_on_masked_volume(volumes, old_volumes, previous_container.service)
volume_bindings.update(
build_volume_binding(volume) for volume in data_volumes)
build_volume_binding(volume) for volume in old_volumes)
return list(volume_bindings.values())
if old_volumes:
affinity = {'affinity:container': '=' + previous_container.id}
return list(volume_bindings.values()), affinity
def get_container_data_volumes(container, volumes_option):
@ -904,8 +924,12 @@ def get_container_data_volumes(container, volumes_option):
if not mount:
continue
# Volume was previously a host volume, now it's a container volume
if not mount.get('Name'):
continue
# Copy existing volume from old container
volume = volume._replace(external=mount['Source'])
volume = volume._replace(external=mount['Name'])
volumes.append(volume)
return volumes
@ -996,3 +1020,12 @@ def get_log_config(logging_dict):
type=log_driver,
config=log_options
)
# TODO: remove once fix is available in docker-py
def format_environment(environment):
def format_env(key, value):
if value is None:
return key
return '{key}={value}'.format(key=key, value=value)
return [format_env(*item) for item in environment.items()]

View file

@ -92,3 +92,7 @@ def json_hash(obj):
def microseconds_from_time_nano(time_nano):
return int(time_nano % 1000000000 / 1000)
def build_string_dict(source_dict):
return dict((k, str(v)) for k, v in source_dict.items())

View file

@ -3,8 +3,10 @@ from __future__ import unicode_literals
import logging
from docker.errors import APIError
from docker.errors import NotFound
from .config import ConfigurationError
log = logging.getLogger(__name__)
@ -50,3 +52,77 @@ class Volume(object):
if self.external_name:
return self.external_name
return '{0}_{1}'.format(self.project, self.name)
class ProjectVolumes(object):
def __init__(self, volumes):
self.volumes = volumes
@classmethod
def from_config(cls, name, config_data, client):
config_volumes = config_data.volumes or {}
volumes = {
vol_name: Volume(
client=client,
project=name,
name=vol_name,
driver=data.get('driver'),
driver_opts=data.get('driver_opts'),
external_name=data.get('external_name')
)
for vol_name, data in config_volumes.items()
}
return cls(volumes)
def remove(self):
for volume in self.volumes.values():
volume.remove()
def initialize(self):
try:
for volume in self.volumes.values():
if volume.external:
log.debug(
'Volume {0} declared as external. No new '
'volume will be created.'.format(volume.name)
)
if not volume.exists():
raise ConfigurationError(
'Volume {name} declared as external, but could'
' not be found. Please create the volume manually'
' using `{command}{name}` and try again.'.format(
name=volume.full_name,
command='docker volume create --name='
)
)
continue
log.info(
'Creating volume "{0}" with {1} driver'.format(
volume.full_name, volume.driver or 'default'
)
)
volume.create()
except NotFound:
raise ConfigurationError(
'Volume %s specifies nonexistent driver %s' % (volume.name, volume.driver)
)
except APIError as e:
if 'Choose a different volume name' in str(e):
raise ConfigurationError(
'Configuration for volume {0} specifies driver {1}, but '
'a volume with the same name uses a different driver '
'({3}). If you wish to use the new configuration, please '
'remove the existing volume "{2}" first:\n'
'$ docker volume rm {2}'.format(
volume.name, volume.driver, volume.full_name,
volume.inspect()['Driver']
)
)
def namespace_spec(self, volume_spec):
if not volume_spec.is_named_volume:
return volume_spec
volume = self.volumes[volume_spec.external]
return volume_spec._replace(external=volume.full_name)

View file

@ -17,6 +17,10 @@
# . ~/.docker-compose-completion.sh
__docker_compose_q() {
docker-compose 2>/dev/null ${compose_file:+-f $compose_file} ${compose_project:+-p $compose_project} "$@"
}
# suppress trailing whitespace
__docker_compose_nospace() {
# compopt is not available in ancient bash versions
@ -39,7 +43,7 @@ __docker_compose_compose_file() {
# Extracts all service names from the compose file.
___docker_compose_all_services_in_compose_file() {
awk -F: '/^[a-zA-Z0-9]/{print $1}' "${compose_file:-$(__docker_compose_compose_file)}" 2>/dev/null
__docker_compose_q config --services
}
# All services, even those without an existing container
@ -49,8 +53,12 @@ __docker_compose_services_all() {
# All services that have an entry with the given key in their compose_file section
___docker_compose_services_with_key() {
# 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}'
# flatten sections under "services" to one line, then filter lines containing the key and return section name
__docker_compose_q config \
| sed -n -e '/^services:/,/^[^ ]/p' \
| sed -n 's/^ //p' \
| awk '/^[a-zA-Z0-9]/{printf "\n"};{printf $0;next;}' \
| awk -F: -v key=": +$1:" '$0 ~ key {print $1}'
}
# All services that are defined by a Dockerfile reference
@ -67,11 +75,9 @@ __docker_compose_services_from_image() {
# by a boolean expression passed in as argument.
__docker_compose_services_with() {
local containers names
containers="$(docker-compose 2>/dev/null ${compose_file:+-f $compose_file} ${compose_project:+-p $compose_project} ps -q)"
names=( $(docker 2>/dev/null inspect --format "{{if ${1:-true}}} {{ .Name }} {{end}}" $containers) )
names=( ${names[@]%_*} ) # strip trailing numbers
names=( ${names[@]#*_} ) # strip project name
COMPREPLY=( $(compgen -W "${names[*]}" -- "$cur") )
containers="$(__docker_compose_q ps -q)"
names=$(docker 2>/dev/null inspect -f "{{if ${1:-true}}}{{range \$k, \$v := .Config.Labels}}{{if eq \$k \"com.docker.compose.service\"}}{{\$v}}{{end}}{{end}}{{end}}" $containers)
COMPREPLY=( $(compgen -W "$names" -- "$cur") )
}
# The services for which at least one paused container exists
@ -107,6 +113,18 @@ _docker_compose_config() {
}
_docker_compose_create() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--force-recreate --help --no-build --no-recreate" -- "$cur" ) )
;;
*)
__docker_compose_services_all
;;
esac
}
_docker_compose_docker_compose() {
case "$prev" in
--file|-f)
@ -409,6 +427,7 @@ _docker_compose() {
local commands=(
build
config
create
down
events
help

View file

@ -179,7 +179,7 @@ __docker-compose_commands() {
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
(( $#_docker_compose_subcommands > 0 )) && _store_cache docker_compose_subcommands _docker_compose_subcommands
fi
_describe -t docker-compose-commands "docker-compose command" _docker_compose_subcommands
}
@ -203,6 +203,20 @@ __docker-compose_subcommand() {
'(--quiet -q)'{--quiet,-q}"[Only validate the configuration, don't print anything.]" \
'--services[Print the service names, one per line.]' && ret=0
;;
(create)
_arguments \
$opts_help \
"(--no-recreate --no-build)--force-recreate[Recreate containers even if their configuration and image haven't changed. Incompatible with --no-recreate.]" \
"(--force-recreate)--no-build[If containers already exist, don't recreate them. Incompatible with --force-recreate.]" \
"(--force-recreate)--no-recreate[Don't build an image, even if it's missing]" \
'*:services:__docker-compose_services_all' && ret=0
;;
(down)
_arguments \
$opts_help \
"--rmi[Remove images, type may be one of: 'all' to remove all images, or 'local' to remove only images that don't have an custom name set by the 'image' field]:type:(all local)" \
'(-v --volumes)'{-v,--volumes}"[Remove data volumes]" && ret=0
;;
(events)
_arguments \
$opts_help \
@ -298,12 +312,13 @@ __docker-compose_subcommand() {
(up)
_arguments \
$opts_help \
'-d[Detached mode: Run containers in the background, print new container names.]' \
'(--abort-on-container-exit)-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.]" \
"--no-build[Don't build an image, even if it's missing]" \
"(-d)--abort-on-container-exit[Stops all containers if any container was stopped. Incompatible with -d.]" \
'(-t --timeout)'{-t,--timeout}"[Specify a shutdown timeout in seconds. (default: 10)]:seconds: " \
'*:services:__docker-compose_services_all' && ret=0
;;

View file

@ -0,0 +1,173 @@
#!/usr/bin/env python
"""
Migrate a Compose file from the V1 format in Compose 1.5 to the V2 format
supported by Compose 1.6+
"""
from __future__ import absolute_import
from __future__ import unicode_literals
import argparse
import logging
import sys
import ruamel.yaml
from compose.config.types import VolumeSpec
log = logging.getLogger('migrate')
def migrate(content):
data = ruamel.yaml.load(content, ruamel.yaml.RoundTripLoader)
service_names = data.keys()
for name, service in data.items():
warn_for_links(name, service)
warn_for_external_links(name, service)
rewrite_net(service, service_names)
rewrite_build(service)
rewrite_logging(service)
rewrite_volumes_from(service, service_names)
services = {name: data.pop(name) for name in data.keys()}
data['version'] = "2"
data['services'] = services
create_volumes_section(data)
return data
def warn_for_links(name, service):
links = service.get('links')
if links:
example_service = links[0].partition(':')[0]
log.warn(
"Service {name} has links, which no longer create environment "
"variables such as {example_service_upper}_PORT. "
"If you are using those in your application code, you should "
"instead connect directly to the hostname, e.g. "
"'{example_service}'."
.format(name=name, example_service=example_service,
example_service_upper=example_service.upper()))
def warn_for_external_links(name, service):
external_links = service.get('external_links')
if external_links:
log.warn(
"Service {name} has external_links: {ext}, which now work "
"slightly differently. In particular, two containers must be "
"connected to at least one network in common in order to "
"communicate, even if explicitly linked together.\n\n"
"Either connect the external container to your app's default "
"network, or connect both the external container and your "
"service's containers to a pre-existing network. See "
"https://docs.docker.com/compose/networking/ "
"for more on how to do this."
.format(name=name, ext=external_links))
def rewrite_net(service, service_names):
if 'net' in service:
network_mode = service.pop('net')
# "container:<service name>" is now "service:<service name>"
if network_mode.startswith('container:'):
name = network_mode.partition(':')[2]
if name in service_names:
network_mode = 'service:{}'.format(name)
service['network_mode'] = network_mode
def rewrite_build(service):
if 'dockerfile' in service:
service['build'] = {
'context': service.pop('build'),
'dockerfile': service.pop('dockerfile'),
}
def rewrite_logging(service):
if 'log_driver' in service:
service['logging'] = {'driver': service.pop('log_driver')}
if 'log_opt' in service:
service['logging']['options'] = service.pop('log_opt')
def rewrite_volumes_from(service, service_names):
for idx, volume_from in enumerate(service.get('volumes_from', [])):
if volume_from.split(':', 1)[0] not in service_names:
service['volumes_from'][idx] = 'container:%s' % volume_from
def create_volumes_section(data):
named_volumes = get_named_volumes(data['services'])
if named_volumes:
log.warn(
"Named volumes ({names}) must be explicitly declared. Creating a "
"'volumes' section with declarations.\n\n"
"For backwards-compatibility, they've been declared as external. "
"If you don't mind the volume names being prefixed with the "
"project name, you can remove the 'external' option from each one."
.format(names=', '.join(list(named_volumes))))
data['volumes'] = named_volumes
def get_named_volumes(services):
volume_specs = [
VolumeSpec.parse(volume)
for service in services.values()
for volume in service.get('volumes', [])
]
names = {
spec.external
for spec in volume_specs
if spec.is_named_volume
}
return {name: {'external': True} for name in names}
def write(stream, new_format, indent, width):
ruamel.yaml.dump(
new_format,
stream,
Dumper=ruamel.yaml.RoundTripDumper,
indent=indent,
width=width)
def parse_opts(args):
parser = argparse.ArgumentParser()
parser.add_argument("filename", help="Compose file filename.")
parser.add_argument("-i", "--in-place", action='store_true')
parser.add_argument(
"--indent", type=int, default=2,
help="Number of spaces used to indent the output yaml.")
parser.add_argument(
"--width", type=int, default=80,
help="Number of spaces used as the output width.")
return parser.parse_args()
def main(args):
logging.basicConfig(format='\033[33m%(levelname)s:\033[37m %(message)s\033[0m\n')
opts = parse_opts(args)
with open(opts.filename, 'r') as fh:
new_format = migrate(fh.read())
if opts.in_place:
output = open(opts.filename, 'w')
else:
output = sys.stdout
write(output, new_format, opts.indent, opts.width)
if __name__ == "__main__":
main(sys.argv)

View file

@ -23,8 +23,8 @@ exe = EXE(pyz,
'DATA'
),
(
'compose/config/fields_schema_v2.json',
'compose/config/fields_schema_v2.json',
'compose/config/fields_schema_v2.0.json',
'compose/config/fields_schema_v2.0.json',
'DATA'
),
(
@ -33,8 +33,8 @@ exe = EXE(pyz,
'DATA'
),
(
'compose/config/service_schema_v2.json',
'compose/config/service_schema_v2.json',
'compose/config/service_schema_v2.0.json',
'compose/config/service_schema_v2.0.json',
'DATA'
),
(

View file

@ -5,9 +5,10 @@ RUN svn checkout https://github.com/docker/docker/trunk/docs /docs/content/engin
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/kitematic/kitematic/trunk/docs /docs/content/kitematic
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
RUN svn checkout https://github.com/docker/notary/trunk/docs /docs/content/notary
RUN svn checkout https://github.com/docker/kitematic/trunk/docs /docs/content/kitematic
RUN svn checkout https://github.com/docker/toolbox/trunk/docs /docs/content/toolbox
RUN svn checkout https://github.com/docker/opensource/trunk/docs /docs/content/project
ENV PROJECT=compose
# To get the git info for this repo

View file

@ -58,7 +58,7 @@ The top of each Docker Compose documentation file contains TOML metadata. The me
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"
parent="workw_compose"
weight=2
+++
<![end-metadata]-->
@ -70,7 +70,7 @@ The metadata alone has this structure:
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"
parent="workw_compose"
weight=2
+++

View file

@ -4,8 +4,8 @@ title = "Command-line Completion"
description = "Compose CLI reference"
keywords = ["fig, composition, compose, docker, orchestration, cli, reference"]
[menu.main]
parent="smn_workw_compose"
weight=10
parent="workw_compose"
weight=88
+++
<![end-metadata]-->

View file

@ -1,90 +1,45 @@
<!--[metadata]>
+++
title = "Compose file reference"
title = "Compose File Reference"
description = "Compose file reference"
keywords = ["fig, composition, compose, docker"]
aliases = ["/compose/yml"]
[menu.main]
parent="smn_compose_ref"
parent="workw_compose"
weight=70
+++
<![end-metadata]-->
# Compose file reference
The compose file is a [YAML](http://yaml.org/) file where all the top level
keys are the name of a service, and the values are the service definition.
The default path for a compose file is `./docker-compose.yml`.
The Compose file is a [YAML](http://yaml.org/) file defining
[services](#service-configuration-reference),
[networks](#network-configuration-reference) and
[volumes](#volume-configuration-reference).
The default path for a Compose file is `./docker-compose.yml`.
Each service defined in `docker-compose.yml` must specify exactly one of
`image` or `build`. Other keys are optional, and are analogous to their
`docker run` command-line counterparts.
A service definition contains configuration which will be applied to each
container started for that service, much like passing command-line parameters to
`docker run`. Likewise, network and volume definitions are analogous to
`docker network create` and `docker volume create`.
As with `docker run`, options specified in the Dockerfile (e.g., `CMD`,
`EXPOSE`, `VOLUME`, `ENV`) are respected by default - you don't need to
specify them again in `docker-compose.yml`.
## Versioning
It is possible to use different versions of the `compose.yml` format.
Below are the formats currently supported by compose.
### Version 1
Compose files that do not declare a version are considered "version 1". In
those files, all the [services](#service-configuration-reference) are declared
at the root of the document.
Version 1 files do not support the declaration of
named [volumes](#volume-configuration-reference) or
[build arguments](#args).
Example:
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
- logvolume01:/var/log
links:
- redis
redis:
image: redis
### Version 2
Compose files using the version 2 syntax must indicate the version number at
the root of the document. All [services](#service-configuration-reference)
must be declared under the `services` key.
Named [volumes](#volume-configuration-reference) must be declared under the
`volumes` key.
Example:
version: 2
services:
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
- logvolume01:/var/log
links:
- redis
redis:
image: redis
volumes:
logvolume01:
driver: default
You can use environment variables in configuration values with a Bash-like
`${VARIABLE}` syntax - see [variable substitution](#variable-substitution) for
full details.
## Service configuration reference
> **Note:** There are two versions of the Compose file format version 1 (the
> legacy format, which does not support volumes or networks) and version 2 (the
> most up-to-date). For more information, see the [Versioning](#versioning)
> section.
This section contains a list of all configuration options supported by a service
definition.
@ -92,28 +47,30 @@ definition.
Configuration options that are applied at build time.
In version 1 this must be given as a string representing the context.
`build` can be specified either as a string containing a path to the build
context, or an object with the path specified under [context](#context) and
optionally [dockerfile](#dockerfile) and [args](#args).
build: .
build: ./dir
In version 2 this can alternatively be given as an object with extra options.
build:
context: ./dir
dockerfile: Dockerfile-alternate
args:
buildno: 1
version: 2
services:
web:
build: .
version: 2
services:
web:
build:
context: .
dockerfile: Dockerfile-alternate
args:
buildno: 1
> **Note**: In the [version 1 file format](#version-1), `build` is different in
> two ways:
>
> - Only the string form (`build: .`) is allowed - not the object form.
> - Using `build` together with `image` is not allowed. Attempting to do so
> results in an error.
#### context
> [Version 2 file format](#version-2) only. In version 1, just use
> [build](#build).
Either a path to a directory containing a Dockerfile, or a url to a git repository.
When the value supplied is a relative path, it is interpreted as relative to the
@ -122,29 +79,34 @@ 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:
context: /path/to/build/dir
Using `context` together with `image` is not allowed. Attempting to do so results in
an error.
context: ./dir
#### dockerfile
Alternate Dockerfile.
Compose will use an alternate file to build with. A build path must also be
specified using the `build` key.
specified.
build:
context: /path/to/build/dir
context: .
dockerfile: Dockerfile-alternate
Using `dockerfile` together with `image` is not allowed. Attempting to do so results in an error.
> **Note**: In the [version 1 file format](#version-1), `dockerfile` is
> different in two ways:
>
> - It appears alongside `build`, not as a sub-option:
>
> build: .
> dockerfile: Dockerfile-alternate
> - Using `dockerfile` together with `image` is not allowed. Attempting to do
> so results in an error.
#### args
> [Version 2 file format](#version-2) only.
Add build arguments. You can use either an array or a dictionary. Any
boolean values; true, false, yes, no, need to be enclosed in quotes to ensure
they are not converted to True or False by the YML parser.
@ -152,8 +114,6 @@ they are not converted to True or False by the YML parser.
Build arguments with only a key are resolved to their environment value on the
machine Compose is running on.
> **Note:** Introduced in version 2 of the compose file format.
build:
args:
buildno: 1
@ -210,6 +170,31 @@ client create option.
devices:
- "/dev/ttyUSB0:/dev/ttyUSB0"
### depends_on
Express dependency between services, which has two effects:
- `docker-compose up` will start services in dependency order. In the following
example, `db` and `redis` will be started before `web`.
- `docker-compose up SERVICE` will automatically include `SERVICE`'s
dependencies. In the following example, `docker-compose up web` will also
create and start `db` and `redis`.
Simple example:
version: '2'
services:
web:
build: .
depends_on:
- db
- redis
redis:
image: redis
db:
image: postgres
### dns
Custom DNS servers. Can be a single value or a list.
@ -336,6 +321,10 @@ container name and the link alias (`CONTAINER:ALIAS`).
- project_db_1:mysql
- project_db_1:postgresql
> **Note:** If you're using the [version 2 file format](#version-2), the
> externally-created containers must be connected to at least one of the same
> networks as the service which is linking to them.
### extra_hosts
Add hostname mappings. Use the same values as the docker client `--add-host` parameter.
@ -377,33 +366,35 @@ It's recommended that you use reverse-DNS notation to prevent your labels from c
### links
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).
a link alias (`SERVICE:ALIAS`), or just the service name.
links:
- db
- db:database
- redis
web:
links:
- db
- db:database
- redis
An entry with the alias' name will be created in `/etc/hosts` inside containers
for this service, e.g:
Containers for the linked service will be reachable at a hostname identical to
the alias, or the service name if no alias was specified.
172.17.2.186 db
172.17.2.186 database
172.17.2.187 redis
Links also express dependency between services in the same way as
[depends_on](#depends-on), so they determine the order of service startup.
Environment variables will also be created - see the [environment variable
reference](env.md) for details.
> **Note:** If you define both links and [networks](#networks), services with
> links between them must share at least one network in common in order to
> communicate.
### logging
Logging configuration for the service. This configuration replaces the previous
`log_driver` and `log_opt` keys.
> [Version 2 file format](#version-2) only. In version 1, use
> [log_driver](#log_driver) and [log_opt](#log_opt).
Logging configuration for the service.
logging:
driver: log_driver
options:
syslog-address: "tcp://192.168.0.42:123"
driver: syslog
options:
syslog-address: "tcp://192.168.0.42:123"
The `driver` name specifies a logging driver for the service's
containers, as with the ``--log-driver`` option for docker run
@ -421,21 +412,116 @@ The default value is json-file.
Specify logging options for the logging driver with the ``options`` key, as with the ``--log-opt`` option for `docker run`.
Logging options are key value pairs. An example of `syslog` options:
Logging options are key-value pairs. An example of `syslog` options:
driver: "syslog"
options:
syslog-address: "tcp://192.168.0.42:123"
### log_driver
> [Version 1 file format](#version-1) only. In version 2, use
> [logging](#logging).
Specify a log driver. The default is `json-file`.
log_driver: syslog
### log_opt
> [Version 1 file format](#version-1) only. In version 2, use
> [logging](#logging).
Specify logging options as key-value pairs. An example of `syslog` options:
log_opt:
syslog-address: "tcp://192.168.0.42:123"
### net
Networking mode. Use the same values as the docker client `--net` parameter.
> [Version 1 file format](#version-1) only. In version 2, use
> [network_mode](#network_mode).
Network mode. Use the same values as the docker client `--net` parameter.
The `container:...` form can take a service name instead of a container name or
id.
net: "bridge"
net: "none"
net: "container:[name or id]"
net: "host"
net: "none"
net: "container:[service name or container name/id]"
### network_mode
> [Version 2 file format](#version-2) only. In version 1, use [net](#net).
Network mode. Use the same values as the docker client `--net` parameter, plus
the special form `service:[service name]`.
network_mode: "bridge"
network_mode: "host"
network_mode: "none"
network_mode: "service:[service name]"
network_mode: "container:[container name/id]"
### networks
> [Version 2 file format](#version-2) only. In version 1, use [net](#net).
Networks to join, referencing entries under the
[top-level `networks` key](#network-configuration-reference).
networks:
- some-network
- other-network
#### aliases
Aliases (alternative hostnames) for this service on the network. Other containers on the same network can use either the service name or this alias to connect to one of the service's containers.
Since `aliases` is network-scoped, the same service can have different aliases on different networks.
> **Note**: A network-wide alias can be shared by multiple containers, and even by multiple services. If it is, then exactly which container the name will resolve to is not guaranteed.
The general format is shown here.
networks:
some-network:
aliases:
- alias1
- alias3
other-network:
aliases:
- alias2
In the example below, three services are provided (`web`, `worker`, and `db`), along with two networks (`new` and `legacy`). The `db` service is reachable at the hostname `db` or `database` on the `new` network, and at `db` or `mysql` on the `legacy` network.
version: 2
services:
web:
build: ./web
networks:
- new
worker:
build: ./worker
networks:
- legacy
db:
image: mysql
networks:
new:
aliases:
- database
legacy:
aliases:
- mysql
networks:
new:
legacy:
### pid
@ -469,9 +555,17 @@ port (a random host port will be chosen).
Override the default labeling scheme for each container.
security_opt:
- label:user:USER
- label:role:ROLE
security_opt:
- label:user:USER
- label:role:ROLE
### stop_signal
Sets an alternative signal to stop the container. By default `stop` uses
SIGTERM. Setting an alternative signal using `stop_signal` will cause
`stop` to send that signal instead.
stop_signal: SIGUSR1
### ulimits
@ -479,31 +573,50 @@ Override the default ulimits for a container. You can either specify a single
limit as an integer or soft/hard limits as a mapping.
ulimits:
nproc: 65535
nofile:
soft: 20000
hard: 40000
ulimits:
nproc: 65535
nofile:
soft: 20000
hard: 40000
### volumes, volume\_driver
Mount paths as volumes, optionally specifying a path on the host machine
Mount paths or named 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
For [version 2 files](#version-2), named volumes need to be specified with the
[top-level `volumes` key](#volume-configuration-reference).
When using [version 1](#version-1), the Docker Engine will create the named
volume automatically if it doesn't exist.
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 `..`.
If you use a volume name (instead of a volume path), you may also specify
a `volume_driver`.
volumes:
# Just specify a path and let the Engine create a volume
- /var/lib/mysql
# Specify an absolute path mapping
- /opt/data:/var/lib/mysql
# Path on the host, relative to the Compose file
- ./cache:/tmp/cache
# User-relative path
- ~/configs:/etc/configs/:ro
# Named volume
- datavolume:/var/lib/mysql
If you do not use a host path, you may specify a `volume_driver`.
volume_driver: mydriver
Note that for [version 2 files](#version-2), this driver
will not apply to named volumes (you should use the `driver` option when
[declaring the volume](#volume-configuration-reference) instead).
For [version 1](#version-1), both named volumes and container volumes will
use the specified driver.
> Note: No path expansion will be done if you have also specified a
> `volume_driver`.
@ -519,8 +632,18 @@ specifying read-only access(``ro``) or read-write(``rw``).
volumes_from:
- service_name
- container_name
- service_name:rw
- service_name:ro
- container:container_name
- container:container_name:rw
> **Note:** The `container:...` formats are only supported in the
> [version 2 file format](#version-2). In [version 1](#version-1), you can use
> container names without marking them as such:
>
> - service_name
> - service_name:ro
> - container_name
> - container_name:rw
### cpu\_shares, cpu\_quota, cpuset, domainname, hostname, ipc, mac\_address, mem\_limit, memswap\_limit, privileged, read\_only, restart, stdin\_open, tty, user, working\_dir
@ -556,26 +679,347 @@ While it is possible to declare volumes on the fly as part of the service
declaration, this section allows you to create named volumes that can be
reused across multiple services (without relying on `volumes_from`), and are
easily retrieved and inspected using the docker command line or API.
See the [docker volume](http://docs.docker.com/reference/commandline/volume/)
See the [docker volume](/engine/reference/commandline/volume_create.md)
subcommand documentation for more information.
### driver
Specify which volume driver should be used for this volume. Defaults to
`local`. An exception will be raised if the driver is not available.
`local`. The Docker Engine will return an error if the driver is not available.
driver: foobar
driver: foobar
### driver_opts
Specify a list of options as key-value pairs to pass to the driver for this
volume. Those options are driver dependent - consult the driver's
volume. Those options are driver-dependent - consult the driver's
documentation for more information. Optional.
driver_opts:
foo: "bar"
baz: 1
## external
If set to `true`, specifies that this volume has been created outside of
Compose. `docker-compose up` will not attempt to create it, and will raise
an error if it doesn't exist.
`external` cannot be used in conjunction with other volume configuration keys
(`driver`, `driver_opts`).
In the example below, instead of attemping to create a volume called
`[projectname]_data`, Compose will look for an existing volume simply
called `data` and mount it into the `db` service's containers.
version: '2'
services:
db:
image: postgres
volumes:
- data:/var/lib/postgres/data
volumes:
data:
external: true
You can also specify the name of the volume separately from the name used to
refer to it within the Compose file:
volumes
data:
external:
name: actual-name-of-volume
## Network configuration reference
The top-level `networks` key lets you specify networks to be created. For a full
explanation of Compose's use of Docker networking features, see the
[Networking guide](networking.md).
### driver
Specify which driver should be used for this network.
The default driver depends on how the Docker Engine you're using is configured,
but in most instances it will be `bridge` on a single host and `overlay` on a
Swarm.
The Docker Engine will return an error if the driver is not available.
driver: overlay
### driver_opts
Specify a list of options as key-value pairs to pass to the driver for this
network. Those options are driver-dependent - consult the driver's
documentation for more information. Optional.
driver_opts:
foo: "bar"
baz: 1
### ipam
Specify custom IPAM config. This is an object with several properties, each of
which is optional:
- `driver`: Custom IPAM driver, instead of the default.
- `config`: A list with zero or more config blocks, each containing any of
the following keys:
- `subnet`: Subnet in CIDR format that represents a network segment
- `ip_range`: Range of IPs from which to allocate container IPs
- `gateway`: IPv4 or IPv6 gateway for the master subnet
- `aux_addresses`: Auxiliary IPv4 or IPv6 addresses used by Network driver,
as a mapping from hostname to IP
A full example:
ipam:
driver: default
config:
- subnet: 172.28.0.0/16
ip_range: 172.28.5.0/24
gateway: 172.28.5.254
aux_addresses:
host1: 172.28.1.5
host2: 172.28.1.6
host3: 172.28.1.7
### external
If set to `true`, specifies that this network has been created outside of
Compose. `docker-compose up` will not attempt to create it, and will raise
an error if it doesn't exist.
`external` cannot be used in conjunction with other network configuration keys
(`driver`, `driver_opts`, `ipam`).
In the example below, `proxy` is the gateway to the outside world. Instead of
attemping to create a network called `[projectname]_outside`, Compose will
look for an existing network simply called `outside` and connect the `proxy`
service's containers to it.
version: '2'
services:
proxy:
build: ./proxy
networks:
- outside
- default
app:
build: ./app
networks:
- default
networks:
outside:
external: true
You can also specify the name of the network separately from the name used to
refer to it within the Compose file:
networks:
outside:
external:
name: actual-name-of-network
## Versioning
There are two versions of the Compose file format:
- Version 1, the legacy format. This is specified by omitting a `version` key at
the root of the YAML.
- Version 2, the recommended format. This is specified with a `version: '2'` entry
at the root of the YAML.
To move your project from version 1 to 2, see the [Upgrading](#upgrading)
section.
> **Note:** If you're using
> [multiple Compose files](extends.md#different-environments) or
> [extending services](extends.md#extending-services), each file must be of the
> same version - you cannot mix version 1 and 2 in a single project.
Several things differ depending on which version you use:
- The structure and permitted configuration keys
- The minimum Docker Engine version you must be running
- Compose's behaviour with regards to networking
These differences are explained below.
### Version 1
Compose files that do not declare a version are considered "version 1". In
those files, all the [services](#service-configuration-reference) are declared
at the root of the document.
Version 1 is supported by **Compose up to 1.6.x**. It will be deprecated in a
future Compose release.
Version 1 files cannot declare named
[volumes](#volume-configuration-reference), [networks](networking.md) or
[build arguments](#args).
Example:
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
links:
- redis
redis:
image: redis
### Version 2
Compose files using the version 2 syntax must indicate the version number at
the root of the document. All [services](#service-configuration-reference)
must be declared under the `services` key.
Version 2 files are supported by **Compose 1.6.0+** and require a Docker Engine
of version **1.10.0+**.
Named [volumes](#volume-configuration-reference) can be declared under the
`volumes` key, and [networks](#network-configuration-reference) can be declared
under the `networks` key.
Simple example:
version: '2'
services:
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
redis:
image: redis
A more extended example, defining volumes and networks:
version: '2'
services:
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
networks:
- front-tier
- back-tier
redis:
image: redis
volumes:
- redis-data:/var/lib/redis
networks:
- back-tier
volumes:
redis-data:
driver: local
networks:
front-tier:
driver: bridge
back-tier:
driver: bridge
### Upgrading
In the majority of cases, moving from version 1 to 2 is a very simple process:
1. Indent the whole file by one level and put a `services:` key at the top.
2. Add a `version: '2'` line at the top of the file.
It's more complicated if you're using particular configuration features:
- `dockerfile`: This now lives under the `build` key:
build:
context: .
dockerfile: Dockerfile-alternate
- `log_driver`, `log_opt`: These now live under the `logging` key:
logging:
driver: syslog
options:
syslog-address: "tcp://192.168.0.42:123"
- `links` with environment variables: As documented in the
[environment variables reference](link-env-deprecated.md), environment variables
created by
links have been deprecated for some time. In the new Docker network system,
they have been removed. You should either connect directly to the
appropriate hostname or set the relevant environment variable yourself,
using the link hostname:
web:
links:
- db
environment:
- DB_PORT=tcp://db:5432
- `external_links`: Compose uses Docker networks when running version 2
projects, so links behave slightly differently. In particular, two
containers must be connected to at least one network in common in order to
communicate, even if explicitly linked together.
Either connect the external container to your app's
[default network](networking.md), or connect both the external container and
your service's containers to an
[external network](networking.md#using-a-pre-existing-network).
- `net`: This is now replaced by [network_mode](#network_mode):
net: host -> network_mode: host
net: bridge -> network_mode: bridge
net: none -> network_mode: none
If you're using `net: "container:[service name]"`, you must now use
`network_mode: "service:[service name]"` instead.
net: "container:web" -> network_mode: "service:web"
If you're using `net: "container:[container name/id]"`, the value does not
need to change.
net: "container:cont-name" -> network_mode: "container:cont-name"
net: "container:abc12345" -> network_mode: "container:abc12345"
- `volumes` with named volumes: these must now be explicitly declared in a
top-level `volumes` section of your Compose file. If a service mounts a
named volume called `data`, you must declare a `data` volume in your
top-level `volumes` section. The whole file might look like this:
version: '2'
services:
db:
image: postgres
volumes:
- data:/var/lib/postgresql/data
volumes:
data: {}
By default, Compose creates a volume whose name is prefixed with your
project name. If you want it to just be called `data`, declared it as
external:
volumes:
data:
external: true
## Variable substitution

View file

@ -1,16 +1,16 @@
<!--[metadata]>
+++
title = "Quickstart Guide: Compose and Django"
title = "Quickstart: Compose and Django"
description = "Getting started with Docker Compose and Django"
keywords = ["documentation, docs, docker, compose, orchestration, containers"]
[menu.main]
parent="smn_workw_compose"
parent="workw_compose"
weight=4
+++
<![end-metadata]-->
# Quickstart Guide: Compose and Django
# Quickstart: Compose and Django
This quick-start guide demonstrates how to use Compose to set up and run a
simple Django/PostgreSQL app. Before starting, you'll need to have
@ -72,17 +72,19 @@ and a `docker-compose.yml` file.
9. Add the following configuration to the file.
db:
image: postgres
web:
build: .
command: python manage.py runserver 0.0.0.0:8000
volumes:
- .:/code
ports:
- "8000:8000"
links:
- db
version: '2'
services:
db:
image: postgres
web:
build: .
command: python manage.py runserver 0.0.0.0:8000
volumes:
- .:/code
ports:
- "8000:8000"
depends_on:
- db
This file defines two services: The `db` service and the `web` service.
@ -129,7 +131,7 @@ In this step, you create a Django started project by building the image from the
In this section, you set up the database connection for Django.
1. In your project dirctory, edit the `composeexample/settings.py` file.
1. In your project directory, edit the `composeexample/settings.py` file.
2. Replace the `DATABASES = ...` with the following:

View file

@ -1,11 +1,11 @@
<!--[metadata]>
+++
title = "Extending services in Compose"
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
parent="workw_compose"
weight=20
+++
<![end-metadata]-->
@ -32,17 +32,14 @@ contains your base configuration. The override file, as its name implies, can
contain configuration overrides for existing services or entirely new
services.
If a service is defined in both files, Compose merges the configurations using
the same rules as the `extends` field (see [Adding and overriding
configuration](#adding-and-overriding-configuration)), with one exception. If a
service contains `links` or `volumes_from` those fields are copied over and
replace any values in the original service, in the same way single-valued fields
are copied.
If a service is defined in both files Compose merges the configurations using
the rules described in [Adding and overriding
configuration](#adding-and-overriding-configuration).
To use multiple override files, or an override file with a different name, you
can use the `-f` option to specify the list of files. Compose merges files in
the order they're specified on the command line. See the [`docker-compose`
command reference](./reference/docker-compose.md) for more information about
command reference](./reference/overview.md) for more information about
using `-f`.
When you use multiple configuration files, you must make sure all paths in the
@ -176,10 +173,12 @@ is useful if you have several services that reuse a common set of configuration
options. Using `extends` you can define a common set of service options in one
place and refer to it from anywhere.
> **Note:** `links` and `volumes_from` are never shared between services using
> `extends`. See
> [Adding and overriding configuration](#adding-and-overriding-configuration)
> for more information.
> **Note:** `links`, `volumes_from`, and `depends_on` are never shared between
> services using >`extends`. 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.
### Understand the extends configuration
@ -275,13 +274,7 @@ common configuration:
## 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.
Compose copies configurations from the original service over to the local one.
If a configuration option is defined in both the original service the local
service, the local value *replaces* or *extends* the original value.

View file

@ -4,8 +4,9 @@ title = "Frequently Asked Questions"
description = "Docker Compose FAQ"
keywords = "documentation, docs, docker, compose, faq"
[menu.main]
parent="smn_workw_compose"
weight=9
identifier="faq.compose"
parent="workw_compose"
weight=90
+++
<![end-metadata]-->
@ -50,8 +51,8 @@ handling `SIGTERM` properly.
Compose uses the project name to create unique identifiers for all of a
project's containers and other resources. To run multiple copies of a project,
set a custom project name using the [`-p` command line
option](./reference/docker-compose.md) or the [`COMPOSE_PROJECT_NAME`
environment variable](./reference/overview.md#compose-project-name).
option](./reference/overview.md) or the [`COMPOSE_PROJECT_NAME`
environment variable](./reference/envvars.md#compose-project-name).
## What's the difference between `up`, `run`, and `start`?

View file

@ -4,8 +4,8 @@ title = "Getting Started"
description = "Getting started with Docker Compose"
keywords = ["documentation, docs, docker, compose, orchestration, containers"]
[menu.main]
parent="smn_workw_compose"
weight=3
parent="workw_compose"
weight=-85
+++
<![end-metadata]-->
@ -95,16 +95,19 @@ Define a set of services using `docker-compose.yml`:
1. Create a file called docker-compose.yml in your project directory and add
the following:
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
links:
- redis
redis:
image: redis
version: '2'
services:
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
depends_on:
- redis
redis:
image: redis
This Compose file defines two services, `web` and `redis`. The web service:

View file

@ -1,66 +1,21 @@
<!--[metadata]>
+++
title = "Overview of Docker Compose"
title = "Docker Compose"
description = "Introduction and Overview of Compose"
keywords = ["documentation, docs, docker, compose, orchestration, containers"]
[menu.main]
parent="smn_workw_compose"
identifier="workw_compose"
weight=-70
+++
<![end-metadata]-->
# Overview of Docker Compose
# Docker Compose
Compose is a tool for defining and running multi-container Docker applications.
With Compose, you use a Compose file to configure your application's services.
Then, using a single command, you create and start all the services
from your configuration. To learn more about all the features of Compose
see [the list of features](#features).
Compose is a tool for defining and running multi-container Docker applications. To learn more about Compose refer to the following documentation:
Compose is great for development, testing, and staging environments, as well as
CI workflows. You can learn more about each case in
[Common Use Cases](#common-use-cases).
Using Compose is basically a three-step process.
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.
A `docker-compose.yml` looks like this:
version: 2
services:
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
- logvolume01:/var/log
links:
- redis
redis:
image: redis
volumes:
logvolume01:
driver: default
For more information about the Compose file, see the
[Compose file reference](compose-file.md)
Compose has commands for managing the whole lifecycle of your application:
* Start, stop and rebuild services
* View the status of running services
* Stream the log output of running services
* Run a one-off command on a service
## Compose documentation
- [Installing Compose](install.md)
- [Compose Overview](overview.md)
- [Install Compose](install.md)
- [Getting Started](gettingstarted.md)
- [Get started with Django](django.md)
- [Get started with Rails](rails.md)
@ -69,123 +24,6 @@ Compose has commands for managing the whole lifecycle of your application:
- [Command line reference](./reference/index.md)
- [Compose file reference](compose-file.md)
## Features
The features of Compose that make it effective are:
* [Multiple isolated environments on a single host](#Multiple-isolated-environments-on-a-single-host)
* [Preserve volume data when containers are created](#preserve-volume-data-when-containers-are-created)
* [Only recreate containers that have changed](#only-recreate-containers-that-have-changed)
* [Variables and moving a composition between environments](#variables-and-moving-a-composition-between-environments)
#### Multiple isolated environments on a single host
Compose uses a project name to isolate environments from each other. You can use
this project name to:
* on a dev host, to create multiple copies of a single environment (ex: you want
to run a stable copy for each feature branch of a project)
* on a CI server, to keep builds from interfering with each other, you can set
the project name to a unique build number
* on a shared host or dev host, to prevent different projects which may use the
same service names, from interfering with each other
The default project name is the basename of the project directory. You can set
a custom project name by using the
[`-p` command line option](./reference/docker-compose.md) or the
[`COMPOSE_PROJECT_NAME` environment variable](./reference/overview.md#compose-project-name).
#### Preserve volume data when containers are created
Compose preserves all volumes used by your services. When `docker-compose up`
runs, if it finds any containers from previous runs, it copies the volumes from
the old container to the new container. This process ensures that any data
you've created in volumes isn't lost.
#### Only recreate containers that have changed
Compose caches the configuration used to create a container. When you
restart a service that has not changed, Compose re-uses the existing
containers. Re-using containers means that you can make changes to your
environment very quickly.
#### Variables and moving a composition between environments
Compose supports variables in the Compose file. You can use these variables
to customize your composition for different environments, or different users.
See [Variable substitution](compose-file.md#variable-substitution) for more
details.
You can extend a Compose file using the `extends` field or by creating multiple
Compose files. See [extends](extends.md) for more details.
## Common Use Cases
Compose can be used in many different ways. Some common use cases are outlined
below.
### Development environments
When you're developing software, the ability to run an application in an
isolated environment and interact with it is crucial. The Compose command
line tool can be used to create the environment and interact with it.
The [Compose file](compose-file.md) provides a way to document and configure
all of the application's service dependencies (databases, queues, caches,
web service APIs, etc). Using the Compose command line tool you can create
and start one or more containers for each dependency with a single command
(`docker-compose up`).
Together, these features provide a convenient way for developers to get
started on a project. Compose can reduce a multi-page "developer getting
started guide" to a single machine readable Compose file and a few commands.
### Automated testing environments
An important part of any Continuous Deployment or Continuous Integration process
is the automated test suite. Automated end-to-end testing requires an
environment in which to run tests. Compose provides a convenient way to create
and destroy isolated testing environments for your test suite. By defining the full
environment in a [Compose file](compose-file.md) you can create and destroy these
environments in just a few commands:
$ docker-compose up -d
$ ./run_tests
$ docker-compose down
### Single host deployments
Compose has traditionally been focused on development and testing workflows,
but with each release we're making progress on more production-oriented features.
You can use Compose to deploy to a remote Docker Engine. The Docker Engine may
be a single instance provisioned with
[Docker Machine](https://docs.docker.com/machine/) or an entire
[Docker Swarm](https://docs.docker.com/swarm/) cluster.
For details on using production-oriented features, see
[compose in production](production.md) in this documentation.
## Release Notes
To see a detailed list of changes for past and current releases of Docker
Compose, please refer to the
[CHANGELOG](https://github.com/docker/compose/blob/master/CHANGELOG.md).
## Getting help
Docker Compose is 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 freenode 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/opensource/get-help/).

View file

@ -1,11 +1,11 @@
<!--[metadata]>
+++
title = "Docker Compose"
title = "Install Compose"
description = "How to install Docker Compose"
keywords = ["compose, orchestration, install, installation, docker, documentation"]
[menu.main]
parent="mn_install"
weight=4
parent="workw_compose"
weight=-90
+++
<![end-metadata]-->
@ -39,7 +39,7 @@ which the release page specifies, in your terminal.
The following is an example command illustrating the format:
curl -L https://github.com/docker/compose/releases/download/1.5.2/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
curl -L https://github.com/docker/compose/releases/download/1.6.1/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
If you have problems installing with `curl`, see
[Alternative Install Options](#alternative-install-options).
@ -54,7 +54,7 @@ which the release page specifies, in your terminal.
7. Test the installation.
$ docker-compose --version
docker-compose version: 1.5.2
docker-compose version: 1.6.1
## Alternative install options
@ -77,7 +77,7 @@ to get started.
Compose can also be run inside a container, from a small bash script wrapper.
To install compose as a container run:
$ curl -L https://github.com/docker/compose/releases/download/1.5.2/run.sh > /usr/local/bin/docker-compose
$ curl -L https://github.com/docker/compose/releases/download/1.6.1/run.sh > /usr/local/bin/docker-compose
$ chmod +x /usr/local/bin/docker-compose
## Master builds

View file

@ -1,17 +1,20 @@
<!--[metadata]>
+++
title = "Compose environment variables reference"
title = "Link Environment Variables"
description = "Compose CLI reference"
keywords = ["fig, composition, compose, docker, orchestration, cli, reference"]
aliases = ["/compose/env"]
[menu.main]
parent="smn_compose_ref"
weight=3
parent="workw_compose"
weight=89
+++
<![end-metadata]-->
# Compose environment variables reference
# Link 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](compose-file.md#links) for details.
> **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](compose-file.md#links) for details.
>
> Environment variables will only be populated if you're using the [legacy version 1 Compose file format](compose-file.md#versioning).
Compose uses [Docker links] to expose services' containers to one another. Each linked container injects a set of environment variables, each of which begins with the uppercase name of the container.

View file

@ -4,91 +4,149 @@ title = "Networking in Compose"
description = "How Compose sets up networking between containers"
keywords = ["documentation, docs, docker, compose, orchestration, containers, networking"]
[menu.main]
parent="smn_workw_compose"
weight=6
parent="workw_compose"
weight=21
+++
<![end-metadata]-->
# Networking in Compose
> **Note:** Compose's networking support is experimental, and must be explicitly enabled with the `docker-compose --x-networking` flag.
> **Note:** This document only applies if you're using [version 2 of the Compose file format](compose-file.md#versioning). Networking features are not supported for version 1 (legacy) Compose files.
Compose sets up a single default
By default Compose sets up a single
[network](/engine/reference/commandline/network_create.md) for your app. Each
container for a service joins the default network and is both *reachable* by
other containers on that network, and *discoverable* by them at a hostname
identical to the container name.
> **Note:** Your app's network is given the same name as the "project name", which is based on the name of the directory it lives in. See the [Command line overview](reference/docker-compose.md) for how to override it.
> **Note:** Your app's network is given a name based on the "project name",
> which is based on the name of the directory it lives in. You can override the
> project name with either the [`--project-name`
> flag](reference/overview.md) or the [`COMPOSE_PROJECT_NAME` environment
> variable](reference/envvars.md#compose-project-name).
For example, suppose your app is in a directory called `myapp`, and your `docker-compose.yml` looks like this:
web:
build: .
ports:
- "8000:8000"
db:
image: postgres
version: '2'
When you run `docker-compose --x-networking up`, the following happens:
services:
web:
build: .
ports:
- "8000:8000"
db:
image: postgres
1. A network called `myapp` is created.
2. A container is created using `web`'s configuration. It joins the network
`myapp` under the name `myapp_web_1`.
3. A container is created using `db`'s configuration. It joins the network
`myapp` under the name `myapp_db_1`.
When you run `docker-compose up`, the following happens:
Each container can now look up the hostname `myapp_web_1` or `myapp_db_1` and
1. A network called `myapp_default` is created.
2. A container is created using `web`'s configuration. It joins the network
`myapp_default` under the name `web`.
3. A container is created using `db`'s configuration. It joins the network
`myapp_default` under the name `db`.
Each container can now look up the hostname `web` or `db` and
get back the appropriate container's IP address. For example, `web`'s
application code could connect to the URL `postgres://myapp_db_1:5432` and start
application code could connect to the URL `postgres://db:5432` and start
using the Postgres database.
Because `web` explicitly maps a port, it's also accessible from the outside world via port 8000 on your Docker host's network interface.
> **Note:** in the next release there will be additional aliases for the
> container, including a short name without the project name and container
> index. The full container name will remain as one of the alias for backwards
> compatibility.
## Updating containers
If you make a configuration change to a service and run `docker-compose up` to update it, the old container will be removed and the new one will join the network under a different IP address but the same name. Running containers will be able to look up that name and connect to the new address, but the old address will stop working.
If any containers have connections open to the old container, they will be closed. It is a container's responsibility to detect this condition, look up the name again and reconnect.
## Configure how services are published
By default, containers for each service are published on the network with the
container name. If you want to change the name, or stop containers from being
discoverable at all, you can use the `container_name` option:
web:
build: .
container_name: "my-web-application"
## Links
Docker links are a one-way, single-host communication system. They should now be considered deprecated, and you should update your app to use networking instead. In the majority of cases, this will simply involve removing the `links` sections from your `docker-compose.yml`.
Links allow you to define extra aliases by which a service is reachable from another service. They are not required to enable services to communicate - by default, any service can reach any other service at that service's name. In the following example, `db` is reachable from `web` at the hostnames `db` and `database`:
## Specifying the network driver
version: '2'
services:
web:
build: .
links:
- "db:database"
db:
image: postgres
By default, Compose uses the `bridge` driver when creating the apps network. The Docker Engine provides one other driver out-of-the-box: `overlay`, which implements secure communication between containers on different hosts (see the next section for how to set up and use the `overlay` driver). Docker also allows you to install [custom network drivers](/engine/extend/plugins_network.md).
See the [links reference](compose-file.md#links) for more information.
You can specify which one to use with the `--x-network-driver` flag:
$ docker-compose --x-networking --x-network-driver=overlay up
<!--[metadata]>
## Multi-host networking
(TODO: talk about Swarm and the overlay driver)
<![end-metadata]-->
When [deploying a Compose application to a Swarm cluster](swarm.md), you can make use of the built-in `overlay` driver to enable multi-host communication between containers with no changes to your Compose file or application code.
## Custom container network modes
Consult the [Getting started with multi-host networking](/engine/userguide/networking/get-started-overlay.md) to see how to set up a Swarm cluster. The cluster will use the `overlay` driver by default, but you can specify it explicitly if you prefer - see below for how to do this.
Compose allows you to specify a custom network mode for a service with the `net` option - for example, `net: "host"` specifies that its containers should use the same network namespace as the Docker host, and `net: "none"` specifies that they should have no networking capabilities.
## Specifying custom networks
If a service specifies the `net` option, its containers will *not* join the apps network and will not be able to communicate with other services in the app.
Instead of just using the default app network, you can specify your own networks with the top-level `networks` key. This lets you create more complex topologies and specify [custom network drivers](/engine/extend/plugins_network.md) and options. You can also use it to connect services to externally-created networks which aren't managed by Compose.
If *all* services in an app specify the `net` option, a network will not be created at all.
Each service can specify what networks to connect to with the *service-level* `networks` key, which is a list of names referencing entries under the *top-level* `networks` key.
Here's an example Compose file defining two custom networks. The `proxy` service is isolated from the `db` service, because they do not share a network in common - only `app` can talk to both.
version: '2'
services:
proxy:
build: ./proxy
networks:
- front
app:
build: ./app
networks:
- front
- back
db:
image: postgres
networks:
- back
networks:
front:
# Use a custom driver
driver: custom-driver-1
back:
# Use a custom driver which takes special options
driver: custom-driver-2
driver_opts:
foo: "1"
bar: "2"
For full details of the network configuration options available, see the following references:
- [Top-level `networks` key](compose-file.md#network-configuration-reference)
- [Service-level `networks` key](compose-file.md#networks)
## Configuring the default network
Instead of (or as well as) specifying your own networks, you can also change the settings of the app-wide default network by defining an entry under `networks` named `default`:
version: '2'
services:
web:
build: .
ports:
- "8000:8000"
db:
image: postgres
networks:
default:
# Use a custom driver
driver: custom-driver-1
## Using a pre-existing network
If you want your containers to join a pre-existing network, use the [`external` option](compose-file.md#network-configuration-reference):
networks:
default:
external:
name: my-pre-existing-network
Instead of attemping to create a network called `[projectname]_default`, Compose will look for a network called `my-pre-existing-network` and connect your app's containers to it.

191
docs/overview.md Normal file
View file

@ -0,0 +1,191 @@
<!--[metadata]>
+++
title = "Overview of Docker Compose"
description = "Introduction and Overview of Compose"
keywords = ["documentation, docs, docker, compose, orchestration, containers"]
[menu.main]
parent="workw_compose"
weight=-99
+++
<![end-metadata]-->
# Overview of Docker Compose
Compose is a tool for defining and running multi-container Docker applications.
With Compose, you use a Compose file to configure your application's services.
Then, using a single command, you create and start all the services
from your configuration. To learn more about all the features of Compose
see [the list of features](#features).
Compose is great for development, testing, and staging environments, as well as
CI workflows. You can learn more about each case in
[Common Use Cases](#common-use-cases).
Using Compose is basically a three-step process.
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.
A `docker-compose.yml` looks like this:
version: '2'
services:
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
- logvolume01:/var/log
links:
- redis
redis:
image: redis
volumes:
logvolume01: {}
For more information about the Compose file, see the
[Compose file reference](compose-file.md)
Compose has commands for managing the whole lifecycle of your application:
* Start, stop and rebuild services
* View the status of running services
* Stream the log output of running services
* Run a one-off command on a service
## Compose documentation
- [Installing Compose](install.md)
- [Getting Started](gettingstarted.md)
- [Get started with Django](django.md)
- [Get started with Rails](rails.md)
- [Get started with WordPress](wordpress.md)
- [Frequently asked questions](faq.md)
- [Command line reference](./reference/index.md)
- [Compose file reference](compose-file.md)
## Features
The features of Compose that make it effective are:
* [Multiple isolated environments on a single host](#Multiple-isolated-environments-on-a-single-host)
* [Preserve volume data when containers are created](#preserve-volume-data-when-containers-are-created)
* [Only recreate containers that have changed](#only-recreate-containers-that-have-changed)
* [Variables and moving a composition between environments](#variables-and-moving-a-composition-between-environments)
### Multiple isolated environments on a single host
Compose uses a project name to isolate environments from each other. You can use
this project name to:
* on a dev host, to create multiple copies of a single environment (ex: you want
to run a stable copy for each feature branch of a project)
* on a CI server, to keep builds from interfering with each other, you can set
the project name to a unique build number
* on a shared host or dev host, to prevent different projects which may use the
same service names, from interfering with each other
The default project name is the basename of the project directory. You can set
a custom project name by using the
[`-p` command line option](./reference/overview.md) or the
[`COMPOSE_PROJECT_NAME` environment variable](./reference/envvars.md#compose-project-name).
### Preserve volume data when containers are created
Compose preserves all volumes used by your services. When `docker-compose up`
runs, if it finds any containers from previous runs, it copies the volumes from
the old container to the new container. This process ensures that any data
you've created in volumes isn't lost.
### Only recreate containers that have changed
Compose caches the configuration used to create a container. When you
restart a service that has not changed, Compose re-uses the existing
containers. Re-using containers means that you can make changes to your
environment very quickly.
### Variables and moving a composition between environments
Compose supports variables in the Compose file. You can use these variables
to customize your composition for different environments, or different users.
See [Variable substitution](compose-file.md#variable-substitution) for more
details.
You can extend a Compose file using the `extends` field or by creating multiple
Compose files. See [extends](extends.md) for more details.
## Common Use Cases
Compose can be used in many different ways. Some common use cases are outlined
below.
### Development environments
When you're developing software, the ability to run an application in an
isolated environment and interact with it is crucial. The Compose command
line tool can be used to create the environment and interact with it.
The [Compose file](compose-file.md) provides a way to document and configure
all of the application's service dependencies (databases, queues, caches,
web service APIs, etc). Using the Compose command line tool you can create
and start one or more containers for each dependency with a single command
(`docker-compose up`).
Together, these features provide a convenient way for developers to get
started on a project. Compose can reduce a multi-page "developer getting
started guide" to a single machine readable Compose file and a few commands.
### Automated testing environments
An important part of any Continuous Deployment or Continuous Integration process
is the automated test suite. Automated end-to-end testing requires an
environment in which to run tests. Compose provides a convenient way to create
and destroy isolated testing environments for your test suite. By defining the full
environment in a [Compose file](compose-file.md) you can create and destroy these
environments in just a few commands:
$ docker-compose up -d
$ ./run_tests
$ docker-compose down
### Single host deployments
Compose has traditionally been focused on development and testing workflows,
but with each release we're making progress on more production-oriented features.
You can use Compose to deploy to a remote Docker Engine. The Docker Engine may
be a single instance provisioned with
[Docker Machine](https://docs.docker.com/machine/) or an entire
[Docker Swarm](https://docs.docker.com/swarm/) cluster.
For details on using production-oriented features, see
[compose in production](production.md) in this documentation.
## Release Notes
To see a detailed list of changes for past and current releases of Docker
Compose, please refer to the
[CHANGELOG](https://github.com/docker/compose/blob/master/CHANGELOG.md).
## Getting help
Docker Compose is 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 freenode 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/opensource/get-help/).

View file

@ -1,11 +1,11 @@
<!--[metadata]>
+++
title = "Using Compose in production"
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
parent="workw_compose"
weight=22
+++
<![end-metadata]-->
@ -60,7 +60,7 @@ recreating any services which `web` depends on.
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
[Docker Machine](/machine/overview) makes managing local and
remote Docker hosts very easy, and is recommended even if you're not deploying
remotely.
@ -69,14 +69,12 @@ commands will work with no further configuration.
### Running Compose on a Swarm cluster
[Docker Swarm](https://docs.docker.com/swarm/), a Docker-native clustering
[Docker Swarm](/swarm/overview), 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 <a
href="https://github.com/docker/compose/blob/master/SWARM.md">integration
guide</a>.
Compose/Swarm integration is still in the experimental stage, but if you'd like
to explore and experiment, check out the [integration guide](swarm.md).
## Compose documentation

View file

@ -1,15 +1,15 @@
<!--[metadata]>
+++
title = "Quickstart Guide: Compose and Rails"
title = "Quickstart: Compose and Rails"
description = "Getting started with Docker Compose and Rails"
keywords = ["documentation, docs, docker, compose, orchestration, containers"]
[menu.main]
parent="smn_workw_compose"
parent="workw_compose"
weight=5
+++
<![end-metadata]-->
## Quickstart Guide: Compose and Rails
## Quickstart: 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).
@ -43,17 +43,19 @@ You'll need an empty `Gemfile.lock` in order to build our `Dockerfile`.
Finally, `docker-compose.yml` is where the magic happens. This file describes the services that comprise your app (a database and a web app), how to get each one's Docker image (the database just runs on a pre-made PostgreSQL image, and the web app is built from the current directory), and the configuration needed to link them together and expose the web app's port.
db:
image: postgres
web:
build: .
command: bundle exec rails s -p 3000 -b '0.0.0.0'
volumes:
- .:/myapp
ports:
- "3000:3000"
links:
- db
version: '2'
services:
db:
image: postgres
web:
build: .
command: bundle exec rails s -p 3000 -b '0.0.0.0'
volumes:
- .:/myapp
ports:
- "3000:3000"
depends_on:
- db
### Build the project

View file

@ -1,106 +0,0 @@
<!--[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 [-f=<arg>...] [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
pause Pause services
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
unpause Unpause services
up Create and start containers
version Show the Docker-Compose version information
```
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. You
can supply multiple `-f` configuration files. When you supply multiple files,
Compose combines them into a single configuration. Compose builds the
configuration in the order you supply the files. Subsequent files override and
add to their successors.
For example, consider this command line:
```
$ docker-compose -f docker-compose.yml -f docker-compose.admin.yml run backup_db`
```
The `docker-compose.yml` file might specify a `webapp` service.
```
webapp:
image: examples/web
ports:
- "8000:8000"
volumes:
- "/data"
```
If the `docker-compose.admin.yml` also specifies this same service, any matching
fields will override the previous file. New values, add to the `webapp` service
configuration.
```
webapp:
build: .
environment:
- DEBUG=1
```
Use a `-f` with `-` (dash) as the filename to read the configuration from
stdin. When stdin is used all paths in the configuration are
relative to the current working directory.
The `-f` flag is optional. If you don't provide this flag on the command line,
Compose traverses the working directory and its subdirectories looking for a
`docker-compose.yml` and a `docker-compose.override.yml` file. You must
supply at least the `docker-compose.yml` file. If both files are present,
Compose combines the two files into a single configuration. The configuration
in the `docker-compose.override.yml` file is applied over and in addition to
the values in the `docker-compose.yml` file.
See also the `COMPOSE_FILE` [environment variable](overview.md#compose-file).
Each configuration 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. See also the `COMPOSE_PROJECT_NAME` [environment variable](
overview.md#compose-project-name)
## Where to go next
* [CLI environment variables](overview.md)
* [Command line reference](index.md)

View file

@ -4,7 +4,7 @@ title = "down"
description = "down"
keywords = ["fig, composition, compose, docker, orchestration, cli, down"]
[menu.main]
identifier="build.compose"
identifier="down.compose"
parent = "smn_compose_cli"
+++
<![end-metadata]-->

78
docs/reference/envvars.md Normal file
View file

@ -0,0 +1,78 @@
<!--[metadata]>
+++
title = "CLI Environment Variables"
description = "CLI Environment Variables"
keywords = ["fig, composition, compose, docker, orchestration, cli, reference"]
[menu.main]
parent = "smn_compose_cli"
weight=-1
+++
<![end-metadata]-->
# CLI 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 project directory. See also the `-p`
[command-line option](overview.md).
## 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. See also the `-f` [command-line option](overview.md).
## COMPOSE\_API\_VERSION
The Docker API only supports requests from clients which report a specific
version. If you receive a `client and server don't have same version error` using
`docker-compose`, you can workaround this error by setting this environment
variable. Set the version value to match the server version.
Setting this variable is intended as a workaround for situations where you need
to run temporarily with a mismatch between the client and server version. For
example, if you can upgrade the client but need to wait to upgrade the server.
Running with this variable set and a known mismatch does prevent some Docker
features from working properly. The exact features that fail would depend on the
Docker client and server versions. For this reason, running with this variable
set is only intended as a workaround and it is not officially supported.
If you run into problems running with this set, resolve the mismatch through
upgrade and remove this setting to see if your problems resolve before notifying
support.
## 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\_HTTP\_TIMEOUT
Configures the time (in seconds) a request to the Docker daemon is allowed to hang before Compose considers
it failed. Defaults to 60 seconds.
## Related Information
- [User guide](../index.md)
- [Installing Compose](../install.md)
- [Compose file reference](../compose-file.md)

View file

@ -1,18 +1,20 @@
<!--[metadata]>
+++
title = "Compose CLI reference"
title = "Command-line Reference"
description = "Compose CLI reference"
keywords = ["fig, composition, compose, docker, orchestration, cli, reference"]
[menu.main]
identifier = "smn_compose_cli"
parent = "smn_compose_ref"
parent = "workw_compose"
weight=80
+++
<![end-metadata]-->
## Compose CLI reference
## Compose command-line reference
The following pages describe the usage information for the [docker-compose](docker-compose.md) subcommands. You can also see this information by running `docker-compose [SUBCOMMAND] --help` from the command line.
The following pages describe the usage information for the [docker-compose](overview.md) subcommands. You can also see this information by running `docker-compose [SUBCOMMAND] --help` from the command line.
* [docker-compose](overview.md)
* [build](build.md)
* [config](config.md)
* [create](create.md)
@ -36,5 +38,5 @@ The following pages describe the usage information for the [docker-compose](dock
## Where to go next
* [CLI environment variables](overview.md)
* [docker-compose Command](docker-compose.md)
* [CLI environment variables](envvars.md)
* [docker-compose Command](overview.md)

View file

@ -1,8 +1,9 @@
<!--[metadata]>
+++
title = "Introduction to the CLI"
description = "Introduction to the CLI"
keywords = ["fig, composition, compose, docker, orchestration, cli, reference"]
title = "Overview of docker-compose CLI"
description = "Overview of docker-compose CLI"
keywords = ["fig, composition, compose, docker, orchestration, cli, docker-compose"]
aliases = ["/compose/reference/docker-compose/"]
[menu.main]
parent = "smn_compose_cli"
weight=-2
@ -10,80 +11,107 @@ weight=-2
<![end-metadata]-->
# Introduction to the CLI
# Overview of docker-compose 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.
This page provides the usage information for the `docker-compose` Command.
You can also see this information by running `docker-compose --help` from the
command line.
```
Define and run multi-container applications with Docker.
Usage:
docker-compose [-f=<arg>...] [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
config Validate and view the compose file
create Create services
down Stop and remove containers, networks, images, and volumes
events Receive real time events from containers
help Get help on a command
kill Kill containers
logs View output from containers
pause Pause services
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
unpause Unpause services
up Create and start containers
version Show the Docker-Compose version information
```
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. You
can supply multiple `-f` configuration files. When you supply multiple files,
Compose combines them into a single configuration. Compose builds the
configuration in the order you supply the files. Subsequent files override and
add to their successors.
For example, consider this command line:
```
$ docker-compose -f docker-compose.yml -f docker-compose.admin.yml run backup_db`
```
The `docker-compose.yml` file might specify a `webapp` service.
```
webapp:
image: examples/web
ports:
- "8000:8000"
volumes:
- "/data"
```
If the `docker-compose.admin.yml` also specifies this same service, any matching
fields will override the previous file. New values, add to the `webapp` service
configuration.
```
webapp:
build: .
environment:
- DEBUG=1
```
Use a `-f` with `-` (dash) as the filename to read the configuration from
stdin. When stdin is used all paths in the configuration are
relative to the current working directory.
The `-f` flag is optional. If you don't provide this flag on the command line,
Compose traverses the working directory and its parent directories looking for a
`docker-compose.yml` and a `docker-compose.override.yml` file. You must
supply at least the `docker-compose.yml` file. If both files are present on the
same directory level, Compose combines the two files into a single configuration.
The configuration in the `docker-compose.override.yml` file is applied over and
in addition to the values in the `docker-compose.yml` file.
See also the `COMPOSE_FILE` [environment variable](envvars.md#compose-file).
Each configuration 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. See also the `COMPOSE_PROJECT_NAME` [environment variable](
envvars.md#compose-project-name)
## Commands
## Where to go next
* [docker-compose Command](docker-compose.md)
* [CLI Reference](index.md)
## 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 project directory. See also the `-p`
[command-line option](docker-compose.md).
### 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. See also the `-f` [command-line option](docker-compose.md).
### COMPOSE\_API\_VERSION
The Docker API only supports requests from clients which report a specific
version. If you receive a `client and server don't have same version error` using
`docker-compose`, you can workaround this error by setting this environment
variable. Set the version value to match the server version.
Setting this variable is intended as a workaround for situations where you need
to run temporarily with a mismatch between the client and server version. For
example, if you can upgrade the client but need to wait to upgrade the server.
Running with this variable set and a known mismatch does prevent some Docker
features from working properly. The exact features that fail would depend on the
Docker client and server versions. For this reason, running with this variable
set is only intended as a workaround and it is not officially supported.
If you run into problems running with this set, resolve the mismatch through
upgrade and remove this setting to see if your problems resolve before notifying
support.
### 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\_HTTP\_TIMEOUT
Configures the time (in seconds) a request to the Docker daemon is allowed to hang before Compose considers
it failed. Defaults to 60 seconds.
## Related Information
- [User guide](../index.md)
- [Installing Compose](../install.md)
- [Compose file reference](../compose-file.md)
* [CLI environment variables](envvars.md)

184
docs/swarm.md Normal file
View file

@ -0,0 +1,184 @@
<!--[metadata]>
+++
title = "Using Compose with Swarm"
description = "How to use Compose and Swarm together to deploy apps to multi-host clusters"
keywords = ["documentation, docs, docker, compose, orchestration, containers, swarm"]
[menu.main]
parent="workw_compose"
+++
<![end-metadata]-->
# Using Compose with Swarm
Docker Compose and [Docker Swarm](/swarm/overview) 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.
The actual extent of integration depends on which version of the [Compose file
format](compose-file.md#versioning) you are using:
1. If you're using version 1 along with `links`, your app will work, but Swarm
will schedule all containers on one host, because links between containers
do not work across hosts with the old networking system.
2. If you're using version 2, your app should work with no changes:
- subject to the [limitations](#limitations) described below,
- as long as the Swarm cluster is configured to use the [overlay
driver](/engine/userguide/networking/dockernetworks.md#an-overlay-network),
or a custom driver which supports multi-host networking.
Read the [Getting started with multi-host
networking](/engine/userguide/networking/get-started-overlay.md) to see how to
set up a Swarm cluster with [Docker Machine](/machine/overview) and the overlay driver.
Once you've got it running, deploying your app to it should be as simple as:
$ eval "$(docker-machine env --swarm <name of swarm master machine>)"
$ docker-compose up
## Limitations
### Building images
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.
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 build -t myusername/web .
$ docker push myusername/web
$ cat docker-compose.yml
web:
image: myusername/web
$ docker-compose up -d
$ docker-compose scale web=3
### Multiple dependencies
If a service has multiple dependencies of the type which force co-scheduling
(see [Automatic scheduling](#automatic-scheduling) below), it's possible that
Swarm will schedule the dependencies on different nodes, making the dependent
service impossible to schedule. For example, here `foo` needs to be co-scheduled
with `bar` and `baz`:
version: "2"
services:
foo:
image: foo
volumes_from: ["bar"]
network_mode: "service:baz"
bar:
image: bar
baz:
image: baz
The problem is that Swarm might first schedule `bar` and `baz` on different
nodes (since they're not dependent on one another), making it impossible to
pick an appropriate node for `foo`.
To work around this, use [manual scheduling](#manual-scheduling) to ensure that
all three services end up on the same node:
version: "2"
services:
foo:
image: foo
volumes_from: ["bar"]
network_mode: "service:baz"
environment:
- "constraint:node==node-1"
bar:
image: bar
environment:
- "constraint:node==node-1"
baz:
image: baz
environment:
- "constraint:node==node-1"
### Host ports and recreating containers
If a service maps a port from the host, e.g. `80:8000`, then you may get an
error like this when running `docker-compose up` on it after the first time:
docker: Error response from daemon: unable to find a node that satisfies
container==6ab2dfe36615ae786ef3fc35d641a260e3ea9663d6e69c5b70ce0ca6cb373c02.
The usual cause of this error is that the container has a volume (defined either
in its image or in the Compose file) without an explicit mapping, and so in
order to preserve its data, Compose has directed Swarm to schedule the new
container on the same node as the old container. This results in a port clash.
There are two viable workarounds for this problem:
- Specify a named volume, and use a volume driver which is capable of mounting
the volume into the container regardless of what node it's scheduled on.
Compose does not give Swarm any specific scheduling instructions if a
service uses only named volumes.
version: "2"
services:
web:
build: .
ports:
- "80:8000"
volumes:
- web-logs:/var/log/web
volumes:
web-logs:
driver: custom-volume-driver
- Remove the old container before creating the new one. You will lose any data
in the volume.
$ docker-compose stop web
$ docker-compose rm -f web
$ docker-compose up web
## Scheduling containers
### Automatic scheduling
Some configuration options will result in containers being automatically
scheduled on the same Swarm node to ensure that they work correctly. These are:
- `network_mode: "service:..."` and `network_mode: "container:..."` (and
`net: "container:..."` in the version 1 file format).
- `volumes_from`
- `links`
### Manual scheduling
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.
# Schedule containers on a specific node
environment:
- "constraint:node==node-1"
# Schedule containers on a node that has the 'storage' label set to 'ssd'
environment:
- "constraint:storage==ssd"
# Schedule containers where the 'redis' image is already pulled
environment:
- "affinity:image==redis"
For the full set of available filters and expressions, see the [Swarm
documentation](/swarm/scheduler/filter.md).

View file

@ -1,16 +1,16 @@
<!--[metadata]>
+++
title = "Quickstart Guide: Compose and WordPress"
title = "Quickstart: Compose and WordPress"
description = "Getting started with Compose and WordPress"
keywords = ["documentation, docs, docker, compose, orchestration, containers"]
[menu.main]
parent="smn_workw_compose"
parent="workw_compose"
weight=6
+++
<![end-metadata]-->
# Quickstart Guide: Compose and WordPress
# Quickstart: Compose and WordPress
You can use Compose to easily run WordPress in an isolated environment built
with Docker containers.
@ -41,19 +41,21 @@ 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
version: '2'
services:
web:
build: .
command: php -S 0.0.0.0:8000 -t /code
ports:
- "8000:8000"
depends_on:
- db
volumes:
- .:/code
db:
image: orchardup/mysql
environment:
MYSQL_DATABASE: wordpress
A supporting file is needed to get this working. `wp-config.php` is
the standard WordPress config file with a single change to point the database

View file

@ -1 +1 @@
pyinstaller==3.0
pyinstaller==3.1.1

View file

@ -1,9 +1,9 @@
PyYAML==3.11
cached-property==1.2.0
dockerpty==0.3.4
docker-py==1.7.1
dockerpty==0.4.1
docopt==0.6.1
enum34==1.0.4
git+https://github.com/docker/docker-py.git@master#egg=docker-py
jsonschema==2.5.1
requests==2.7.0
six==1.7.3

View file

@ -41,6 +41,9 @@ Get-ChildItem -Recurse -Include *.pyc | foreach ($_) { Remove-Item $_.FullName }
# Create virtualenv
virtualenv .\venv
# pip and pyinstaller generate lots of warnings, so we need to ignore them
$ErrorActionPreference = "Continue"
# Install dependencies
.\venv\Scripts\pip install pypiwin32==219
.\venv\Scripts\pip install -r requirements.txt
@ -50,8 +53,6 @@ virtualenv .\venv
git rev-parse --short HEAD | out-file -encoding ASCII compose\GITSHA
# Build binary
# pyinstaller has lots of warnings, so we need to run with ErrorAction = Continue
$ErrorActionPreference = "Continue"
.\venv\Scripts\pyinstaller .\docker-compose.spec
$ErrorActionPreference = "Stop"

View file

@ -18,10 +18,13 @@ PREV_RELEASE=$1
VERSION=HEAD
URL="https://api.github.com/repos/docker/compose/compare"
curl -sf "$URL/$PREV_RELEASE...$VERSION" | \
contribs=$(curl -sf "$URL/$PREV_RELEASE...$VERSION" | \
jq -r '.commits[].author.login' | \
sort | \
uniq -c | \
sort -nr | \
awk '{print "@"$2","}' | \
xargs echo
sort -nr)
echo "Contributions by user: "
echo "$contribs"
echo
echo "$contribs" | awk '{print "@"$2","}' | xargs

View file

@ -63,15 +63,17 @@ git merge --strategy=ours --no-edit $REMOTE/release
git config "branch.${BRANCH}.release" $VERSION
editor=${EDITOR:-vim}
echo "Update versions in docs/install.md, compose/__init__.py, script/run.sh"
$EDITOR docs/install.md
$EDITOR compose/__init__.py
$EDITOR script/run.sh
$editor docs/install.md
$editor compose/__init__.py
$editor script/run.sh
echo "Write release notes in CHANGELOG.md"
browser "https://github.com/docker/compose/issues?q=milestone%3A$VERSION+is%3Aclosed"
$EDITOR CHANGELOG.md
$editor CHANGELOG.md
git diff
@ -84,10 +86,10 @@ echo "Push branch to user remote"
GITHUB_USER=$USER
USER_REMOTE="$(find_remote $GITHUB_USER/compose)"
if [ -z "$USER_REMOTE" ]; then
echo "No user remote found for $GITHUB_USER"
read -r -p "Enter the name of your github user: " GITHUB_USER
echo "$GITHUB_USER/compose not found"
read -r -p "Enter the name of your GitHub fork (username/repo): " GITHUB_REPO
# assumes there is already a user remote somewhere
USER_REMOTE=$(find_remote $GITHUB_USER/compose)
USER_REMOTE=$(find_remote $GITHUB_REPO)
fi
if [ -z "$USER_REMOTE" ]; then
>&2 echo "No user remote found. You need to 'git push' your branch."

View file

@ -60,7 +60,7 @@ sed -i -e 's/logo.png?raw=true/https:\/\/github.com\/docker\/compose\/raw\/maste
./script/write-git-sha
python setup.py sdist
if [ "$(command -v twine 2> /dev/null)" ]; then
twine upload ./dist/docker-compose-${VERSION}.tar.gz
twine upload ./dist/docker-compose-${VERSION/-/}.tar.gz
else
python setup.py upload
fi

View file

@ -32,7 +32,7 @@ if [[ "$sha" == "$(git rev-parse HEAD)" ]]; then
exit 0
fi
commits=$(git log --format="%H" "$sha..HEAD" | wc -l)
commits=$(git log --format="%H" "$sha..HEAD" | wc -l | xargs echo)
git rebase --onto $sha~1 HEAD~$commits $BRANCH
git cherry-pick $sha

View file

@ -5,7 +5,7 @@
# $Env:DOCKER_COMPOSE_OPTIONS.
if ($Env:DOCKER_COMPOSE_VERSION -eq $null -or $Env:DOCKER_COMPOSE_VERSION.Length -eq 0) {
$Env:DOCKER_COMPOSE_VERSION = "latest"
$Env:DOCKER_COMPOSE_VERSION = "1.6.0rc1"
}
if ($Env:DOCKER_COMPOSE_OPTIONS -eq $null) {

View file

@ -15,7 +15,7 @@
set -e
VERSION="1.5.2"
VERSION="1.6.1"
IMAGE="docker/compose:$VERSION"
@ -31,7 +31,9 @@ fi
# Setup volume mounts for compose config and context
VOLUMES="-v $(pwd):$(pwd)"
if [ "$(pwd)" != '/' ]; then
VOLUMES="-v $(pwd):$(pwd)"
fi
if [ -n "$COMPOSE_FILE" ]; then
compose_dir=$(dirname $COMPOSE_FILE)
fi
@ -45,9 +47,10 @@ fi
# Only allocate tty if we detect one
if [ -t 1 ]; then
DOCKER_RUN_OPTIONS="-ti"
else
DOCKER_RUN_OPTIONS="-i"
DOCKER_RUN_OPTIONS="-t"
fi
if [ -t 0 ]; then
DOCKER_RUN_OPTIONS="$DOCKER_RUN_OPTIONS -i"
fi
exec docker run --rm $DOCKER_RUN_OPTIONS $DOCKER_ADDR $COMPOSE_OPTIONS $VOLUMES -w $(pwd) $IMAGE $@
exec docker run --rm $DOCKER_RUN_OPTIONS $DOCKER_ADDR $COMPOSE_OPTIONS $VOLUMES -w "$(pwd)" $IMAGE "$@"

View file

@ -18,7 +18,7 @@ get_versions="docker run --rm
if [ "$DOCKER_VERSIONS" == "" ]; then
DOCKER_VERSIONS="$($get_versions default)"
elif [ "$DOCKER_VERSIONS" == "all" ]; then
DOCKER_VERSIONS="1.9.1 1.10.0-dev"
DOCKER_VERSIONS=$($get_versions -n 2 recent)
fi
@ -38,11 +38,7 @@ for version in $DOCKER_VERSIONS; do
trap "on_exit" EXIT
if [[ $version == *"-dev" ]]; then
repo="dnephin/dind"
else
repo="dockerswarm/dind"
fi
repo="dockerswarm/dind"
docker run \
-d \

View file

@ -34,8 +34,8 @@ install_requires = [
'requests >= 2.6.1, < 2.8',
'texttable >= 0.8.1, < 0.9',
'websocket-client >= 0.32.0, < 1.0',
'docker-py >= 1.5.0, < 2',
'dockerpty >= 0.3.4, < 0.4',
'docker-py >= 1.7.0, < 2',
'dockerpty >= 0.4.1, < 0.5',
'six >= 1.3.0, < 2',
'jsonschema >= 2.5.1, < 3',
]

View file

@ -43,12 +43,13 @@ def start_process(base_dir, options):
def wait_on_process(proc, returncode=0):
stdout, stderr = proc.communicate()
if proc.returncode != returncode:
print(stderr.decode('utf-8'))
print("Stderr: {}".format(stderr))
print("Stdout: {}".format(stdout))
assert proc.returncode == returncode
return ProcessResult(stdout.decode('utf-8'), stderr.decode('utf-8'))
def wait_on_condition(condition, delay=0.1, timeout=20):
def wait_on_condition(condition, delay=0.1, timeout=40):
start_time = time.time()
while not condition():
if time.time() - start_time > timeout:
@ -81,7 +82,6 @@ class ContainerStateCondition(object):
self.name = name
self.running = running
# State.Running == true
def __call__(self):
try:
container = self.client.inspect_container(self.name)
@ -159,7 +159,7 @@ class CLITestCase(DockerClientTestCase):
'-f', 'tests/fixtures/invalid-composefile/invalid.yml',
'config', '-q'
], returncode=1)
assert "'notaservice' doesn't have any configuration" in result.stderr
assert "'notaservice' must be a mapping" in result.stderr
# TODO: this shouldn't be v2-dependent
@v2_only()
@ -177,7 +177,7 @@ class CLITestCase(DockerClientTestCase):
output = yaml.load(result.stdout)
expected = {
'version': 2,
'version': '2.0',
'volumes': {'data': {'driver': 'local'}},
'networks': {'front': {}},
'services': {
@ -185,7 +185,7 @@ class CLITestCase(DockerClientTestCase):
'build': {
'context': os.path.abspath(self.base_dir),
},
'networks': ['front', 'default'],
'networks': {'front': None, 'default': None},
'volumes_from': ['service:other:rw'],
},
'other': {
@ -406,9 +406,11 @@ class CLITestCase(DockerClientTestCase):
services = self.project.get_services()
networks = self.client.networks(names=[self.project.default_network.full_name])
network_name = self.project.networks.networks['default'].full_name
networks = self.client.networks(names=[network_name])
self.assertEqual(len(networks), 1)
self.assertEqual(networks[0]['Driver'], 'bridge')
assert 'com.docker.network.bridge.enable_icc' not in networks[0]['Options']
network = self.client.inspect_network(networks[0]['Id'])
@ -419,12 +421,58 @@ class CLITestCase(DockerClientTestCase):
container = containers[0]
self.assertIn(container.id, network['Containers'])
networks = list(container.get('NetworkSettings.Networks'))
self.assertEqual(networks, [network['Name']])
networks = container.get('NetworkSettings.Networks')
self.assertEqual(list(networks), [network['Name']])
self.assertEqual(
sorted(networks[network['Name']]['Aliases']),
sorted([service.name, container.short_id]))
for service in services:
assert self.lookup(container, service.name)
@v2_only()
def test_up_with_default_network_config(self):
filename = 'default-network-config.yml'
self.base_dir = 'tests/fixtures/networks'
self._project = get_project(self.base_dir, [filename])
self.dispatch(['-f', filename, 'up', '-d'], None)
network_name = self.project.networks.networks['default'].full_name
networks = self.client.networks(names=[network_name])
assert networks[0]['Options']['com.docker.network.bridge.enable_icc'] == 'false'
@v2_only()
def test_up_with_network_aliases(self):
filename = 'network-aliases.yml'
self.base_dir = 'tests/fixtures/networks'
self.dispatch(['-f', filename, 'up', '-d'], None)
back_name = '{}_back'.format(self.project.name)
front_name = '{}_front'.format(self.project.name)
networks = [
n for n in self.client.networks()
if n['Name'].startswith('{}_'.format(self.project.name))
]
# Two networks were created: back and front
assert sorted(n['Name'] for n in networks) == [back_name, front_name]
web_container = self.project.get_service('web').containers()[0]
back_aliases = web_container.get(
'NetworkSettings.Networks.{}.Aliases'.format(back_name)
)
assert 'web' in back_aliases
front_aliases = web_container.get(
'NetworkSettings.Networks.{}.Aliases'.format(front_name)
)
assert 'web' in front_aliases
assert 'forward_facing' in front_aliases
assert 'ahead' in front_aliases
@v2_only()
def test_up_with_networks(self):
self.base_dir = 'tests/fixtures/networks'
@ -448,6 +496,10 @@ class CLITestCase(DockerClientTestCase):
app_container = self.project.get_service('app').containers()[0]
db_container = self.project.get_service('db').containers()[0]
for net_name in [front_name, back_name]:
links = app_container.get('NetworkSettings.Networks.{}.Links'.format(net_name))
assert '{}:database'.format(db_container.name) in links
# db and app joined the back network
assert sorted(back_network['Containers']) == sorted([db_container.id, app_container.id])
@ -461,6 +513,9 @@ class CLITestCase(DockerClientTestCase):
# app can see db
assert self.lookup(app_container, "db")
# app has aliased db to "database"
assert self.lookup(app_container, "database")
@v2_only()
def test_up_missing_network(self):
self.base_dir = 'tests/fixtures/networks'
@ -472,8 +527,13 @@ class CLITestCase(DockerClientTestCase):
assert 'Service "web" uses an undefined network "foo"' in result.stderr
@v2_only()
def test_up_predefined_networks(self):
filename = 'predefined-networks.yml'
def test_up_with_network_mode(self):
c = self.client.create_container('busybox', 'top', name='composetest_network_mode_container')
self.addCleanup(self.client.remove_container, c, force=True)
self.client.start(c)
container_mode_source = 'container:{}'.format(c['Id'])
filename = 'network-mode.yml'
self.base_dir = 'tests/fixtures/networks'
self._project = get_project(self.base_dir, [filename])
@ -491,6 +551,16 @@ class CLITestCase(DockerClientTestCase):
assert list(container.get('NetworkSettings.Networks')) == [name]
assert container.get('HostConfig.NetworkMode') == name
service_mode_source = 'container:{}'.format(
self.project.get_service('bridge').containers()[0].id)
service_mode_container = self.project.get_service('service').containers()[0]
assert not service_mode_container.get('NetworkSettings.Networks')
assert service_mode_container.get('HostConfig.NetworkMode') == service_mode_source
container_mode_container = self.project.get_service('container').containers()[0]
assert not container_mode_container.get('NetworkSettings.Networks')
assert container_mode_container.get('HostConfig.NetworkMode') == container_mode_source
@v2_only()
def test_up_external_networks(self):
filename = 'external-networks.yml'
@ -515,6 +585,29 @@ class CLITestCase(DockerClientTestCase):
container = self.project.containers()[0]
assert sorted(list(container.get('NetworkSettings.Networks'))) == sorted(network_names)
@v2_only()
def test_up_with_external_default_network(self):
filename = 'external-default.yml'
self.base_dir = 'tests/fixtures/networks'
self._project = get_project(self.base_dir, [filename])
result = self.dispatch(['-f', filename, 'up', '-d'], returncode=1)
assert 'declared as external, but could not be found' in result.stderr
networks = [
n['Name'] for n in self.client.networks()
if n['Name'].startswith('{}_'.format(self.project.name))
]
assert not networks
network_name = 'composetest_external_network'
self.client.create_network(network_name)
self.dispatch(['-f', filename, 'up', '-d'])
container = self.project.containers()[0]
assert list(container.get('NetworkSettings.Networks')) == [network_name]
@v2_only()
def test_up_no_services(self):
self.base_dir = 'tests/fixtures/no-services'
@ -524,30 +617,15 @@ class CLITestCase(DockerClientTestCase):
n['Name'] for n in self.client.networks()
if n['Name'].startswith('{}_'.format(self.project.name))
]
assert sorted(network_names) == [
'{}_{}'.format(self.project.name, name)
for name in ['bar', 'foo']
]
@v2_only()
def test_up_with_links_is_invalid(self):
self.base_dir = 'tests/fixtures/v2-simple'
result = self.dispatch(
['-f', 'links-invalid.yml', 'up', '-d'],
returncode=1)
# TODO: fix validation error messages for v2 files
# assert "Unsupported config option for service 'simple': 'links'" in result.stderr
assert "Unsupported config option" in result.stderr
assert network_names == []
def test_up_with_links_v1(self):
self.base_dir = 'tests/fixtures/links-composefile'
self.dispatch(['up', '-d', 'web'], None)
# No network was created
networks = self.client.networks(names=[self.project.default_network.full_name])
network_name = self.project.networks.networks['default'].full_name
networks = self.client.networks(names=[network_name])
assert networks == []
web = self.project.get_service('web')
@ -648,14 +726,31 @@ class CLITestCase(DockerClientTestCase):
wait_on_condition(ContainerCountCondition(self.project, 2))
os.kill(proc.pid, signal.SIGINT)
wait_on_condition(ContainerCountCondition(self.project, 0), timeout=30)
wait_on_condition(ContainerCountCondition(self.project, 0))
def test_up_handles_sigterm(self):
proc = start_process(self.base_dir, ['up', '-t', '2'])
wait_on_condition(ContainerCountCondition(self.project, 2))
os.kill(proc.pid, signal.SIGTERM)
wait_on_condition(ContainerCountCondition(self.project, 0), timeout=30)
wait_on_condition(ContainerCountCondition(self.project, 0))
@v2_only()
def test_up_handles_force_shutdown(self):
self.base_dir = 'tests/fixtures/sleeps-composefile'
proc = start_process(self.base_dir, ['up', '-t', '200'])
wait_on_condition(ContainerCountCondition(self.project, 2))
os.kill(proc.pid, signal.SIGTERM)
time.sleep(0.1)
os.kill(proc.pid, signal.SIGTERM)
wait_on_condition(ContainerCountCondition(self.project, 0))
def test_up_handles_abort_on_container_exit(self):
start_process(self.base_dir, ['up', '--abort-on-container-exit'])
wait_on_condition(ContainerCountCondition(self.project, 2))
self.project.stop(['simple'])
wait_on_condition(ContainerCountCondition(self.project, 0))
def test_run_service_without_links(self):
self.base_dir = 'tests/fixtures/links-composefile'
@ -677,6 +772,15 @@ class CLITestCase(DockerClientTestCase):
self.assertEqual(len(db.containers()), 1)
self.assertEqual(len(console.containers()), 0)
@v2_only()
def test_run_service_with_dependencies(self):
self.base_dir = 'tests/fixtures/v2-dependencies'
self.dispatch(['run', 'web', '/bin/true'], None)
db = self.project.get_service('db')
console = self.project.get_service('console')
self.assertEqual(len(db.containers()), 1)
self.assertEqual(len(console.containers()), 0)
def test_run_with_no_deps(self):
self.base_dir = 'tests/fixtures/links-composefile'
self.dispatch(['run', '--no-deps', 'web', '/bin/true'])
@ -872,14 +976,47 @@ class CLITestCase(DockerClientTestCase):
self.assertEqual(container.name, name)
@v2_only()
def test_run_with_networking(self):
self.base_dir = 'tests/fixtures/v2-simple'
self.dispatch(['run', 'simple', 'true'], None)
service = self.project.get_service('simple')
container, = service.containers(stopped=True, one_off=True)
networks = self.client.networks(names=[self.project.default_network.full_name])
self.assertEqual(len(networks), 1)
self.assertEqual(container.human_readable_command, u'true')
def test_run_interactive_connects_to_network(self):
self.base_dir = 'tests/fixtures/networks'
self.dispatch(['up', '-d'])
self.dispatch(['run', 'app', 'nslookup', 'app'])
self.dispatch(['run', 'app', 'nslookup', 'db'])
containers = self.project.get_service('app').containers(
stopped=True, one_off=True)
assert len(containers) == 2
for container in containers:
networks = container.get('NetworkSettings.Networks')
assert sorted(list(networks)) == [
'{}_{}'.format(self.project.name, name)
for name in ['back', 'front']
]
for _, config in networks.items():
assert not config['Aliases']
@v2_only()
def test_run_detached_connects_to_network(self):
self.base_dir = 'tests/fixtures/networks'
self.dispatch(['up', '-d'])
self.dispatch(['run', '-d', 'app', 'top'])
container = self.project.get_service('app').containers(one_off=True)[0]
networks = container.get('NetworkSettings.Networks')
assert sorted(list(networks)) == [
'{}_{}'.format(self.project.name, name)
for name in ['back', 'front']
]
for _, config in networks.items():
assert not config['Aliases']
assert self.lookup(container, 'app')
assert self.lookup(container, 'db')
def test_run_handles_sigint(self):
proc = start_process(self.base_dir, ['run', '-T', 'simple', 'top'])

View file

@ -1,6 +1,7 @@
web:
image: busybox
command: /bin/true
net: host
environment:
- FOO=1
- BAR=1

View file

@ -11,6 +11,7 @@ myweb:
BAR: "2"
# add BAZ
BAZ: "2"
net: bridge
mydb:
image: busybox
command: top

View file

@ -0,0 +1,12 @@
version: "2"
services:
myweb:
build: '.'
extends:
service: web
command: top
web:
build: '.'
network_mode: "service:net"
net:
build: '.'

View file

@ -1,4 +1,4 @@
version: 2
version: "2"
services:
simple:
image: busybox:latest

View file

@ -1,4 +1,4 @@
version: 2
version: "2"
services:
foo:

9
tests/fixtures/networks/bridge.yml vendored Normal file
View file

@ -0,0 +1,9 @@
version: "2"
services:
web:
image: busybox
command: top
networks:
- bridge
- default

View file

@ -0,0 +1,13 @@
version: "2"
services:
simple:
image: busybox:latest
command: top
another:
image: busybox:latest
command: top
networks:
default:
driver: bridge
driver_opts:
"com.docker.network.bridge.enable_icc": "false"

View file

@ -1,4 +1,4 @@
version: 2
version: "2"
services:
web:
@ -9,6 +9,8 @@ services:
image: busybox
command: top
networks: ["front", "back"]
links:
- "db:database"
db:
image: busybox
command: top

View file

@ -0,0 +1,12 @@
version: "2"
services:
simple:
image: busybox:latest
command: top
another:
image: busybox:latest
command: top
networks:
default:
external:
name: composetest_external_network

View file

@ -1,4 +1,4 @@
version: 2
version: "2"
services:
web:

View file

@ -1,4 +1,4 @@
version: 2
version: "2"
services:
web:

View file

@ -0,0 +1,16 @@
version: "2"
services:
web:
image: busybox
command: top
networks:
front:
aliases:
- forward_facing
- ahead
back:
networks:
front: {}
back: {}

View file

@ -0,0 +1,27 @@
version: "2"
services:
bridge:
image: busybox
command: top
network_mode: bridge
service:
image: busybox
command: top
network_mode: "service:bridge"
container:
image: busybox
command: top
network_mode: "container:composetest_network_mode_container"
host:
image: busybox
command: top
network_mode: host
none:
image: busybox
command: top
network_mode: none

View file

@ -1,17 +0,0 @@
version: 2
services:
bridge:
image: busybox
command: top
networks: ["bridge"]
host:
image: busybox
command: top
networks: ["host"]
none:
image: busybox
command: top
networks: []

View file

@ -1,4 +1,4 @@
version: 2
version: "2"
networks:
foo: {}

View file

@ -0,0 +1,10 @@
version: "2"
services:
simple:
image: busybox:latest
command: sleep 200
another:
image: busybox:latest
command: sleep 200

View file

@ -0,0 +1,13 @@
version: "2.0"
services:
db:
image: busybox:latest
command: top
web:
image: busybox:latest
command: top
depends_on:
- db
console:
image: busybox:latest
command: top

View file

@ -1,5 +1,5 @@
version: 2
version: "2"
volumes:
data:

View file

@ -1,4 +1,4 @@
version: 2
version: "2"
services:
simple:
image: busybox:latest

View file

@ -1,4 +1,4 @@
version: 2
version: "2"
services:
simple:
image: busybox:latest

View file

@ -4,10 +4,13 @@ from __future__ import unicode_literals
import random
import py
import pytest
from docker.errors import NotFound
from .testcases import DockerClientTestCase
from compose.config import config
from compose.config import ConfigurationError
from compose.config.config import V2_0
from compose.config.types import VolumeFromSpec
from compose.config.types import VolumeSpec
from compose.const import LABEL_PROJECT
@ -104,7 +107,71 @@ class ProjectTest(DockerClientTestCase):
db = project.get_service('db')
self.assertEqual(db._get_volumes_from(), [data_container.id + ':rw'])
def test_net_from_service(self):
@v2_only()
def test_network_mode_from_service(self):
project = Project.from_config(
name='composetest',
client=self.client,
config_data=build_service_dicts({
'version': V2_0,
'services': {
'net': {
'image': 'busybox:latest',
'command': ["top"]
},
'web': {
'image': 'busybox:latest',
'network_mode': 'service:net',
'command': ["top"]
},
},
}),
)
project.up()
web = project.get_service('web')
net = project.get_service('net')
self.assertEqual(web.network_mode.mode, 'container:' + net.containers()[0].id)
@v2_only()
def test_network_mode_from_container(self):
def get_project():
return Project.from_config(
name='composetest',
config_data=build_service_dicts({
'version': V2_0,
'services': {
'web': {
'image': 'busybox:latest',
'network_mode': 'container:composetest_net_container'
},
},
}),
client=self.client,
)
with pytest.raises(ConfigurationError) as excinfo:
get_project()
assert "container 'composetest_net_container' which does not exist" in excinfo.exconly()
net_container = Container.create(
self.client,
image='busybox:latest',
name='composetest_net_container',
command='top',
labels={LABEL_PROJECT: 'composetest'},
)
net_container.start()
project = get_project()
project.up()
web = project.get_service('web')
self.assertEqual(web.network_mode.mode, 'container:' + net_container.id)
def test_net_from_service_v1(self):
project = Project.from_config(
name='composetest',
config_data=build_service_dicts({
@ -125,9 +192,26 @@ class ProjectTest(DockerClientTestCase):
web = project.get_service('web')
net = project.get_service('net')
self.assertEqual(web.net.mode, 'container:' + net.containers()[0].id)
self.assertEqual(web.network_mode.mode, 'container:' + net.containers()[0].id)
def test_net_from_container_v1(self):
def get_project():
return Project.from_config(
name='composetest',
config_data=build_service_dicts({
'web': {
'image': 'busybox:latest',
'net': 'container:composetest_net_container'
},
}),
client=self.client,
)
with pytest.raises(ConfigurationError) as excinfo:
get_project()
assert "container 'composetest_net_container' which does not exist" in excinfo.exconly()
def test_net_from_container(self):
net_container = Container.create(
self.client,
image='busybox:latest',
@ -137,21 +221,11 @@ class ProjectTest(DockerClientTestCase):
)
net_container.start()
project = Project.from_config(
name='composetest',
config_data=build_service_dicts({
'web': {
'image': 'busybox:latest',
'net': 'container:composetest_net_container'
},
}),
client=self.client,
)
project = get_project()
project.up()
web = project.get_service('web')
self.assertEqual(web.net.mode, 'container:' + net_container.id)
self.assertEqual(web.network_mode.mode, 'container:' + net_container.id)
def test_start_pause_unpause_stop_kill_remove(self):
web = self.create_service('web')
@ -486,11 +560,12 @@ class ProjectTest(DockerClientTestCase):
@v2_only()
def test_project_up_networks(self):
config_data = config.Config(
version=2,
version=V2_0,
services=[{
'name': 'web',
'image': 'busybox:latest',
'command': 'top',
'networks': {'foo': None, 'bar': None, 'baz': None},
}],
volumes={},
networks={
@ -516,12 +591,73 @@ class ProjectTest(DockerClientTestCase):
foo_data = self.client.inspect_network('composetest_foo')
self.assertEqual(foo_data['Driver'], 'bridge')
@v2_only()
def test_up_with_ipam_config(self):
config_data = config.Config(
version=V2_0,
services=[{
'name': 'web',
'image': 'busybox:latest',
'networks': {'front': None},
}],
volumes={},
networks={
'front': {
'driver': 'bridge',
'driver_opts': {
"com.docker.network.bridge.enable_icc": "false",
},
'ipam': {
'driver': 'default',
'config': [{
"subnet": "172.28.0.0/16",
"ip_range": "172.28.5.0/24",
"gateway": "172.28.5.254",
"aux_addresses": {
"a": "172.28.1.5",
"b": "172.28.1.6",
"c": "172.28.1.7",
},
}],
},
},
},
)
project = Project.from_config(
client=self.client,
name='composetest',
config_data=config_data,
)
project.up()
network = self.client.networks(names=['composetest_front'])[0]
assert network['Options'] == {
"com.docker.network.bridge.enable_icc": "false"
}
assert network['IPAM'] == {
'Driver': 'default',
'Options': None,
'Config': [{
'Subnet': "172.28.0.0/16",
'IPRange': "172.28.5.0/24",
'Gateway': "172.28.5.254",
'AuxiliaryAddresses': {
'a': '172.28.1.5',
'b': '172.28.1.6',
'c': '172.28.1.7',
},
}],
}
@v2_only()
def test_project_up_volumes(self):
vol_name = '{0:x}'.format(random.getrandbits(32))
full_vol_name = 'composetest_{0}'.format(vol_name)
config_data = config.Config(
version=2,
version=V2_0,
services=[{
'name': 'web',
'image': 'busybox:latest',
@ -547,7 +683,7 @@ class ProjectTest(DockerClientTestCase):
base_file = config.ConfigFile(
'base.yml',
{
'version': 2,
'version': V2_0,
'services': {
'simple': {'image': 'busybox:latest', 'command': 'top'},
'another': {
@ -566,7 +702,7 @@ class ProjectTest(DockerClientTestCase):
override_file = config.ConfigFile(
'override.yml',
{
'version': 2,
'version': V2_0,
'services': {
'another': {
'logging': {
@ -599,7 +735,7 @@ class ProjectTest(DockerClientTestCase):
vol_name = '{0:x}'.format(random.getrandbits(32))
full_vol_name = 'composetest_{0}'.format(vol_name)
config_data = config.Config(
version=2,
version=V2_0,
services=[{
'name': 'web',
'image': 'busybox:latest',
@ -613,7 +749,7 @@ class ProjectTest(DockerClientTestCase):
name='composetest',
config_data=config_data, client=self.client
)
project.initialize_volumes()
project.volumes.initialize()
volume_data = self.client.inspect_volume(full_vol_name)
self.assertEqual(volume_data['Name'], full_vol_name)
@ -624,7 +760,7 @@ class ProjectTest(DockerClientTestCase):
vol_name = '{0:x}'.format(random.getrandbits(32))
full_vol_name = 'composetest_{0}'.format(vol_name)
config_data = config.Config(
version=2,
version=V2_0,
services=[{
'name': 'web',
'image': 'busybox:latest',
@ -649,7 +785,7 @@ class ProjectTest(DockerClientTestCase):
vol_name = '{0:x}'.format(random.getrandbits(32))
config_data = config.Config(
version=2,
version=V2_0,
services=[{
'name': 'web',
'image': 'busybox:latest',
@ -664,7 +800,7 @@ class ProjectTest(DockerClientTestCase):
config_data=config_data, client=self.client
)
with self.assertRaises(config.ConfigurationError):
project.initialize_volumes()
project.volumes.initialize()
@v2_only()
def test_initialize_volumes_updated_driver(self):
@ -672,7 +808,7 @@ class ProjectTest(DockerClientTestCase):
full_vol_name = 'composetest_{0}'.format(vol_name)
config_data = config.Config(
version=2,
version=V2_0,
services=[{
'name': 'web',
'image': 'busybox:latest',
@ -685,7 +821,7 @@ class ProjectTest(DockerClientTestCase):
name='composetest',
config_data=config_data, client=self.client
)
project.initialize_volumes()
project.volumes.initialize()
volume_data = self.client.inspect_volume(full_vol_name)
self.assertEqual(volume_data['Name'], full_vol_name)
@ -696,10 +832,11 @@ class ProjectTest(DockerClientTestCase):
)
project = Project.from_config(
name='composetest',
config_data=config_data, client=self.client
config_data=config_data,
client=self.client
)
with self.assertRaises(config.ConfigurationError) as e:
project.initialize_volumes()
project.volumes.initialize()
assert 'Configuration for volume {0} specifies driver smb'.format(
vol_name
) in str(e.exception)
@ -711,7 +848,7 @@ class ProjectTest(DockerClientTestCase):
full_vol_name = 'composetest_{0}'.format(vol_name)
self.client.create_volume(vol_name)
config_data = config.Config(
version=2,
version=V2_0,
services=[{
'name': 'web',
'image': 'busybox:latest',
@ -726,7 +863,7 @@ class ProjectTest(DockerClientTestCase):
name='composetest',
config_data=config_data, client=self.client
)
project.initialize_volumes()
project.volumes.initialize()
with self.assertRaises(NotFound):
self.client.inspect_volume(full_vol_name)
@ -736,7 +873,7 @@ class ProjectTest(DockerClientTestCase):
vol_name = '{0:x}'.format(random.getrandbits(32))
config_data = config.Config(
version=2,
version=V2_0,
services=[{
'name': 'web',
'image': 'busybox:latest',
@ -752,7 +889,44 @@ class ProjectTest(DockerClientTestCase):
config_data=config_data, client=self.client
)
with self.assertRaises(config.ConfigurationError) as e:
project.initialize_volumes()
project.volumes.initialize()
assert 'Volume {0} declared as external'.format(
vol_name
) in str(e.exception)
@v2_only()
def test_project_up_named_volumes_in_binds(self):
vol_name = '{0:x}'.format(random.getrandbits(32))
full_vol_name = 'composetest_{0}'.format(vol_name)
base_file = config.ConfigFile(
'base.yml',
{
'version': V2_0,
'services': {
'simple': {
'image': 'busybox:latest',
'command': 'top',
'volumes': ['{0}:/data'.format(vol_name)]
},
},
'volumes': {
vol_name: {'driver': 'local'}
}
})
config_details = config.ConfigDetails('.', [base_file])
config_data = config.load(config_details)
project = Project.from_config(
name='composetest', config_data=config_data, client=self.client
)
service = project.services[0]
self.assertEqual(service.name, 'simple')
volumes = service.options.get('volumes')
self.assertEqual(len(volumes), 1)
self.assertEqual(volumes[0].external, full_vol_name)
project.up()
engine_volumes = self.client.volumes()['Volumes']
container = service.get_container()
assert [mount['Name'] for mount in container.get('Mounts')] == [full_vol_name]
assert next((v for v in engine_volumes if v['Name'] == vol_name), None) is None

View file

@ -7,7 +7,6 @@ import tempfile
from os import path
from docker.errors import APIError
from pytest import mark
from six import StringIO
from six import text_type
@ -27,7 +26,7 @@ from compose.const import LABEL_VERSION
from compose.container import Container
from compose.service import ConvergencePlan
from compose.service import ConvergenceStrategy
from compose.service import Net
from compose.service import NetworkMode
from compose.service import Service
@ -267,6 +266,30 @@ class ServiceTest(DockerClientTestCase):
self.client.inspect_container,
old_container.id)
def test_execute_convergence_plan_recreate_twice(self):
service = self.create_service(
'db',
volumes=[VolumeSpec.parse('/etc')],
entrypoint=['top'],
command=['-d', '1'])
orig_container = service.create_container()
service.start_container(orig_container)
orig_container.inspect() # reload volume data
volume_path = orig_container.get_mount('/etc')['Source']
# Do this twice to reproduce the bug
for _ in range(2):
new_container, = service.execute_convergence_plan(
ConvergencePlan('recreate', [orig_container]))
assert new_container.get_mount('/etc')['Source'] == volume_path
assert ('affinity:container==%s' % orig_container.id in
new_container.get('Config.Env'))
orig_container = new_container
def test_execute_convergence_plan_when_containers_are_stopped(self):
service = self.create_service(
'db',
@ -344,6 +367,31 @@ class ServiceTest(DockerClientTestCase):
)
self.assertEqual(new_container.get_mount('/data')['Source'], volume_path)
def test_execute_convergence_plan_when_host_volume_is_removed(self):
host_path = '/tmp/host-path'
service = self.create_service(
'db',
build={'context': 'tests/fixtures/dockerfile-with-volume'},
volumes=[VolumeSpec(host_path, '/data', 'rw')])
old_container = create_and_start_container(service)
assert (
[mount['Destination'] for mount in old_container.get('Mounts')] ==
['/data']
)
service.options['volumes'] = []
with mock.patch('compose.service.log', autospec=True) as mock_log:
new_container, = service.execute_convergence_plan(
ConvergencePlan('recreate', [old_container]))
assert not mock_log.warn.called
assert (
[mount['Destination'] for mount in new_container.get('Mounts')],
['/data']
)
assert new_container.get_mount('/data')['Source'] != host_path
def test_execute_convergence_plan_without_start(self):
service = self.create_service(
'db',
@ -372,7 +420,6 @@ class ServiceTest(DockerClientTestCase):
create_and_start_container(db)
self.assertEqual(db.containers()[0].environment['FOO'], 'BAR')
@mark.skipif(True, reason="Engine returns error - needs investigating")
def test_start_container_creates_links(self):
db = self.create_service('db')
web = self.create_service('web', links=[(db, None)])
@ -389,7 +436,6 @@ class ServiceTest(DockerClientTestCase):
'db'])
)
@mark.skipif(True, reason="Engine returns error - needs investigating")
def test_start_container_creates_links_with_names(self):
db = self.create_service('db')
web = self.create_service('web', links=[(db, 'custom_link_name')])
@ -433,7 +479,6 @@ class ServiceTest(DockerClientTestCase):
c = create_and_start_container(db)
self.assertEqual(set(get_links(c)), set([]))
@mark.skipif(True, reason="Engine returns error - needs investigating")
def test_start_one_off_container_creates_links_to_its_own_service(self):
db = self.create_service('db')
@ -750,18 +795,23 @@ class ServiceTest(DockerClientTestCase):
for container in containers:
self.assertEqual(list(container.inspect()['HostConfig']['PortBindings'].keys()), ['8000/tcp'])
def test_scale_with_immediate_exit(self):
service = self.create_service('web', image='busybox', command='true')
service.scale(2)
assert len(service.containers(stopped=True)) == 2
def test_network_mode_none(self):
service = self.create_service('web', net=Net('none'))
service = self.create_service('web', network_mode=NetworkMode('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=Net('bridge'))
service = self.create_service('web', network_mode=NetworkMode('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=Net('host'))
service = self.create_service('web', network_mode=NetworkMode('host'))
container = create_and_start_container(service)
self.assertEqual(container.get('HostConfig.NetworkMode'), 'host')
@ -859,7 +909,7 @@ class ServiceTest(DockerClientTestCase):
'FILE_DEF': 'F1',
'FILE_DEF_EMPTY': '',
'ENV_DEF': 'E3',
'NO_DEF': ''
'NO_DEF': None
}.items():
self.assertEqual(env[k], v)

View file

@ -10,6 +10,8 @@ from pytest import skip
from .. import unittest
from compose.cli.docker_client import docker_client
from compose.config.config import resolve_environment
from compose.config.config import V1
from compose.config.config import V2_0
from compose.const import API_VERSIONS
from compose.const import LABEL_PROJECT
from compose.progress_stream import stream_output
@ -54,9 +56,9 @@ class DockerClientTestCase(unittest.TestCase):
@classmethod
def setUpClass(cls):
if engine_version_too_low_for_v2():
version = API_VERSIONS[1]
version = API_VERSIONS[V1]
else:
version = API_VERSIONS[2]
version = API_VERSIONS[V2_0]
cls.client = docker_client(version)

View file

@ -6,12 +6,9 @@ import logging
from compose import container
from compose.cli.errors import UserError
from compose.cli.formatter import ConsoleWarningFormatter
from compose.cli.log_printer import LogPrinter
from compose.cli.main import attach_to_logs
from compose.cli.main import build_log_printer
from compose.cli.main import convergence_strategy_from_opts
from compose.cli.main import setup_console_handler
from compose.project import Project
from compose.service import ConvergenceStrategy
from tests import mock
from tests import unittest
@ -49,21 +46,6 @@ class CLIMainTestCase(unittest.TestCase):
log_printer = build_log_printer(containers, service_names, True, False)
self.assertEqual(log_printer.containers, containers)
def test_attach_to_logs(self):
project = mock.create_autospec(Project)
log_printer = mock.create_autospec(LogPrinter, containers=[])
service_names = ['web', 'db']
timeout = 12
with mock.patch('compose.cli.main.signals.signal', autospec=True) as mock_signal:
attach_to_logs(project, log_printer, service_names, timeout)
assert mock_signal.signal.mock_calls == [
mock.call(mock_signal.SIGINT, mock.ANY),
mock.call(mock_signal.SIGTERM, mock.ANY),
]
log_printer.run.assert_called_once_with()
class SetupConsoleHandlerTestCase(unittest.TestCase):

View file

@ -49,6 +49,13 @@ class CLITestCase(unittest.TestCase):
project_name = get_project_name(None)
self.assertEquals(project_name, name)
def test_project_name_with_empty_environment_var(self):
base_dir = 'tests/fixtures/simple-composefile'
with mock.patch.dict(os.environ):
os.environ['COMPOSE_PROJECT_NAME'] = ''
project_name = get_project_name(base_dir)
self.assertEquals('simplecomposefile', project_name)
def test_get_project(self):
base_dir = 'tests/fixtures/longer-filename-composefile'
project = get_project(base_dir)
@ -72,8 +79,40 @@ class CLITestCase(unittest.TestCase):
TopLevelCommand().dispatch(['help', 'nonexistent'], None)
@pytest.mark.xfail(IS_WINDOWS_PLATFORM, reason="requires dockerpty")
@mock.patch('compose.cli.main.dockerpty', autospec=True)
def test_run_with_environment_merged_with_options_list(self, mock_dockerpty):
@mock.patch('compose.cli.main.RunOperation', autospec=True)
@mock.patch('compose.cli.main.PseudoTerminal', autospec=True)
def test_run_interactive_passes_logs_false(self, mock_pseudo_terminal, mock_run_operation):
command = TopLevelCommand()
mock_client = mock.create_autospec(docker.Client)
mock_project = mock.Mock(client=mock_client)
mock_project.get_service.return_value = Service(
'service',
client=mock_client,
environment=['FOO=ONE', 'BAR=TWO'],
image='someimage')
with pytest.raises(SystemExit):
command.run(mock_project, {
'SERVICE': 'service',
'COMMAND': None,
'-e': ['BAR=NEW', 'OTHER=bär'.encode('utf-8')],
'--user': None,
'--no-deps': None,
'-d': False,
'-T': None,
'--entrypoint': None,
'--service-ports': None,
'--publish': [],
'--rm': None,
'--name': None,
})
_, _, call_kwargs = mock_run_operation.mock_calls[0]
assert call_kwargs['logs'] is False
@pytest.mark.xfail(IS_WINDOWS_PLATFORM, reason="requires dockerpty")
@mock.patch('compose.cli.main.PseudoTerminal', autospec=True)
def test_run_with_environment_merged_with_options_list(self, mock_pseudo_terminal):
command = TopLevelCommand()
mock_client = mock.create_autospec(docker.Client)
mock_project = mock.Mock(client=mock_client)
@ -99,9 +138,10 @@ class CLITestCase(unittest.TestCase):
})
_, _, call_kwargs = mock_client.create_container.mock_calls[0]
self.assertEqual(
call_kwargs['environment'],
{'FOO': 'ONE', 'BAR': 'NEW', 'OTHER': u'bär'})
assert (
sorted(call_kwargs['environment']) ==
sorted(['FOO=ONE', 'BAR=NEW', 'OTHER=bär'])
)
def test_run_service_with_restart_always(self):
command = TopLevelCommand()

File diff suppressed because it is too large Load diff

View file

@ -1,13 +1,14 @@
from __future__ import absolute_import
from __future__ import unicode_literals
import pytest
from compose.config.errors import DependencyError
from compose.config.sort_services import sort_service_dicts
from compose.config.types import VolumeFromSpec
from tests import unittest
class SortServiceTest(unittest.TestCase):
class TestSortService(object):
def test_sort_service_dicts_1(self):
services = [
{
@ -23,10 +24,10 @@ class SortServiceTest(unittest.TestCase):
]
sorted_services = sort_service_dicts(services)
self.assertEqual(len(sorted_services), 3)
self.assertEqual(sorted_services[0]['name'], 'grunt')
self.assertEqual(sorted_services[1]['name'], 'redis')
self.assertEqual(sorted_services[2]['name'], 'web')
assert len(sorted_services) == 3
assert sorted_services[0]['name'] == 'grunt'
assert sorted_services[1]['name'] == 'redis'
assert sorted_services[2]['name'] == 'web'
def test_sort_service_dicts_2(self):
services = [
@ -44,10 +45,10 @@ class SortServiceTest(unittest.TestCase):
]
sorted_services = sort_service_dicts(services)
self.assertEqual(len(sorted_services), 3)
self.assertEqual(sorted_services[0]['name'], 'redis')
self.assertEqual(sorted_services[1]['name'], 'postgres')
self.assertEqual(sorted_services[2]['name'], 'web')
assert len(sorted_services) == 3
assert sorted_services[0]['name'] == 'redis'
assert sorted_services[1]['name'] == 'postgres'
assert sorted_services[2]['name'] == 'web'
def test_sort_service_dicts_3(self):
services = [
@ -65,10 +66,10 @@ class SortServiceTest(unittest.TestCase):
]
sorted_services = sort_service_dicts(services)
self.assertEqual(len(sorted_services), 3)
self.assertEqual(sorted_services[0]['name'], 'child')
self.assertEqual(sorted_services[1]['name'], 'parent')
self.assertEqual(sorted_services[2]['name'], 'grandparent')
assert len(sorted_services) == 3
assert sorted_services[0]['name'] == 'child'
assert sorted_services[1]['name'] == 'parent'
assert sorted_services[2]['name'] == 'grandparent'
def test_sort_service_dicts_4(self):
services = [
@ -86,10 +87,10 @@ class SortServiceTest(unittest.TestCase):
]
sorted_services = sort_service_dicts(services)
self.assertEqual(len(sorted_services), 3)
self.assertEqual(sorted_services[0]['name'], 'child')
self.assertEqual(sorted_services[1]['name'], 'parent')
self.assertEqual(sorted_services[2]['name'], 'grandparent')
assert len(sorted_services) == 3
assert sorted_services[0]['name'] == 'child'
assert sorted_services[1]['name'] == 'parent'
assert sorted_services[2]['name'] == 'grandparent'
def test_sort_service_dicts_5(self):
services = [
@ -99,7 +100,7 @@ class SortServiceTest(unittest.TestCase):
},
{
'name': 'parent',
'net': 'container:child'
'network_mode': 'service:child'
},
{
'name': 'child'
@ -107,10 +108,10 @@ class SortServiceTest(unittest.TestCase):
]
sorted_services = sort_service_dicts(services)
self.assertEqual(len(sorted_services), 3)
self.assertEqual(sorted_services[0]['name'], 'child')
self.assertEqual(sorted_services[1]['name'], 'parent')
self.assertEqual(sorted_services[2]['name'], 'grandparent')
assert len(sorted_services) == 3
assert sorted_services[0]['name'] == 'child'
assert sorted_services[1]['name'] == 'parent'
assert sorted_services[2]['name'] == 'grandparent'
def test_sort_service_dicts_6(self):
services = [
@ -128,15 +129,15 @@ class SortServiceTest(unittest.TestCase):
]
sorted_services = sort_service_dicts(services)
self.assertEqual(len(sorted_services), 3)
self.assertEqual(sorted_services[0]['name'], 'child')
self.assertEqual(sorted_services[1]['name'], 'parent')
self.assertEqual(sorted_services[2]['name'], 'grandparent')
assert len(sorted_services) == 3
assert sorted_services[0]['name'] == 'child'
assert sorted_services[1]['name'] == 'parent'
assert sorted_services[2]['name'] == 'grandparent'
def test_sort_service_dicts_7(self):
services = [
{
'net': 'container:three',
'network_mode': 'service:three',
'name': 'four'
},
{
@ -153,11 +154,11 @@ class SortServiceTest(unittest.TestCase):
]
sorted_services = sort_service_dicts(services)
self.assertEqual(len(sorted_services), 4)
self.assertEqual(sorted_services[0]['name'], 'one')
self.assertEqual(sorted_services[1]['name'], 'two')
self.assertEqual(sorted_services[2]['name'], 'three')
self.assertEqual(sorted_services[3]['name'], 'four')
assert len(sorted_services) == 4
assert sorted_services[0]['name'] == 'one'
assert sorted_services[1]['name'] == 'two'
assert sorted_services[2]['name'] == 'three'
assert sorted_services[3]['name'] == 'four'
def test_sort_service_dicts_circular_imports(self):
services = [
@ -171,13 +172,10 @@ class SortServiceTest(unittest.TestCase):
},
]
try:
with pytest.raises(DependencyError) as exc:
sort_service_dicts(services)
except DependencyError as e:
self.assertIn('redis', e.msg)
self.assertIn('web', e.msg)
else:
self.fail('Should have thrown an DependencyError')
assert 'redis' in exc.exconly()
assert 'web' in exc.exconly()
def test_sort_service_dicts_circular_imports_2(self):
services = [
@ -194,13 +192,10 @@ class SortServiceTest(unittest.TestCase):
}
]
try:
with pytest.raises(DependencyError) as exc:
sort_service_dicts(services)
except DependencyError as e:
self.assertIn('redis', e.msg)
self.assertIn('web', e.msg)
else:
self.fail('Should have thrown an DependencyError')
assert 'redis' in exc.exconly()
assert 'web' in exc.exconly()
def test_sort_service_dicts_circular_imports_3(self):
services = [
@ -218,13 +213,10 @@ class SortServiceTest(unittest.TestCase):
}
]
try:
with pytest.raises(DependencyError) as exc:
sort_service_dicts(services)
except DependencyError as e:
self.assertIn('a', e.msg)
self.assertIn('b', e.msg)
else:
self.fail('Should have thrown an DependencyError')
assert 'a' in exc.exconly()
assert 'b' in exc.exconly()
def test_sort_service_dicts_self_imports(self):
services = [
@ -234,9 +226,18 @@ class SortServiceTest(unittest.TestCase):
},
]
try:
with pytest.raises(DependencyError) as exc:
sort_service_dicts(services)
except DependencyError as e:
self.assertIn('web', e.msg)
else:
self.fail('Should have thrown an DependencyError')
assert 'web' in exc.exconly()
def test_sort_service_dicts_depends_on_self(self):
services = [
{
'depends_on': ['web'],
'name': 'web'
},
]
with pytest.raises(DependencyError) as exc:
sort_service_dicts(services)
assert 'A service can not depend on itself: web' in exc.exconly()

View file

@ -3,13 +3,13 @@ from __future__ import unicode_literals
import pytest
from compose.config.config import V1
from compose.config.config import V2_0
from compose.config.errors import ConfigurationError
from compose.config.types import parse_extra_hosts
from compose.config.types import VolumeFromSpec
from compose.config.types import VolumeSpec
from compose.const import IS_WINDOWS_PLATFORM
from tests.unit.config.config_test import V1
from tests.unit.config.config_test import V2
def test_parse_extra_hosts_list():
@ -91,26 +91,26 @@ class TestVolumesFromSpec(object):
VolumeFromSpec.parse('unknown:format:ro', self.services, V1)
def test_parse_v2_from_service(self):
volume_from = VolumeFromSpec.parse('servicea', self.services, V2)
volume_from = VolumeFromSpec.parse('servicea', self.services, V2_0)
assert volume_from == VolumeFromSpec('servicea', 'rw', 'service')
def test_parse_v2_from_service_with_mode(self):
volume_from = VolumeFromSpec.parse('servicea:ro', self.services, V2)
volume_from = VolumeFromSpec.parse('servicea:ro', self.services, V2_0)
assert volume_from == VolumeFromSpec('servicea', 'ro', 'service')
def test_parse_v2_from_container(self):
volume_from = VolumeFromSpec.parse('container:foo', self.services, V2)
volume_from = VolumeFromSpec.parse('container:foo', self.services, V2_0)
assert volume_from == VolumeFromSpec('foo', 'rw', 'container')
def test_parse_v2_from_container_with_mode(self):
volume_from = VolumeFromSpec.parse('container:foo:ro', self.services, V2)
volume_from = VolumeFromSpec.parse('container:foo:ro', self.services, V2_0)
assert volume_from == VolumeFromSpec('foo', 'ro', 'container')
def test_parse_v2_invalid_type(self):
with pytest.raises(ConfigurationError) as exc:
VolumeFromSpec.parse('bogus:foo:ro', self.services, V2)
VolumeFromSpec.parse('bogus:foo:ro', self.services, V2_0)
assert "Unknown volumes_from type 'bogus'" in exc.exconly()
def test_parse_v2_invalid(self):
with pytest.raises(ConfigurationError):
VolumeFromSpec.parse('unknown:format:ro', self.services, V2)
VolumeFromSpec.parse('unknown:format:ro', self.services, V2_0)

View file

@ -12,8 +12,9 @@ from compose.container import get_container_name
class ContainerTest(unittest.TestCase):
def setUp(self):
self.container_id = "abcabcabcbabc12345"
self.container_dict = {
"Id": "abc",
"Id": self.container_id,
"Image": "busybox:latest",
"Command": "top",
"Created": 1387384730,
@ -41,19 +42,22 @@ class ContainerTest(unittest.TestCase):
self.assertEqual(
container.dictionary,
{
"Id": "abc",
"Id": self.container_id,
"Image": "busybox:latest",
"Name": "/composetest_db_1",
})
def test_from_ps_prefixed(self):
self.container_dict['Names'] = ['/swarm-host-1' + n for n in self.container_dict['Names']]
self.container_dict['Names'] = [
'/swarm-host-1' + n for n in self.container_dict['Names']
]
container = Container.from_ps(None,
self.container_dict,
has_been_inspected=True)
container = Container.from_ps(
None,
self.container_dict,
has_been_inspected=True)
self.assertEqual(container.dictionary, {
"Id": "abc",
"Id": self.container_id,
"Image": "busybox:latest",
"Name": "/composetest_db_1",
})
@ -142,6 +146,10 @@ class ContainerTest(unittest.TestCase):
self.assertEqual(container.get('HostConfig.VolumesFrom'), ["volume_id"])
self.assertEqual(container.get('Foo.Bar.DoesNotExist'), None)
def test_short_id(self):
container = Container(None, self.container_dict, has_been_inspected=True)
assert container.short_id == self.container_id[:12]
class GetContainerNameTestCase(unittest.TestCase):

View file

@ -45,7 +45,7 @@ class ProjectTest(unittest.TestCase):
self.assertEqual(project.get_service('web').options['image'], 'busybox:latest')
self.assertEqual(project.get_service('db').name, 'db')
self.assertEqual(project.get_service('db').options['image'], 'busybox:latest')
self.assertFalse(project.use_networking)
self.assertFalse(project.networks.use_networking)
def test_from_config_v2(self):
config = Config(
@ -65,7 +65,7 @@ class ProjectTest(unittest.TestCase):
)
project = Project.from_config('composetest', config, None)
self.assertEqual(len(project.services), 2)
self.assertTrue(project.use_networking)
self.assertTrue(project.networks.use_networking)
def test_get_service(self):
web = Service(
@ -349,7 +349,7 @@ class ProjectTest(unittest.TestCase):
),
)
service = project.get_service('test')
self.assertEqual(service.net.id, None)
self.assertEqual(service.network_mode.id, None)
self.assertNotIn('NetworkMode', service._get_container_host_config({}))
def test_use_net_from_container(self):
@ -365,7 +365,7 @@ class ProjectTest(unittest.TestCase):
{
'name': 'test',
'image': 'busybox:latest',
'net': 'container:aaa'
'network_mode': 'container:aaa'
},
],
networks=None,
@ -373,7 +373,7 @@ class ProjectTest(unittest.TestCase):
),
)
service = project.get_service('test')
self.assertEqual(service.net.mode, 'container:' + container_id)
self.assertEqual(service.network_mode.mode, 'container:' + container_id)
def test_use_net_from_service(self):
container_name = 'test_aaa_1'
@ -398,7 +398,7 @@ class ProjectTest(unittest.TestCase):
{
'name': 'test',
'image': 'busybox:latest',
'net': 'container:aaa'
'network_mode': 'service:aaa'
},
],
networks=None,
@ -407,7 +407,7 @@ class ProjectTest(unittest.TestCase):
)
service = project.get_service('test')
self.assertEqual(service.net.mode, 'container:' + container_name)
self.assertEqual(service.network_mode.mode, 'container:' + container_name)
def test_uses_default_network_true(self):
project = Project.from_config(
@ -426,7 +426,7 @@ class ProjectTest(unittest.TestCase):
),
)
assert project.uses_default_network()
assert 'default' in project.networks.networks
def test_uses_default_network_false(self):
project = Project.from_config(
@ -438,7 +438,7 @@ class ProjectTest(unittest.TestCase):
{
'name': 'foo',
'image': 'busybox:latest',
'networks': ['custom']
'networks': {'custom': None}
},
],
networks={'custom': {}},
@ -446,7 +446,7 @@ class ProjectTest(unittest.TestCase):
),
)
assert not project.uses_default_network()
assert 'default' not in project.networks.networks
def test_container_without_name(self):
self.mock_client.containers.return_value = [

View file

@ -15,16 +15,16 @@ from compose.const import LABEL_SERVICE
from compose.container import Container
from compose.service import build_ulimits
from compose.service import build_volume_binding
from compose.service import ContainerNet
from compose.service import ContainerNetworkMode
from compose.service import get_container_data_volumes
from compose.service import ImageType
from compose.service import merge_volume_bindings
from compose.service import NeedsBuildError
from compose.service import Net
from compose.service import NetworkMode
from compose.service import NoSuchImageError
from compose.service import parse_repository_tag
from compose.service import Service
from compose.service import ServiceNet
from compose.service import ServiceNetworkMode
from compose.service import warn_on_masked_volume
@ -266,14 +266,53 @@ class ServiceTest(unittest.TestCase):
self.assertEqual(
opts['labels'][LABEL_CONFIG_HASH],
'3c85881a8903b9d73a06c41860c8be08acce1494ab4cf8408375966dccd714de')
self.assertEqual(
opts['environment'],
{
'affinity:container': '=ababab',
'also': 'real',
}
'f8bfa1058ad1f4231372a0b1639f0dfdb574dafff4e8d7938049ae993f7cf1fc')
assert opts['environment'] == ['also=real']
def test_get_container_create_options_sets_affinity_with_binds(self):
service = Service(
'foo',
image='foo',
client=self.mock_client,
)
self.mock_client.inspect_image.return_value = {'Id': 'abcd'}
prev_container = mock.Mock(
id='ababab',
image_config={'ContainerConfig': {'Volumes': ['/data']}})
def container_get(key):
return {
'Mounts': [
{
'Destination': '/data',
'Source': '/some/path',
'Name': 'abab1234',
},
]
}.get(key, None)
prev_container.get.side_effect = container_get
opts = service._get_container_create_options(
{},
1,
previous_container=prev_container)
assert opts['environment'] == ['affinity:container==ababab']
def test_get_container_create_options_no_affinity_without_binds(self):
service = Service('foo', image='foo', client=self.mock_client)
self.mock_client.inspect_image.return_value = {'Id': 'abcd'}
prev_container = mock.Mock(
id='ababab',
image_config={'ContainerConfig': {}})
prev_container.get.return_value = None
opts = service._get_container_create_options(
{},
1,
previous_container=prev_container)
assert opts['environment'] == []
def test_get_container_not_found(self):
self.mock_client.containers.return_value = []
@ -407,7 +446,7 @@ class ServiceTest(unittest.TestCase):
'foo',
image='example.com/foo',
client=self.mock_client,
net=ServiceNet(Service('other')),
network_mode=ServiceNetworkMode(Service('other')),
links=[(Service('one'), 'one')],
volumes_from=[VolumeFromSpec(Service('two'), 'rw', 'service')])
@ -417,11 +456,12 @@ class ServiceTest(unittest.TestCase):
'options': {'image': 'example.com/foo'},
'links': [('one', 'one')],
'net': 'other',
'networks': [],
'volumes_from': [('two', 'rw')],
}
self.assertEqual(config_dict, expected)
assert config_dict == expected
def test_config_dict_with_net_from_container(self):
def test_config_dict_with_network_mode_from_container(self):
self.mock_client.inspect_image.return_value = {'Id': 'abcd'}
container = Container(
self.mock_client,
@ -430,17 +470,18 @@ class ServiceTest(unittest.TestCase):
'foo',
image='example.com/foo',
client=self.mock_client,
net=container)
network_mode=ContainerNetworkMode(container))
config_dict = service.config_dict()
expected = {
'image_id': 'abcd',
'options': {'image': 'example.com/foo'},
'links': [],
'networks': [],
'net': 'aaabbb',
'volumes_from': [],
}
self.assertEqual(config_dict, expected)
assert config_dict == expected
def test_remove_image_none(self):
web = Service('web', image='example', client=self.mock_client)
@ -536,14 +577,6 @@ class ServiceTest(unittest.TestCase):
ports=["127.0.0.1:1000-2000:2000-3000"])
self.assertEqual(service.specifies_host_port(), True)
def test_get_links_with_networking(self):
service = Service(
'foo',
image='foo',
links=[(Service('one'), 'one')],
use_networking=True)
self.assertEqual(service._get_links(link_to_self=True), [])
def test_image_name_from_config(self):
image_name = 'example/web:latest'
service = Service('foo', image=image_name)
@ -597,20 +630,20 @@ class BuildUlimitsTestCase(unittest.TestCase):
class NetTestCase(unittest.TestCase):
def test_net(self):
net = Net('host')
self.assertEqual(net.id, 'host')
self.assertEqual(net.mode, 'host')
self.assertEqual(net.service_name, None)
def test_network_mode(self):
network_mode = NetworkMode('host')
self.assertEqual(network_mode.id, 'host')
self.assertEqual(network_mode.mode, 'host')
self.assertEqual(network_mode.service_name, None)
def test_net_container(self):
def test_network_mode_container(self):
container_id = 'abcd'
net = ContainerNet(Container(None, {'Id': container_id}))
self.assertEqual(net.id, container_id)
self.assertEqual(net.mode, 'container:' + container_id)
self.assertEqual(net.service_name, None)
network_mode = ContainerNetworkMode(Container(None, {'Id': container_id}))
self.assertEqual(network_mode.id, container_id)
self.assertEqual(network_mode.mode, 'container:' + container_id)
self.assertEqual(network_mode.service_name, None)
def test_net_service(self):
def test_network_mode_service(self):
container_id = 'bbbb'
service_name = 'web'
mock_client = mock.create_autospec(docker.Client)
@ -619,23 +652,23 @@ class NetTestCase(unittest.TestCase):
]
service = Service(name=service_name, client=mock_client)
net = ServiceNet(service)
network_mode = ServiceNetworkMode(service)
self.assertEqual(net.id, service_name)
self.assertEqual(net.mode, 'container:' + container_id)
self.assertEqual(net.service_name, service_name)
self.assertEqual(network_mode.id, service_name)
self.assertEqual(network_mode.mode, 'container:' + container_id)
self.assertEqual(network_mode.service_name, service_name)
def test_net_service_no_containers(self):
def test_network_mode_service_no_containers(self):
service_name = 'web'
mock_client = mock.create_autospec(docker.Client)
mock_client.containers.return_value = []
service = Service(name=service_name, client=mock_client)
net = ServiceNet(service)
network_mode = ServiceNetworkMode(service)
self.assertEqual(net.id, service_name)
self.assertEqual(net.mode, None)
self.assertEqual(net.service_name, service_name)
self.assertEqual(network_mode.id, service_name)
self.assertEqual(network_mode.mode, None)
self.assertEqual(network_mode.service_name, service_name)
def build_mount(destination, source, mode='rw'):
@ -656,6 +689,7 @@ class ServiceVolumesTest(unittest.TestCase):
'/host/volume:/host/volume:ro',
'/new/volume',
'/existing/volume',
'named:/named/vol',
]]
self.mock_client.inspect_image.return_value = {
@ -697,8 +731,8 @@ class ServiceVolumesTest(unittest.TestCase):
}, has_been_inspected=True)
expected = [
VolumeSpec.parse('/var/lib/docker/aaaaaaaa:/existing/volume:rw'),
VolumeSpec.parse('/var/lib/docker/cccccccc:/mnt/image/data:rw'),
VolumeSpec.parse('existingvolume:/existing/volume:rw'),
VolumeSpec.parse('imagedata:/mnt/image/data:rw'),
]
volumes = get_container_data_volumes(container, options)
@ -716,7 +750,8 @@ class ServiceVolumesTest(unittest.TestCase):
'ContainerConfig': {'Volumes': {}}
}
intermediate_container = Container(self.mock_client, {
previous_container = Container(self.mock_client, {
'Id': 'cdefab',
'Image': 'ababab',
'Mounts': [{
'Source': '/var/lib/docker/aaaaaaaa',
@ -730,11 +765,12 @@ class ServiceVolumesTest(unittest.TestCase):
expected = [
'/host/volume:/host/volume:ro',
'/host/rw/volume:/host/rw/volume:rw',
'/var/lib/docker/aaaaaaaa:/existing/volume:rw',
'existingvolume:/existing/volume:rw',
]
binds = merge_volume_bindings(options, intermediate_container)
self.assertEqual(set(binds), set(expected))
binds, affinity = merge_volume_bindings(options, previous_container)
assert sorted(binds) == sorted(expected)
assert affinity == {'affinity:container': '=cdefab'}
def test_mount_same_host_path_to_two_volumes(self):
service = Service(
@ -767,13 +803,14 @@ class ServiceVolumesTest(unittest.TestCase):
]),
)
def test_different_host_path_in_container_json(self):
def test_get_container_create_options_with_different_host_path_in_container_json(self):
service = Service(
'web',
image='busybox',
volumes=[VolumeSpec.parse('/host/path:/data')],
client=self.mock_client,
)
volume_name = 'abcdefff1234'
self.mock_client.inspect_image.return_value = {
'Id': 'ababab',
@ -794,7 +831,7 @@ class ServiceVolumesTest(unittest.TestCase):
'Mode': '',
'RW': True,
'Driver': 'local',
'Name': 'abcdefff1234'
'Name': volume_name,
},
]
}
@ -805,9 +842,9 @@ class ServiceVolumesTest(unittest.TestCase):
previous_container=Container(self.mock_client, {'Id': '123123123'}),
)
self.assertEqual(
self.mock_client.create_host_config.call_args[1]['binds'],
['/mnt/sda1/host/path:/data:rw'],
assert (
self.mock_client.create_host_config.call_args[1]['binds'] ==
['{}:/data:rw'.format(volume_name)]
)
def test_warn_on_masked_volume_no_warning_when_no_container_volumes(self):