Compare commits

...

473 Commits
1.0.0 ... 1.3.1

Author SHA1 Message Date
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
Aanand Prasad
1344099e29 Merge pull request #1444 from aanand/migrate-in-dependency-order
Migrate containers in dependency order
2015-05-26 17:30:14 +01:00
Daniel Nephin
48f3d41947 Merge pull request #1447 from aanand/fix-convergence-when-service-not-created
Fix regression in `docker-compose up`
2015-05-26 10:54:28 -05:00
Aanand Prasad
7da8e6be3b Migrate containers in dependency order
This fixes a bug where migration would fail with an error if a
downstream container was migrated before its upstream dependencies, due
to `check_for_legacy_containers()` being implicitly called when we fetch
`links`, `volumes_from` or `net` dependencies.

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

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

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

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

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

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

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

- Move `check_for_legacy_containers` into `legacy` module

- Fix migration test so it can be run in isolation

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

e5d8447f06 (commitcomment-11243708)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

- Added a section about building.

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

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

Signed-off-by: funkyfuture <funkyfuture@riseup.net>
2015-03-26 13:11:05 +01:00
akoskaaa
fa2fb6bd38 [pep8] flake8 run for everything, fix items from this change
Signed-off-by: akoskaaa <akos.hochrein@prezi.com>
2015-03-25 23:15:34 -07:00
akoskaaa
f9ea5ecf40 [pep8] make test files and config files pep8 valid
Signed-off-by: akoskaaa <akos.hochrein@prezi.com>
2015-03-25 20:20:38 -07:00
Aanand Prasad
99f7eba930 Add Docker 1.6 RC2 to tested versions
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-03-25 14:48:39 -07:00
Ben Firshman
e1b27acd02 Add contributing section to readme
Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2015-03-25 17:22:04 +01:00
Ben Firshman
9aab7bc242 Merge pull request #1172 from aanand/fix-dns
Fix regression in Dns and DnsSearch settings
2015-03-25 16:11:13 +00:00
Aanand Prasad
83dcceacaf Fix regression in Dns and DnsSearch settings
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-03-24 16:17:05 -07:00
Aanand Prasad
1f06070f12 Merge pull request #1160 from aanand/fix-volume-merging
Fix volume merging
2015-03-24 13:40:07 -07:00
Aanand Prasad
35c6e0314c Fix volume merging when there's no explicit host path
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-03-24 13:26:36 -07:00
Aanand Prasad
276e43ca6b Fix service dict merging when only one dict has a volumes key
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-03-24 13:26:36 -07:00
Aanand Prasad
b7046777d1 Merge pull request #1161 from aanand/revert-labels-and-dependencies
Revert labels and dependencies
2015-03-24 13:05:46 -07:00
Aanand Prasad
1c14fc06da Update docker-py and requests version ranges
Leave the pinned versions in requirements.txt alone, as there's an
incompatibility between PyInstaller and requests 2.5.2 and 2.5.3, and by
extension with docker-py 1.1.0.

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-03-23 15:04:18 -07:00
Aanand Prasad
f4ef2c09d6 Revert "Remove restriction for requests version, update docker-py requirement"
This reverts commit 81a32a266f.

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-03-23 15:04:15 -07:00
Aanand Prasad
16495c577b Revert "Use dev version of Docker"
This reverts commit d209ded13c.

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-03-23 15:04:10 -07:00
Aanand Prasad
965426e39b Revert "Add 'labels:' config option"
This reverts commit 721327110d.

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-03-23 15:04:04 -07:00
Aanand Prasad
02f119e4b7 Merge pull request #1145 from aanand/labels
Implement `labels` option
2015-03-20 18:53:26 -07:00
Aanand Prasad
721327110d Add 'labels:' config option
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-03-20 18:12:04 -07:00
Aanand Prasad
d209ded13c Use dev version of Docker
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-03-20 18:12:04 -07:00
Aanand Prasad
49c2080bcd Merge pull request #1088 from aanand/extends
`extends` – Inherit services from other services
2015-03-20 16:00:50 -07:00
Aanand Prasad
37efdb1f8b Make volume host paths relative to file, merge volumes when extending
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-03-20 15:45:16 -07:00
Aanand Prasad
4c582e4352 Implement extends
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-03-20 15:45:16 -07:00
Aanand Prasad
5dca6c232e Merge pull request #972 from aanand/set-host-config-at-create-time
Make Swarm schedule containers on the same node when they are linked together
2015-03-20 15:43:52 -07:00
Aanand Prasad
eef4bc3917 Specify all HostConfig at create time
This is required for Swarm integration: the cluster needs to know
about config like `links` and `volumes_from` at create time so that it
can co-schedule containers.

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-03-20 15:14:07 -07:00
Ben Firshman
9931cd2c4c Merge pull request #1140 from jfrazelle/add-to-readme
Add build status \o/
2015-03-20 01:03:37 +00:00
Jessica Frazelle
0226675766 Add build status \o/
Signed-off-by: Jessica Frazelle <jess@docker.com>
2015-03-19 17:50:15 -07:00
Ben Firshman
ae9c965823 Merge pull request #1128 from aanand/jenkins-dco
Validate DCO in script/test-versions
2015-03-18 14:49:40 +00:00
Daniel Nephin
d7a90092c2 Merge pull request #1118 from funkyfuture/issue-1082
Change port in ports-composefile to 49152
2015-03-17 21:39:34 -04:00
Aanand Prasad
85fb8956f3 Validate DCO in script/test-versions
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-03-17 18:02:22 -07:00
Ben Firshman
1e5b9dc2eb Merge pull request #1005 from aanand/docker-in-docker
Run tests using Docker-in-Docker so we can test multiple versions
2015-03-17 15:13:59 +00:00
Ben Firshman
d21b8f1ba3 Merge pull request #1112 from bfirsh/remove-wercker-from-readme
Remove wercker status from readme
2015-03-16 23:53:18 +00:00
funkyfuture
4c5a80f253 Change port in ports-composefile to 49152
This shall lower the propability to interfere with another service (e.g. the
WebUI of an application) that is running on the machine where tests are run.

Signed-off-by: funkyfuture <funkyfuture@riseup.net>
2015-03-17 00:21:29 +01:00
Ben Firshman
198598c936 Remove wercker status from readme
Because it doesn't really work anymore and looks scary when it's broken.

Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2015-03-13 19:36:54 +00:00
Ben Firshman
8b5a882459 Merge pull request #867 from knutwalker/feature/timeout-flag
Add timeout flag to stop, restart, and up
2015-03-13 17:41:16 +00:00
Ben Firshman
dae451019b Merge pull request #1099 from aanand/fix-env-file-resolution
Fix env file resolution
2015-03-13 16:49:10 +00:00
Ben Firshman
dfc729b8f2 Merge pull request #1108 from aanand/fix-deps-tests
Fix Project.up() tests
2015-03-13 16:38:25 +00:00
Aanand Prasad
eb7ea76d6c Merge pull request #1100 from vmalloc/patch-3
Remove restriction for requests version, update docker-py requirement
2015-03-13 16:22:40 +00:00
Aanand Prasad
3c8ef6a94c Fix Project.up() tests
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-03-13 14:51:26 +00:00
Ben Firshman
081afd9bbf Merge pull request #1097 from aanand/script-shell
Add script/shell
2015-03-13 14:43:47 +00:00
Aanand Prasad
528bed9ef6 Fix environment resolution
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-03-13 14:22:28 +00:00
Aanand Prasad
4ecf5e01ff Extract YAML loading and parsing into config module
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-03-13 14:21:58 +00:00
Rotem Yaari
81a32a266f Remove restriction for requests version, update docker-py requirement 2015-03-13 14:39:11 +02:00
Aanand Prasad
25c70c2af4 Merge pull request #1076 from gilclark/master
Make volumes_from and net containers first class dependencies
2015-03-13 11:46:13 +00:00
Aanand Prasad
7b1f01bb52 Add script/shell
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-03-12 14:02:14 +00:00
Daniel Nephin
c23189a5fa Merge pull request #971 from IanVS/master
Provide user override option on command line
2015-03-11 10:16:43 -04:00
Ian VanSchooten
86b723e227 Provide user override option on command line
Allows overriding a user on the command line from the one specified in
the docker-compose.yml

The added tests verify that a specified user overrides a default
user in the docker-compose.yml file.

Based on commit f2f01e207b by @chmouel

Signed-off-by: Ian VanSchooten <ian@badgelabsllc.com>
2015-03-11 00:11:31 -04:00
Paul Horn
2534a0964f Add timeout flag to stop, restart, and up
The commands `stop`, `restart`, and `up` now support a flag `--timeout`.
It represents the number of seconds to give the services to comply to
the command. In case of `up`, this is only relevant if running in
attached mode.

Signed-off-by: Paul Horn <knutwalker@gmail.com>
2015-03-10 12:21:58 +01:00
Aanand Prasad
42e6296b0e Kick everything off from a single container
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-03-09 17:53:54 +00:00
Aanand Prasad
74440b2f92 Run tests using Docker-in-Docker so we can test multiple versions
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-03-09 17:53:54 +00:00
Gil Clark
95f4e2c7c3 Make volumes_from and net containers first class dependencies and
assure that starting order is correct.  Added supporting unit and
integration tests as well.

Signed-off-by: Gil Clark <gilclark1@gmail.com>
2015-03-06 13:30:56 -08:00
Ben Firshman
9e9a66f0f8 Merge pull request #965 from albers/bash-completion
Favour yml and yaml extensions in bash completion for -f
2015-03-06 15:20:12 +00:00
Ben Firshman
300234429c Merge pull request #1034 from bfirsh/remove-d11wtq-as-maintainer
Remove @d11wtq as a maintainer
2015-03-05 11:14:50 +00:00
Ben Firshman
3384bc5fcf Merge pull request #1059 from bfirsh/fix-missing-space-in-docs
Fix missing space in rails docs
2015-03-05 11:14:37 +00:00
Ben Firshman
08f936b2e7 Fix missing space in rails docs
From #1031

Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2015-03-04 10:27:25 +00:00
Ben Firshman
df925bc759 Merge pull request #1031 from fredlf/edit-qs-guides
Edits and revisions to Compose Quickstart guides
2015-03-04 10:26:32 +00:00
Aanand Prasad
33c7b3f752 Merge pull request #1052 from albers/bash-completion-rm-f
Add -f flag to bash completion for docker-compose rm
2015-03-02 18:15:12 +00:00
Harald Albers
ac7a97f420 Add -f flag to bash completion for docker-compose rm
Signed-off-by: Harald Albers <github@albersweb.de>
2015-03-02 18:33:34 +01:00
Aanand Prasad
d5fcb0de67 Merge pull request #970 from dnephin/fix_environment_as_list_bug
Fix merging command line environment with config environment
2015-03-02 15:28:05 +00:00
Daniel Nephin
f47431d591 Resolves #927 - fix merging command line environment with a list in the config
Signed-off-by: Daniel Nephin <dnephin@gmail.com>
2015-03-02 10:23:43 -05:00
Aanand Prasad
8610adcaf3 Merge pull request #997 from abesto/use-latest-docker-py
Use docker-py 1.0.0
2015-03-02 14:15:18 +00:00
Zoltan Nagy
8f38b28816 Use docker-py 1.0.0
Signed-off-by: Zoltan Nagy <abesto@abesto.net>
2015-03-02 13:11:02 +01:00
Aanand Prasad
db3b4731cd Merge pull request #1044 from usrenmae/py3-compat
Move several steps closer to python3 compatibility
2015-03-02 12:08:39 +00:00
Aanand Prasad
30b7c22c71 Merge pull request #1043 from alunduil/add-bashcomp-to-sdist
add bash completion to sdist
2015-03-02 12:05:09 +00:00
Aanand Prasad
f89f9e3190 Merge pull request #1007 from KingsleyKelly/force
Add -f flag as option on rm.
2015-03-02 11:26:28 +00:00
Igor Ch
f1fc1d7a16 Move several steps closer to python3 compatibility
Signed-off-by: Igor Ch <usrenmae@gmail.com>
2015-02-28 23:26:20 +02:00
Alex Brandt
0e30d0085b add bash completion to sdist
When installing from the source distribution (for packaging or other
purposes), it's convenient to be able to install the bash completion
file as part of that process.
2015-02-28 15:18:29 -06:00
Ben Firshman
98f32a21e7 Remove @d11wtq as a maintainer
Thanks for all the help!

Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2015-02-27 11:18:41 +00:00
KingsleyKelly
fa195bc829 Add -f flag as option on rm.
Signed-off-by: Kingsley Kelly <kingsley.kelly@gmail.com>
2015-02-27 07:24:43 +00:00
Fred Lifton
882dc673ce Edits and revisions to Compose Quickstart guides 2015-02-26 18:58:06 -08:00
Ben Firshman
44f5a3ea46 Merge pull request #1028 from aanand/update-readme
Update README with new docs URL and IRC channel
2015-02-26 14:11:37 +00:00
Ben Firshman
ac4e800763 Merge pull request #1009 from aanand/update-docker-install-instructions
Point at official Docker install instructions rather than repeating them
2015-02-25 19:11:08 +00:00
Ben Firshman
2e57575a61 Merge pull request #1018 from aanand/link-to-getting-started-guides
Build and link to getting started guides
2015-02-25 19:07:57 +00:00
Ben Firshman
03535a6158 Merge pull request #1027 from aanand/ship-1.1.0
Ship 1.1.0
2015-02-25 18:42:28 +00:00
Aanand Prasad
c41342501b Update README with new docs URL and IRC channel
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-02-25 18:25:15 +00:00
Aanand Prasad
4ac02bfca6 Ship 1.1.0
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-02-25 18:16:44 +00:00
Aanand Prasad
35d5d1a5b1 Build and link to getting started guides
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-02-25 17:54:02 +00:00
Aanand Prasad
72003de737 Merge pull request #1020 from bfirsh/move-docs-index-higher-up-on-compose-docs
Move docs index higher up on index page
2015-02-25 17:52:56 +00:00
Ben Firshman
a0db5bee3e Merge pull request #1022 from aanand/previously-known-as
Add 'previously known as Fig' note to README.md
2015-02-25 17:52:04 +00:00
Ben Firshman
59c45e3398 Merge pull request #1026 from aanand/fix-requests-version-range
Fix requests version range
2015-02-25 17:51:39 +00:00
Aanand Prasad
ea8364fd11 Add 'previously known as Fig' note to README.md
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-02-25 17:39:12 +00:00
Aanand Prasad
cf6b09e94b Fix requests version range
It was more permissive than docker-py's, resulting in an incompatible
version (2.5.x) being installed.

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-02-25 17:34:22 +00:00
Ben Firshman
66555aa69b Merge pull request #1021 from aanand/update-urls
Update URLs in documentation
2015-02-25 16:48:28 +00:00
Aanand Prasad
bb943d5cb5 Update URLs in documentation
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-02-25 14:58:03 +00:00
Ben Firshman
178c50d46f Move docs index higher up on index page
Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2015-02-25 14:09:30 +00:00
Ben Firshman
32cdabefc2 Merge pull request #1016 from SvenDowideit/docs-tweak
Add an index to the bottom of the Compose docs as they're scattered arou...
2015-02-25 13:55:47 +00:00
Sven Dowideit
5b07c581e0 Add an index to the bottom of the Compose docs as they're scattered around docs.docker.com
Signed-off-by: Sven Dowideit <SvenDowideit@home.org.au>
2015-02-25 23:38:25 +10:00
Ben Firshman
17bbb9d357 Merge pull request #1017 from aanand/swarm-doc
Document Swarm integration
2015-02-25 12:53:07 +00:00
Aanand Prasad
ec2966222a Document Swarm integration
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-02-25 10:45:40 +00:00
Aanand Prasad
8076c7d7fe Merge pull request #1010 from bfirsh/add-create-message-when-scaling-containers
Log "creating container" when scaling
2015-02-24 14:09:12 +00:00
Ben Firshman
b2425c1f1e Log "creating container" when scaling
If an image needs pulling, it just looks like it's hanging.

Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2015-02-24 13:39:38 +00:00
Aanand Prasad
34c6920b37 Point at official Docker install instructions rather than repeating them
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-02-24 11:43:14 +00:00
Ben Firshman
8d8dd37d2c Merge pull request #924 from aanand/update-contributing
Add checklist item for completion script URL to CONTRIBUTING.md
2015-02-24 10:44:16 +00:00
Ben Firshman
b1739e703b Merge pull request #1002 from SvenDowideit/add-links-to-references
Add links to the main Compose references
2015-02-24 10:43:34 +00:00
Ben Firshman
a42f2007ea Merge pull request #1001 from SvenDowideit/build-docs-in-local-fig-repo
add ./script/doc to build fig documentation using the docs.docker.com to...
2015-02-23 22:45:59 +00:00
Aanand Prasad
1dd5ef4133 Merge pull request #619 from bfirsh/add-script-which-runs-fig-inside-docker
Add script which runs Fig inside Docker
2015-02-23 18:20:33 +00:00
Sven Dowideit
c3215a1764 Add links to the main Compose references
Signed-off-by: Sven Dowideit <SvenDowideit@home.org.au>
2015-02-23 16:13:58 +10:00
Sven Dowideit
bd320b19fe add ./script/doc to build fig documentation using the docs.docker.com tooling
Signed-off-by: Sven Dowideit <SvenDowideit@home.org.au>
2015-02-23 13:56:13 +10:00
Ben Firshman
0fa0131372 Add script which runs Fig inside Docker
Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2015-02-20 14:33:15 +00:00
Aanand Prasad
a7fe67e691 Merge pull request #991 from aanand/credit-contributors
Credit contributors in CHANGES.md
2015-02-19 18:08:45 +00:00
Aanand Prasad
f78dfa7958 Credit contributors in CHANGES.md
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-02-19 17:42:33 +00:00
Aanand Prasad
d95de03de9 Merge pull request #990 from bfirsh/add-title-to-docs-index
Add title to docs index
2015-02-19 16:02:22 +00:00
Ben Firshman
e3eccd1047 Merge pull request #979 from aanand/fix-env-docs
Update environment variable names in docs
2015-02-19 14:34:48 +00:00
Ben Firshman
d32994c250 Add title to docs index
Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2015-02-19 14:34:08 +00:00
Aanand Prasad
a516d61b49 Update environment variable names in docs
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-02-18 11:39:57 +00:00
Aanand Prasad
6eb1c8896f Merge pull request #952 from fredlf/cli-doc-revision
Revises Compose cli reference
2015-02-18 11:38:50 +00:00
Ben Firshman
160e2dc7b9 Merge pull request #949 from aanand/update-intro
Rejig introduction
2015-02-18 09:46:16 +00:00
Aanand Prasad
5e8bcd2d29 Rejig introduction
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-02-17 10:58:51 +00:00
Ben Firshman
2eb20d89af Merge pull request #904 from fredlf/install-doc-revision
Revision and edit of Compose install doc
2015-02-13 12:25:32 +00:00
Aanand Prasad
ab65c829ea Merge pull request #960 from bfirsh/remove-nathanleclaire-as-maintainer
Remove @nathanleclaire as a maintainer
2015-02-13 11:59:27 +00:00
Aanand Prasad
83110b8e6b Merge pull request #959 from bfirsh/tidy-up-contributing-guidelines
Tidy up contributing guidelines
2015-02-13 11:43:05 +00:00
Harald Albers
3a8a25b9b3 Favour yml and yaml extensions in bash completion for -f
Signed-off-by: Harald Albers <github@albersweb.de>
2015-02-13 10:25:18 +01:00
Ben Firshman
0a8f9abfae Remove @nathanleclaire as a maintainer
Thanks for your help <3

Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2015-02-12 16:39:59 +00:00
Ben Firshman
3146fe5e4b Tidy up contributing guidelines
- Add intro saying we roughly follow the Docker project's guidelines
- Link to Docker's signing off docs
- Fix formatting at bottom

Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2015-02-12 13:40:02 +00:00
Fred Lifton
4a33686787 Revises Compose cli reference 2015-02-11 18:22:36 -08:00
Aanand Prasad
64239f1408 Merge pull request #950 from aanand/update-pep8
Update pep8, fix errors and freeze requirements-dev.txt
2015-02-11 21:33:14 +00:00
Aanand Prasad
c39a0b0a2d Update pep8, fix errors and freeze requirements-dev.txt
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-02-11 19:00:13 +00:00
Fred Lifton
8a7b3fb0eb Merge pull request #922 from aanand/tweak-intro
Tweak intro
2015-02-09 16:11:21 -08:00
Daniel Nephin
4251f2b732 Merge pull request #911 from ggtools/release_notes
Add missing cpu_shares option for rel. 1.1.0-rc1
2015-02-06 10:47:40 -05:00
Fred Lifton
9f4775c554 Revision and edit of Compose install doc
Fix rc version to latest.

Signed-off-by: Fred Lifton <fred.lifton@docker.com>
2015-02-05 17:48:06 -08:00
Aanand Prasad
7ff607fb7a Add checklist item for completion script URL to CONTRIBUTING.md
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-02-05 17:37:04 -05:00
Aanand Prasad
b25ed59b1c Tweak intro
We shouldn't yet be recommending production use - for now, let's continue to emphasise the development use case and mention staging/CI.

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-02-05 16:17:38 -05:00
Daniel Nephin
eabca3d7b7 Merge pull request #907 from apenney/typo
Fix a small typo.
2015-02-02 11:02:12 -05:00
Christophe Labouisse
45b8d526ba Add missing cpu_shares option for rel. 1.1.0-rc1
Signed-off-by: Christophe Labouisse <christophe@labouisse.org>
2015-02-01 17:03:50 +01:00
Ashley Penney
75247e5a54 Fix a small typo. 2015-01-31 20:00:04 -05:00
Fred Lifton
2cf1fa6c9d Merge pull request #902 from SvenDowideit/iterate-on-freds-index-doc-revisions
Edit and revision of overview & quick start doc
2015-01-30 10:57:19 -08:00
Sven Dowideit
3b7ea5c055 resolve most of my comments
Signed-off-by: Sven Dowideit <SvenDowideit@home.org.au>
2015-01-30 21:27:57 +10:00
Fred Lifton
461f1ad5d5 Edit and revision of overview & quick start doc
Signed-off-by: Fred Lifton <fred.lifton@docker.com>
2015-01-29 19:55:11 -08:00
Fred Lifton
2307adc4b2 Merge pull request #855 from SvenDowideit/add-docker-docs-metadata-and-reflow-to-80-chars
Add Docker docs.docker.com meta-data, and reflow to 80-chars to simplify GH diffs
2015-01-29 19:21:23 -08:00
Sven Dowideit
d8d0fd6dc9 Add Docker docs.docker.com meta-data, and reflow to 80-chars to simplify github diffs
Signed-off-by: Sven Dowideit <SvenDowideit@docker.com>
2015-01-30 13:11:48 +10:00
Aanand Prasad
e4d6a2c240 Merge pull request #900 from aanand/ship-1.1.0-rc2
Ship 1.1.0-rc2
2015-01-29 18:00:26 -05:00
Aanand Prasad
deb2de3c07 Ship 1.1.0-rc2
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-01-29 18:00:10 -05:00
Aanand Prasad
bc1f6c97d8 Merge pull request #898 from aanand/support-fig-yml
Support fig.yml
2015-01-29 13:47:49 -05:00
Aanand Prasad
7c087f1c07 Support fig.y(a)ml and show a deprecation warning
Closes #864

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-01-29 13:14:44 -05:00
Ben Firshman
fd30920aac Merge pull request #897 from aanand/nicer-env-file-error
Nicer env file error
2015-01-29 00:51:05 +00:00
Aanand Prasad
9bc7604e0e Make sure we're testing uppercase directories properly
(OS X is case-sensitive so we can't have fixture dirs which are
identically named if you ignore case)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-01-28 17:19:27 -05:00
Aanand Prasad
de07e0471e Show a nicer error when the env file doesn't exist
Closes #865

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-01-28 16:20:01 -05:00
Aanand Prasad
dfc6206d0d Extract get_env_files()
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-01-28 16:18:02 -05:00
Aanand Prasad
6c45b6ccdb Make sure we're testing blank lines and comments in env files
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-01-28 16:18:02 -05:00
Daniel Nephin
d79dc85fa5 Merge pull request #887 from albers/multiple-configfiles
Bash completion supports fig.yml and other legacy filenames
2015-01-27 23:08:23 -05:00
Aanand Prasad
22aca1a8a5 Merge pull request #879 from albers/bash-completion
Update bash completion for compose 1.1.0-rc1
2015-01-27 16:34:33 -05:00
Aanand Prasad
74302560f5 Merge pull request #891 from aanand/fix-image-volumes-test
Fix test for image-declared volumes
2015-01-27 16:33:59 -05:00
Aanand Prasad
bc2f6044fd Merge pull request #880 from dnephin/use_latest_docker_py
Use latest docker-py
2015-01-27 16:32:56 -05:00
Aanand Prasad
1476027410 Fix test for image-declared volumes
A stray 'fig' got lost in the merge post-rename, meaning the containers
weren't being cleaned up properly.

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-01-27 14:46:58 -05:00
Daniel Nephin
28fa49e569 Merge pull request #810 from madwire/patch-1
Tweaks to the rails tutorial to bring it inline with rails 4.2 release
2015-01-27 14:45:31 -05:00
Harald Albers
27e4f982fa Bash completion supports fig.yml and other legacy filenames
Signed-off-by: Harald Albers <github@albersweb.de>
2015-01-27 09:03:04 +01:00
Daniel Nephin
0bc4a28dcc Use latest docker-py.
Signed-off-by: Daniel Nephin <dnephin@gmail.com>
2015-01-25 15:33:15 -05:00
Harald Albers
bd535c76d0 Add --service-ports to bash completion
Signed-off-by: Harald Albers <github@albersweb.de>
2015-01-25 09:47:30 -08:00
Daniel Nephin
ef027599f7 Merge pull request #871 from albers/bash-completion
Rebrand bash completion script
2015-01-22 12:52:27 -05:00
Harald Albers
f1e4fb7736 Rebrand bash completion script
Signed-off-by: Harald Albers <github@albersweb.de>
2015-01-22 16:07:11 +01:00
Aanand Prasad
df3221df61 Merge pull request #862 from aanand/ship-1.1.0rc1
Ship 1.1.0rc1
2015-01-20 22:50:39 +00:00
Aanand Prasad
43fdae8bc6 Ship 1.1.0-rc1
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-01-20 22:42:59 +00:00
Aanand Prasad
4869fed97f Merge pull request #835 from aanand/rebrand
Rebrand (pre-RC)
2015-01-20 22:24:21 +00:00
Aanand Prasad
16d6018419 Fix typos
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-01-20 21:28:49 +00:00
Aanand Prasad
8297f55f2c Remove all website-related stuff from docs
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-01-20 21:00:23 +00:00
Aanand Prasad
724be54f09 Manual fixes to docs
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-01-20 21:00:23 +00:00
Aanand Prasad
e3c4a662d9 Find-and-replace on docs
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-01-20 21:00:23 +00:00
Aanand Prasad
c981ce929a Update README.md, ROADMAP.md and CONTRIBUTING.md
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-01-20 21:00:23 +00:00
Aanand Prasad
620e29b63f Rename binary to docker-compose and config file to docker-compose.yml
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-01-20 21:00:23 +00:00
Aanand Prasad
2af7693e64 WIP: rename Fig to Compose
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-01-20 21:00:23 +00:00
Aanand Prasad
7be8b4c06d Merge pull request #851 from aanand/swarm-names
Handle Swarm-style prefixed container names
2015-01-20 20:51:47 +00:00
Aanand Prasad
cbd3ca07c4 Handle Swarm-style prefixed names in Container.from_ps()
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-01-20 20:38:06 +00:00
Aanand Prasad
edb6b24b8f Handle Swarm-style prefixed names in Service.containers()
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-01-20 20:38:06 +00:00
Aanand Prasad
608f29c7cb Unit tests for Service.containers() and get_container_name()
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-01-20 20:38:06 +00:00
Aanand Prasad
37ed743ee8 Merge pull request #859 from aanand/test-image-volumes
Test for preservation of volumes declared in images
2015-01-20 20:26:40 +00:00
Aanand Prasad
17a8a7be4b (Failing) test for preservation of volumes declared in images
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-01-20 20:16:18 +00:00
Aanand Prasad
f57db078ba Merge pull request #863 from dnephin/revert_volume_recreate_changes
Revert #711 from dnephin/fix_volumes_on_recreate
2015-01-20 20:15:17 +00:00
Daniel Nephin
2dd1cc80ca Revert "Merge pull request #711 from dnephin/fix_volumes_on_recreate"
This reverts commit 55095ef488, reversing
changes made to 72095f54b2.

Signed-off-by: Daniel Nephin <dnephin@yelp.com>
2015-01-20 12:03:03 -08:00
Aanand Prasad
55095ef488 Merge pull request #711 from dnephin/fix_volumes_on_recreate
Fix volumes on recreate
2015-01-15 18:53:48 +00:00
Daniel Nephin
7eb476e61d Resolves #447, fix volume logic for recreate container
Signed-off-by: Daniel Nephin <dnephin@gmail.com>
2015-01-14 16:20:59 -05:00
Aanand Prasad
72095f54b2 Merge pull request #639 from albers/bash-completion
Bash completion for fig command
2015-01-13 17:46:42 +00:00
Daniel Nephin
26f45efea2 Remove unused intermediate_container from return value.
Signed-off-by: Daniel Nephin <dnephin@gmail.com>
2015-01-12 22:16:54 -08:00
Aanand Prasad
4827e60641 Merge pull request #568 from Aigeruth/fix/project_name
Convert project_name to lowercase
2015-01-12 16:23:09 +00:00
Aanand Prasad
74a0c47389 Merge pull request #830 from ggtools/cpu_shares
Add cpu_shares option in fig.yml
2015-01-12 12:09:08 +00:00
Christophe Labouisse
aa0c43df96 Add cpu_shares option in fig.yml
This options maps exactly the docker run option with the same name.

Signed-off-by: Christophe Labouisse <christophe@labouisse.org>
2015-01-11 20:10:48 +01:00
Aanand Prasad
4ef2e21cca Merge pull request #815 from dnephin/fix_the_build
Fix the failing test
2015-01-09 12:36:09 +00:00
Daniel Nephin
3ee8437eaa Fix the failing test.
Signed-off-by: Daniel Nephin <dnephin@gmail.com>
2015-01-08 10:55:40 -05:00
Daniel Nephin
b903217a4a Merge pull request #544 from LuminosoInsight/external-links
Allow links to containers outside of the project
2015-01-08 10:39:00 -05:00
Harald Albers
2406a3936a Documentation for bash completion
Signed-off-by: Harald Albers <github@albersweb.de>
2015-01-08 15:18:39 +01:00
Harald Albers
69db596b5d Bash completion for fig command
Signed-off-by: Harald Albers <github@albersweb.de>
2015-01-08 15:18:39 +01:00
Richard Adams
9a90a27376 be explicit with a ruby version number in the Dockerfile
Signed-off-by: Richard Adams <richard@madwire.co.uk>
2015-01-07 19:45:00 +00:00
Aanand Prasad
a2f3c0b5da Merge pull request #804 from bfirsh/roadmap-update
Reorganised roadmap, add high-level goals
2015-01-07 19:19:45 +00:00
Richard Adams
3b638f0c43 tweaks to the rails tutorial to bring it inline with rail 4.2 release
Due to a change in Rack, rails server now listens on localhost instead of 0.0.0.0 by default.

Signed-off-by: Richard Adams <richard@madwire.co.uk>
2015-01-07 17:06:47 +00:00
Ben Firshman
b252300e94 Reorganised roadmap, adding high-level goals
Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2015-01-06 15:39:02 +00:00
Daniel Nephin
be553e393a Merge pull request #787 from ggtools/patch-1
Added missing options
2015-01-05 13:05:48 -05:00
Christophe Labouisse
91c90a722a Added missing options
The stdin_open and tty options are supported by fig but were missing from the documentation.

Signed-off-by: Christophe Labouisse <christophe@labouisse.org>
2015-01-03 23:20:10 +01:00
Ben Firshman
d399d386ae Merge pull request #765 from aanand/roadmap
Docker Compose roadmap
2014-12-31 19:25:39 +00:00
Daniel Nephin
4cb47e498b Merge pull request #586 from dnephin/speed_up_fig_up
Speed up fig up
2014-12-31 14:00:07 -05:00
Daniel Nephin
3056ae4be3 Add a no-build option to fig up, to save time when services were already freshly built.
Signed-off-by: Daniel Nephin <dnephin@gmail.com>
2014-12-31 13:47:24 -05:00
Daniel Nephin
5b777ee5f1 Cleanup service unit tests and restructure some service create logic.
Signed-off-by: Daniel Nephin <dnephin@gmail.com>
2014-12-31 13:39:41 -05:00
Aanand Prasad
c885aaa5f8 ROADMAP.md
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2014-12-31 18:10:05 +00:00
Jason Bernardino Alonso
4257707244 Accept an external_links list in the service configuration dictionary to create links to containers outside of the project
Signed-off-by: Jason Bernardino Alonso <jalonso@luminoso.com>
Signed-off-by: Mauricio de Abreu Antunes <mauricio.abreua@gmail.com>
2014-12-30 15:31:33 -05:00
Aanand Prasad
70c3676084 Merge pull request #763 from bfirsh/pass-through-detach-to-create-container
Don't attach stdin and stdout when in detach mode
2014-12-30 16:05:53 +00:00
Ben Firshman
e89826fe43 Don't attach stdin and stdout when in detach mode
This is primarily to make it work with Swarm, which checks that
AttachStd{in,out,err} is false when creating containers.

Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2014-12-30 14:13:36 +00:00
Aanand Prasad
0e172228d1 Merge pull request #485 from squebe/run-service-ports
Added map service ports option for run command.
2014-12-19 14:17:57 -08:00
Daniel Nephin
cb1a5e0a24 Merge pull request #476 from salehe/dns-search
Add dns_search support in yml config
2014-12-19 10:00:45 -05:00
Mohammad Salehe
3c105c6db2 Fix typo in dns_search documentation
Signed-off-by: Mohammad Salehe <salehe+dev@gmail.com>
2014-12-15 16:37:32 +03:30
Mohammad Salehe
5182bd0968 Add dns_search support in yml config
Signed-off-by: Mohammad Salehe <salehe@gmail.com>
2014-12-15 16:37:31 +03:30
Daniel Nephin
200c44cff3 Merge pull request #728 from aanand/volume_test
Stronger integration test for volume binds
2014-12-13 12:51:22 -05:00
Aanand Prasad
05544ce241 Stronger integration tests for volume binds
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2014-12-12 17:53:57 -08:00
Stephen Quebe
cc834aa564 Added map service ports option for run command.
When using the fig run command, ports defined in fig.yml can be mapped
to the host computer using the -service-ports option.

Signed-off-by: Stephen Quebe <squebe@gmail.com>
2014-12-11 20:30:38 -05:00
Ben Firshman
a5505e7711 Merge pull request #721 from dnephin/fix_broken_tests
Include image name for env_file tests
2014-12-12 00:46:41 +00:00
Daniel Nephin
8f8e322de2 Include image name for env_file tests.
Signed-off-by: Daniel Nephin <dnephin@yelp.com>
2014-12-11 14:25:26 -08:00
Daniel Nephin
64762c9dea Merge pull request #716 from bfirsh/pull-latest-tag-by-default
Pull latest tag by default
2014-12-11 15:58:40 -05:00
Ben Firshman
8ebec9a67f Pull latest tag by default
This was changed in Docker recently:

https://github.com/docker/docker/pull/7759

This means we aren't pulling loads of tags when we only use the
latest.

Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2014-12-11 12:37:04 -08:00
Ben Firshman
45b2712032 Merge pull request #720 from dnephin/upstream_minor_fixes
Support a timeout on docker client
2014-12-11 19:28:33 +00:00
Daniel Nephin
3c0f297ba6 Some minor cleanup from yelp/fig
Signed-off-by: Daniel Nephin <dnephin@yelp.com>
2014-12-11 10:08:39 -08:00
Ben Firshman
c0123c7477 Merge pull request #717 from bfirsh/rm-containers-in-scripts
Remove containers in scripts
2014-12-10 23:26:51 +00:00
Ben Firshman
2a0782c660 Merge pull request #718 from bfirsh/docker-py-0.6.0
Upgrade to docker-py 0.6.0
2014-12-09 22:12:05 +00:00
Daniel Nephin
481a8cb7ab Merge pull request #713 from leishman/add-favicon-headers
Add favicon.ico and links in header
2014-12-09 14:29:33 -05:00
Ben Firshman
788741025e Upgrade to docker-py 0.6.0
Force using remote API version 1.14 so Fig is still compatible with
Docker 1.2.

Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2014-12-09 10:57:45 -08:00
Ben Firshman
c12d1d73f0 Remove containers in scripts
So we don't clutter up Docker with loads of stopped containers.

Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2014-12-09 10:49:37 -08:00
Ben Firshman
e794e79209 Merge pull request #665 from benlangfeld/support-env-file
Support env file
2014-12-09 18:42:31 +00:00
Ben Langfeld
98b6d7be78 Add support for 'env_file' key
Signed-off-by: Ben Langfeld <ben@langfeld.me>
2014-12-08 23:42:09 +00:00
Ben Firshman
a12cf826cd Merge pull request #585 from alunduil/add-tests-to-sdist
Add tests to sdist.
2014-12-08 21:34:47 +00:00
Ben Firshman
d18cfa1c98 Merge pull request #661 from bfirsh/add-entrypoint-to-dockerfile
Add fig as entrypoint to Dockerfile
2014-12-08 21:32:06 +00:00
Ben Firshman
9a04ae0ddf Merge pull request #623 from TFenby/capabilities
Add capability add/drop introduced in Docker 1.2
2014-12-08 21:24:02 +00:00
Alexander Leishman
ab77cef7ab Add favicon.ico and links in header
Signed-off-by: Alexander Leishman <leishman3@gmail.com>
2014-12-08 13:04:02 -08:00
Tyler Fenby
5c58180538 Add capability add/drop introduced in Docker 1.2
Signed-off-by: Tyler Fenby <tylerfenby@gmail.com>
2014-12-08 12:32:52 -05:00
Ben Firshman
429a3feabc Merge pull request #699 from dtenenba/master
interpolate service_name in error message
2014-12-08 08:49:54 -08:00
Ben Firshman
1033439e63 Merge pull request #701 from bersace/master
Respect --allow-insecure-ssl option for dependencies
2014-12-08 08:46:30 -08:00
Ben Firshman
e9d946b038 Merge pull request #594 from popox/restart-option
Add restart option to Fig.
2014-12-08 08:43:27 -08:00
Étienne BERSAC
4e8337c168 Respect --allow-insecure-ssl option for dependencies
Signed-off-by: Étienne Bersac <etienne.bersac@novapost.fr>
2014-12-04 11:45:36 +01:00
Dan Tenenbaum
e34a62956e interpolate service_name in error message when service has no configuration options.
Signed-off-by: Dan Tenenbaum <dtenenba@fhcrc.org>
2014-12-03 09:44:35 -08:00
Alex Brandt
0150b38b8f Add tests to sdist.
In order to validate installation it's very convenient to have the tests
as part of the source distribution.  This greatly assists native
packaging that might occur (i.e. Gentoo ebuilds).

Signed-off-by: Alex Brandt <alunduil@alunduil.com>
2014-11-28 09:37:33 -06:00
Ben Firshman
bb85e238e0 Add fig as entrypoint to Dockerfile
A step towards "docker run fig".

Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2014-11-20 17:23:43 +00:00
Daniel Nephin
65ae22e79a Merge pull request #611 from raulcd/604_support_reloading_containers
Add signal in the kill CLI commando to send a specific signal to the service
2014-11-13 10:28:14 -05:00
Paul B
04da6b035e Add restart option to Fig. Related to #478
Signed-off-by: Paul Bonaud <paul@bonaud.fr>
2014-11-08 12:10:11 +01:00
Daniel Nephin
d3e94f2caf Merge pull request #624 from drewkett/multiple_port_mappings
Support multiple port bindings for same internal port
2014-11-07 11:28:27 -08:00
Andrew Burkett
4f6d02867b Move to build_port_bindings(). Added Tests
Signed-off-by: Andrew Burkett <burkett.andrew@gmail.com>
2014-11-06 18:11:09 -08:00
Andrew Burkett
f98323b79e Support multiple port mappings for same internal port
Signed-off-by: Andrew Burkett <burkett.andrew@gmail.com>
2014-11-06 16:29:39 -08:00
Aanand Prasad
3f4b16181d Merge pull request #618 from bfirsh/make-dnephin-a-maintainer
Make @dnephin a maintainer
2014-11-05 10:58:50 +00:00
Aanand Prasad
5dde2a2498 Merge pull request #612 from bfirsh/ship-1.0.1
Ship 1.0.1
2014-11-05 10:57:53 +00:00
Ben Firshman
06a1b32c12 Make @dnephin a maintainer
Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2014-11-05 09:47:48 +00:00
Ben Firshman
46433c70b6 Ship 1.0.1
Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2014-11-04 14:12:48 +00:00
Aanand Prasad
9260603149 Merge pull request #588 from bfirsh/use-upstream-dockerpty
Use upstream dockerpty 0.3.2
2014-11-04 12:59:01 +00:00
Raúl Cumplido
9abdd337b5 Add signal in the kill CLI commando to send a specific signal to the service
Signed-off-by: Raúl Cumplido <raulcumplido@gmail.com>
2014-11-04 12:22:39 +00:00
Ben Firshman
8773f51583 Don't select stdin when interactive=False
Patch from https://github.com/d11wtq/dockerpty/pull/24

Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2014-11-04 10:25:27 +00:00
Ben Firshman
38a3ee8d63 Use upstream dockerpty 0.3.2
This reverts commit 60411e9f05.

Closes #556

Signed-off-by: Ben Firshman <ben@firshman.co.uk>

Conflicts:
	requirements.txt
	setup.py
2014-11-04 10:23:42 +00:00
Aanand Prasad
cb275b8633 Merge pull request #591 from npeters/fix-insecure-registry
fix insecure parameter
2014-11-03 14:17:43 +00:00
Aanand Prasad
604b370bb4 Merge pull request #606 from kojiromike/602-no-help-without-figfile
Fixes #602 Allowing `help $cmd` with no figfile
2014-11-03 11:27:58 +00:00
Michael A. Smith
782a46fd60 Fixes #602 Allowing help $cmd with no figfile
Signed-off-by: Michael A. Smith <michael@smith-li.com>
2014-11-02 06:22:16 -05:00
Aanand Prasad
1bd3d0dd77 Merge pull request #584 from kevinsimper/patch-1
Update wordpress url on wordpress example
2014-10-31 12:23:30 +00:00
Nicolas Peters
b64ea85916 fix insecure parameter
Signed-off-by: Nicolas Peters <peters.nico@gmail.com>
2014-10-28 00:05:17 +01:00
Kevin Simper
b47ab2b0f6 Update wordpress url on wordpress example
The old url gave a 302 and wordpress 4.0 has also been released.
2014-10-24 23:50:06 +02:00
Aanand Prasad
11280e4f30 Merge pull request #581 from bfirsh/add-dockerignore
Add .dockerignore
2014-10-24 11:59:01 +01:00
Aanand Prasad
a83876da09 Merge pull request #580 from bfirsh/improve-contributing-instructions
Improve contributing instructions
2014-10-24 11:43:14 +01:00
Ben Firshman
e66c0452d5 Add .dockerignore
Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2014-10-23 19:00:44 +01:00
Ben Firshman
899670fc6c Move building binaries instructions
It's less important than signing your work.

Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2014-10-23 18:31:28 +01:00
Ben Firshman
ea45715a50 Tidied up development environment instructions
Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2014-10-23 18:30:48 +01:00
Ben Firshman
28f9c8d047 Add TL;DR section to CONTRIBUTING.md
Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2014-10-23 18:28:17 +01:00
Aanand Prasad
292fe6640e Merge pull request #578 from bfirsh/update-url-in-twitter-button
Update Twitter button URL
2014-10-23 16:17:38 +01:00
Ben Firshman
b759b9854a Update Twitter button URL
I originally left it as orchardup.github.io so the number wouldn't roll
back to 0, but people have been tweeting about fig.sh now.

Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2014-10-23 16:05:11 +01:00
Aanand Prasad
40943d5c81 Merge pull request #533 from bfirsh/mention-configuration-files-on-home-page
Mention names of config files on home page
2014-10-23 11:23:33 +01:00
Aanand Prasad
93a0195dc8 Merge branch 'andreagrandi-patch-1'
Closes #570.
2014-10-23 11:14:53 +01:00
Andrea Grandi
7f0745d146 Add command line options to the documentation
Signed-off-by: Andrea Grandi <a.grandi@gmail.com>
2014-10-23 11:13:40 +01:00
Ben Firshman
9813a8d5be Merge pull request #562 from tjrivera/insecure-registry
Allow dependent image pull from insecure registry
2014-10-22 18:45:03 +01:00
Tyler Rivera
491181ec31 Allow dependent image pull from insecure registry
PR #490 Provides the ability to pull from an insecure registry by passing --allow-insecure-ssl. This commit extends the work done in #490 and adds the ability to pass --allow-insecure-ssl to the up and run commands which will attempt to pull dependent images if they do not exist.

Signed-off-by: Tyler Rivera <riverat2@email.chop.edu>
2014-10-22 10:57:43 -04:00
Aanand Prasad
e1ad5b1b99 Merge pull request #575 from bfirsh/fix-google-analytics-code
Fix Google Analytics code
2014-10-22 12:48:45 +01:00
Aanand Prasad
67d4b7c587 Merge pull request #574 from bfirsh/update-tagline
Update tagline to match website
2014-10-22 12:46:16 +01:00
Ben Firshman
98f663bab2 Fix Google Analytics code
It was not reporting because our URL changed.

Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2014-10-22 12:46:05 +01:00
Ben Firshman
75b3dcf5fd Update tagline to match website
Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2014-10-22 12:41:01 +01:00
Ben Firshman
deeb6c2236 Merge pull request #567 from aanand/fix-wordpress-guide
Fix language in Wordpress guide
2014-10-22 11:31:41 +01:00
Gabor Nagy
c838f7da18 Convert project_name to lowercase
Signed-off-by: Gabor Nagy <mail@aigeruth.hu>
2014-10-21 13:40:19 +02:00
Aanand Prasad
e1e2b75691 Fix language in Wordpress guide
Was still talking about 'environment variables'.

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2014-10-21 11:44:42 +01:00
Aanand Prasad
392c118bbc Merge pull request #554 from dnephin/more_defensive_stream
Fix a couple bugs with 1.0 release and Docker 1.3
2014-10-20 15:10:50 +01:00
Ben Firshman
dbd1e56dd3 Merge pull request #548 from aanand/fix-docker-py-requirement
Fix docker-py requirement in setup.py
2014-10-20 10:33:44 +01:00
Daniel Nephin
7544580b4b Resolves #553, Resolves #546 - bug fixes with unit tests
Signed-off-by: Daniel Nephin <dnephin@gmail.com>
2014-10-18 13:54:10 -04:00
Aanand Prasad
2efb4f5be0 Merge pull request #550 from bfirsh/fix-date-on-change-notes
Fix date on 1.0.0 change notes
2014-10-17 15:47:50 +01:00
Ben Firshman
5e8dc6c972 Merge pull request #549 from aanand/credit-1.0-contributors
Credit 1.0 contributors
2014-10-17 15:43:34 +01:00
Ben Firshman
23c8ec8930 Fix date on 1.0.0 change notes
Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2014-10-17 15:43:14 +01:00
Aanand Prasad
bd8affb7aa Credit 1.0 contributors
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2014-10-17 14:54:46 +01:00
Aanand Prasad
7a943739cb Fix docker-py requirement in setup.py
I updated requirements.txt but forgot about setup.py, so the version on
pypi is broken for people who already have docker-py 0.5.0 installed.

Closes #547.

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2014-10-17 11:17:58 +01:00
Ben Firshman
d3f88cace5 Mention names of config files on home page
Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2014-10-10 11:06:49 +01:00
143 changed files with 7530 additions and 2625 deletions

4
.dockerignore Normal file
View File

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

2
.gitignore vendored
View File

@@ -5,4 +5,4 @@
/dist
/docs/_site
/venv
fig.spec
docker-compose.spec

View File

@@ -1,7 +1,124 @@
Change log
==========
1.0.0 (2014-10-07)
1.3.1 (2015-06-21)
------------------
The following bugs have been fixed:
- `docker-compose build` would always attempt to pull the base image before building.
- `docker-compose help migrate-to-labels` failed with an error.
- If no network mode was specified, Compose would set it to "bridge", rather than allowing the Docker daemon to use its configured default network mode.
1.3.0 (2015-06-18)
------------------
Firstly, two important notes:
- **This release contains breaking changes, and you will need to either remove or migrate your existing containers before running your app** - see the [upgrading section of the install docs](https://github.com/docker/compose/blob/1.3.0rc1/docs/install.md#upgrading) for details.
- Compose now requires Docker 1.6.0 or later.
We've done a lot of work in this release to remove hacks and make Compose more stable:
- Compose now uses container labels, rather than names, to keep track of containers. This makes Compose both faster and easier to integrate with your own tools.
- Compose no longer uses "intermediate containers" when recreating containers for a service. This makes `docker-compose up` less complex and more resilient to failure.
There are some new features:
- `docker-compose up` has an **experimental** new behaviour: it will only recreate containers for services whose configuration has changed in `docker-compose.yml`. This will eventually become the default, but for now you can take it for a spin:
$ docker-compose up --x-smart-recreate
- When invoked in a subdirectory of a project, `docker-compose` will now climb up through parent directories until it finds a `docker-compose.yml`.
Several new configuration keys have been added to `docker-compose.yml`:
- `dockerfile`, like `docker build --file`, lets you specify an alternate Dockerfile to use with `build`.
- `labels`, like `docker run --labels`, lets you add custom metadata to containers.
- `extra_hosts`, like `docker run --add-host`, lets you add entries to a container's `/etc/hosts` file.
- `pid: host`, like `docker run --pid=host`, lets you reuse the same PID namespace as the host machine.
- `cpuset`, like `docker run --cpuset-cpus`, lets you specify which CPUs to allow execution in.
- `read_only`, like `docker run --read-only`, lets you mount a container's filesystem as read-only.
- `security_opt`, like `docker run --security-opt`, lets you specify [security options](https://docs.docker.com/reference/run/#security-configuration).
- `log_driver`, like `docker run --log-driver`, lets you specify a [log driver](https://docs.docker.com/reference/run/#logging-drivers-log-driver).
Many bugs have been fixed, including the following:
- The output of `docker-compose run` was sometimes truncated, especially when running under Jenkins.
- A service's volumes would sometimes not update after volume configuration was changed in `docker-compose.yml`.
- Authenticating against third-party registries would sometimes fail.
- `docker-compose run --rm` would fail to remove the container if the service had a `restart` policy in place.
- `docker-compose scale` would refuse to scale a service beyond 1 container if it exposed a specific port number on the host.
- Compose would refuse to create multiple volume entries with the same host path.
Thanks @ahromis, @albers, @aleksandr-vin, @antoineco, @ccverak, @chernjie, @dnephin, @edmorley, @fordhurley, @josephpage, @KyleJamesWalker, @lsowen, @mchasal, @noironetworks, @sdake, @sdurrheimer, @sherter, @stephenlawrence, @thaJeztah, @thieman, @turtlemonvh, @twhiteman, @vdemeester, @xuxinkun and @zwily!
1.2.0 (2015-04-16)
------------------
- `docker-compose.yml` now supports an `extends` option, which enables a service to inherit configuration from another service in another configuration file. This is really good for sharing common configuration between apps, or for configuring the same app for different environments. Here's the [documentation](https://github.com/docker/compose/blob/master/docs/yml.md#extends).
- When using Compose with a Swarm cluster, containers that depend on one another will be co-scheduled on the same node. This means that most Compose apps will now work out of the box, as long as they don't use `build`.
- Repeated invocations of `docker-compose up` when using Compose with a Swarm cluster now work reliably.
- Directories passed to `build`, filenames passed to `env_file` and volume host paths passed to `volumes` are now treated as relative to the *directory of the configuration file*, not the directory that `docker-compose` is being run in. In the majority of cases, those are the same, but if you use the `-f|--file` argument to specify a configuration file in another directory, **this is a breaking change**.
- A service can now share another service's network namespace with `net: container:<service>`.
- `volumes_from` and `net: container:<service>` entries are taken into account when resolving dependencies, so `docker-compose up <service>` will correctly start all dependencies of `<service>`.
- `docker-compose run` now accepts a `--user` argument to specify a user to run the command as, just like `docker run`.
- The `up`, `stop` and `restart` commands now accept a `--timeout` (or `-t`) argument to specify how long to wait when attempting to gracefully stop containers, just like `docker stop`.
- `docker-compose rm` now accepts `-f` as a shorthand for `--force`, just like `docker rm`.
Thanks, @abesto, @albers, @alunduil, @dnephin, @funkyfuture, @gilclark, @IanVS, @KingsleyKelly, @knutwalker, @thaJeztah and @vmalloc!
1.1.0 (2015-02-25)
------------------
Fig has been renamed to Docker Compose, or just Compose for short. This has several implications for you:
- The command you type is now `docker-compose`, not `fig`.
- You should rename your fig.yml to docker-compose.yml.
- If youre installing via PyPi, the package is now `docker-compose`, so install it with `pip install docker-compose`.
Besides that, theres a lot of new stuff in this release:
- Weve made a few small changes to ensure that Compose will work with Swarm, Dockers new clustering tool (https://github.com/docker/swarm). Eventually you'll be able to point Compose at a Swarm cluster instead of a standalone Docker host and itll run your containers on the cluster with no extra work from you. As Swarm is still developing, integration is rough and lots of Compose features don't work yet.
- `docker-compose run` now has a `--service-ports` flag for exposing ports on the given service. This is useful for e.g. running your webapp with an interactive debugger.
- You can now link to containers outside your app with the `external_links` option in docker-compose.yml.
- You can now prevent `docker-compose up` from automatically building images with the `--no-build` option. This will make fewer API calls and run faster.
- If you dont specify a tag when using the `image` key, Compose will default to the `latest` tag, rather than pulling all tags.
- `docker-compose kill` now supports the `-s` flag, allowing you to specify the exact signal you want to send to a services containers.
- docker-compose.yml now has an `env_file` key, analogous to `docker run --env-file`, letting you specify multiple environment variables in a separate file. This is great if you have a lot of them, or if you want to keep sensitive information out of version control.
- docker-compose.yml now supports the `dns_search`, `cap_add`, `cap_drop`, `cpu_shares` and `restart` options, analogous to `docker run`s `--dns-search`, `--cap-add`, `--cap-drop`, `--cpu-shares` and `--restart` options.
- Compose now ships with Bash tab completion - see the installation and usage docs at https://github.com/docker/compose/blob/1.1.0/docs/completion.md
- A number of bugs have been fixed - see the milestone for details: https://github.com/docker/compose/issues?q=milestone%3A1.1.0+
Thanks @dnephin, @squebe, @jbalonso, @raulcd, @benlangfield, @albers, @ggtools, @bersace, @dtenenba, @petercv, @drewkett, @TFenby, @paulRbr, @Aigeruth and @salehe!
1.0.1 (2014-11-04)
------------------
- Added an `--allow-insecure-ssl` option to allow `fig up`, `fig run` and `fig pull` to pull from insecure registries.
- Fixed `fig run` not showing output in Jenkins.
- Fixed a bug where Fig couldn't build Dockerfiles with ADD statements pointing at URLs.
1.0.0 (2014-10-16)
------------------
The highlights:
@@ -43,6 +160,8 @@ Other things:
- When starting a service where `volumes_from` points to a service without any containers running, that service will now be started.
- Lots of docs improvements. Notably, environment variables are documented and official repositories are used throughout.
Thanks @dnephin, @d11wtq, @marksteve, @rubbish, @jbalonso, @timfreund, @alunduil, @mieciu, @shuron, @moss, @suzaku and @chmouel! Whew.
0.5.2 (2014-07-28)
------------------

View File

@@ -1,98 +1,87 @@
# Contributing to Fig
# Contributing to Compose
Compose is a part of the Docker project, and follows the same rules and
principles. Take a read of [Docker's contributing guidelines](https://github.com/docker/docker/blob/master/CONTRIBUTING.md)
to get an overview.
## TL;DR
Pull requests will need:
- Tests
- Documentation
- [To be signed off](https://github.com/docker/docker/blob/master/CONTRIBUTING.md#sign-your-work)
- A logical series of [well written commits](https://github.com/alphagov/styleguides/blob/master/git.md)
## Development environment
If you're looking contribute to [Fig](http://www.fig.sh/)
If you're looking contribute to Compose
but you're new to the project or maybe even to Python, here are the steps
that should get you started.
1. Fork [https://github.com/docker/fig](https://github.com/docker/fig) to your username. kvz in this example.
1. Clone your forked repository locally `git clone git@github.com:kvz/fig.git`.
1. Enter the local directory `cd fig`.
1. Set up a development environment `python setup.py develop`. That will install the dependencies and set up a symlink from your `fig` executable to the checkout of the repo. So from any of your fig projects, `fig` now refers to your development project. Time to start hacking : )
1. Works for you? Run the test suite via `./script/test` to verify it won't break other usecases.
1. All good? Commit and push to GitHub, and submit a pull request.
1. Fork [https://github.com/docker/compose](https://github.com/docker/compose)
to your username.
2. Clone your forked repository locally `git clone git@github.com:yourusername/compose.git`.
3. Enter the local directory `cd compose`.
4. Set up a development environment by running `python setup.py develop`. This
will install the dependencies and set up a symlink from your `docker-compose`
executable to the checkout of the repository. When you now run
`docker-compose` from anywhere on your machine, it will run your development
version of Compose.
## Running the test suite
Use the test script to run linting checks and then the full test suite against
different Python interpreters:
$ script/test
Tests are run against a Docker daemon inside a container, so that we can test
against multiple Docker versions. By default they'll run against only the latest
Docker version - set the `DOCKER_VERSIONS` environment variable to "all" to run
against all supported versions:
$ DOCKER_VERSIONS=all script/test
Arguments to `script/test` are passed through to the `nosetests` executable, so
you can specify a test directory, file, module, class or method:
$ script/test tests/unit
$ script/test tests/unit/cli_test.py
$ script/test tests.integration.service_test
$ script/test tests.integration.service_test:ServiceTest.test_containers
## Building binaries
Linux:
`script/build-linux` will build the Linux binary inside a Docker container:
$ script/build-linux
OS X:
`script/build-osx` will build the Mac OS X binary inside a virtualenv:
$ script/build-osx
Note that this only works on Mountain Lion, not Mavericks, due to a [bug in PyInstaller](http://www.pyinstaller.org/ticket/807).
## Sign your work
The sign-off is a simple line at the end of the explanation for the
patch, which certifies that you wrote it or otherwise have the right to
pass it on as an open-source patch. The rules are pretty simple: if you
can certify the below (from [developercertificate.org](http://developercertificate.org/)):
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
then you just add a line saying
Signed-off-by: Random J Developer <random@developer.example.org>
using your real name (sorry, no pseudonyms or anonymous contributions.)
The easiest way to do this is to use the `--signoff` flag when committing. E.g.:
$ git commit --signoff
For official releases, you should build inside a Mountain Lion VM for proper
compatibility. Run the this script first to prepare the environment before
building - it will use Homebrew to make sure Python is installed and
up-to-date.
$ script/prepare-osx
## Release process
1. Open pull request that:
- Updates version in `fig/__init__.py`
- Updates version in `docs/install.md`
- Updates the version in `compose/__init__.py`
- Updates the binary URL in `docs/install.md`
- Adds release notes to `CHANGES.md`
2. Create unpublished GitHub release with release notes
3. Build Linux version on any Docker host with `script/build-linux` and attach to release
4. Build OS X version on Mountain Lion with `script/build-osx` and attach to release as `fig-Darwin-x86_64` and `fig-Linux-x86_64`.
3. Build Linux version on any Docker host with `script/build-linux` and attach
to release
4. Build OS X version on Mountain Lion with `script/build-osx` and attach to
release as `docker-compose-Darwin-x86_64` and `docker-compose-Linux-x86_64`.
5. Publish GitHub release, creating tag
6. Update website with `script/deploy-docs`
7. Upload PyPi package
$ git checkout $VERSION
$ python setup.py sdist upload
$ git checkout $VERSION
$ python setup.py sdist upload

View File

@@ -1,5 +1,64 @@
FROM debian:wheezy
RUN apt-get update -qq && apt-get install -qy python python-pip python-dev git && apt-get clean
RUN set -ex; \
apt-get update -qq; \
apt-get install -y \
gcc \
make \
zlib1g \
zlib1g-dev \
libssl-dev \
git \
apt-transport-https \
ca-certificates \
curl \
lxc \
iptables \
; \
rm -rf /var/lib/apt/lists/*
# Build Python 2.7.9 from source
RUN set -ex; \
curl -LO https://www.python.org/ftp/python/2.7.9/Python-2.7.9.tgz; \
tar -xzf Python-2.7.9.tgz; \
cd Python-2.7.9; \
./configure --enable-shared; \
make; \
make install; \
cd ..; \
rm -rf /Python-2.7.9; \
rm Python-2.7.9.tgz
# Make libpython findable
ENV LD_LIBRARY_PATH /usr/local/lib
# Install setuptools
RUN set -ex; \
curl -LO https://bootstrap.pypa.io/ez_setup.py; \
python ez_setup.py; \
rm ez_setup.py
# Install pip
RUN set -ex; \
curl -LO https://pypi.python.org/packages/source/p/pip/pip-7.0.1.tar.gz; \
tar -xzf pip-7.0.1.tar.gz; \
cd pip-7.0.1; \
python setup.py install; \
cd ..; \
rm -rf pip-7.0.1; \
rm pip-7.0.1.tar.gz
ENV ALL_DOCKER_VERSIONS 1.6.0 1.7.0
RUN set -ex; \
curl https://get.docker.com/builds/Linux/x86_64/docker-1.6.0 -o /usr/local/bin/docker-1.6.0; \
chmod +x /usr/local/bin/docker-1.6.0; \
curl https://test.docker.com/builds/Linux/x86_64/docker-1.7.0 -o /usr/local/bin/docker-1.7.0; \
chmod +x /usr/local/bin/docker-1.7.0
# Set the default Docker to be run
RUN ln -s /usr/local/bin/docker-1.6.0 /usr/local/bin/docker
RUN useradd -d /home/user -m -s /bin/bash user
WORKDIR /code/
@@ -13,3 +72,5 @@ ADD . /code/
RUN python setup.py install
RUN chown -R user /code/
ENTRYPOINT ["/usr/local/bin/docker-compose"]

View File

@@ -1,4 +1,3 @@
Aanand Prasad <aanand.prasad@gmail.com> (@aanand)
Ben Firshman <ben@firshman.co.uk> (@bfirsh)
Chris Corbyn <chris@w3style.co.uk> (@d11wtq)
Nathan LeClaire <nathan.leclaire@gmail.com> (@nathanleclaire)
Daniel Nephin <dnephin@gmail.com> (@dnephin)

View File

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

View File

@@ -1,46 +1,52 @@
Fig
===
Docker Compose
==============
*(Previously known as Fig)*
[![wercker status](https://app.wercker.com/status/d5dbac3907301c3d5ce735e2d5e95a5b/s/master "wercker status")](https://app.wercker.com/project/bykey/d5dbac3907301c3d5ce735e2d5e95a5b)
Compose is a tool for defining and running multi-container applications with
Docker. With Compose, you define a multi-container application in a single
file, then spin your application up in a single command which does everything
that needs to be done to get it running.
Fast, isolated development environments using Docker.
Compose is great for development environments, staging servers, and CI. We don't
recommend that you use it in production yet.
Define your app's environment with Docker so it can be reproduced anywhere:
Using Compose is basically a three-step process.
FROM python:2.7
ADD . /code
WORKDIR /code
RUN pip install -r requirements.txt
CMD python app.py
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.
Define the services that make up your app so they can be run together in an isolated environment:
A `docker-compose.yml` looks like this:
```yaml
web:
build: .
links:
- db
ports:
- "8000:8000"
- "49100:22"
db:
image: postgres
```
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
links:
- redis
redis:
image: redis
(No more installing Postgres on your laptop!)
Compose has commands for managing the whole lifecycle of your application:
Then type `fig up`, and Fig will start and run your entire app:
![example fig run](https://orchardup.com/static/images/fig-example-large.gif)
There are commands to:
- start, stop and rebuild services
- view the status of running services
- tail running services' log output
- run a one-off command on a service
* 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
Installation and documentation
------------------------------
Full documentation is available on [Fig's website](http://www.fig.sh/).
- Full documentation is available on [Docker's website](http://docs.docker.com/compose/).
- If you have any questions, you can talk in real-time with other developers in the #docker-compose IRC channel on Freenode. [Click here to join using IRCCloud.](https://www.irccloud.com/invite?hostname=irc.freenode.net&channel=%23docker-compose)
Contributing
------------
[![Build Status](http://jenkins.dockerproject.org/buildStatus/icon?job=Compose%20Master)](http://jenkins.dockerproject.org/job/Compose%20Master/)
Want to help build Compose? Check out our [contributing documentation](https://github.com/docker/compose/blob/master/CONTRIBUTING.md).

28
ROADMAP.md Normal file
View File

@@ -0,0 +1,28 @@
# Roadmap
## More than just development environments
Over time we will extend Compose's remit to cover test, staging and production environments. This is not a simple task, and will take many incremental improvements such as:
- Composes brute-force “delete and recreate everything” approach is great for dev and testing, but it not sufficient for production environments. You should be able to define a "desired" state that Compose will intelligently converge to.
- It should be possible to partially modify the config file for different environments (dev/test/staging/prod), passing in e.g. custom ports or volume mount paths. ([#426](https://github.com/docker/fig/issues/426))
- Compose should recommend a technique for zero-downtime deploys.
## Integration with Swarm
Compose should integrate really well with Swarm so you can take an application you've developed on your laptop and run it on a Swarm cluster.
The current state of integration is documented in [SWARM.md](SWARM.md).
## Applications spanning multiple teams
Compose works well for applications that are in a single repository and depend on services that are hosted on Docker Hub. If your application depends on another application within your organisation, Compose doesn't work as well.
There are several ideas about how this could work, such as [including external files](https://github.com/docker/fig/issues/318).
## An even better tool for development environments
Compose is a great tool for development environments, but it could be even better. For example:
- [Compose could watch your code and automatically kick off builds when something changes.](https://github.com/docker/fig/issues/184)
- It should be possible to define hostnames for containers which work from the host machine, e.g. “mywebcontainer.local”. This is needed by apps comprising multiple web services which generate links to one another (e.g. a frontend website and a separate admin webapp)

32
SWARM.md Normal file
View File

@@ -0,0 +1,32 @@
Docker Compose/Swarm integration
================================
Eventually, Compose and Swarm aim to have full integration, meaning you can point a Compose app at a Swarm cluster and have it all just work as if you were using a single Docker host.
However, the current extent of integration is minimal: Compose can create containers on a Swarm cluster, but the majority of Compose apps wont work out of the box unless all containers are scheduled on one host, defeating much of the purpose of using Swarm in the first place.
Still, Compose and Swarm can be useful in a “batch processing” scenario (where a large number of containers need to be spun up and down to do independent computation) or a “shared cluster” scenario (where multiple teams want to deploy apps on a cluster without worrying about where to put them).
A number of things need to happen before full integration is achieved, which are documented below.
Links and networking
--------------------
The primary thing stopping multi-container apps from working seamlessly on Swarm is getting them to talk to one another: enabling private communication between containers on different hosts hasnt been solved in a non-hacky way.
Long-term, networking is [getting overhauled](https://github.com/docker/docker/issues/9983) in such a way that itll fit the multi-host model much better. For now, **linked containers are automatically scheduled on the same host**.
Building
--------
`docker build` against a Swarm cluster is not implemented, so for now the `build` option will not work - you will need to manually build your service's image, push it somewhere and use `image` to instruct Compose to pull it. Here's an example using the Docker Hub:
$ docker build -t myusername/web .
$ docker push myusername/web
$ cat docker-compose.yml
web:
image: myusername/web
links: ["db"]
db:
image: postgres
$ docker-compose up -d

3
bin/docker-compose Executable file
View File

@@ -0,0 +1,3 @@
#!/usr/bin/env python
from compose.cli.main import main
main()

View File

@@ -1,3 +0,0 @@
#!/usr/bin/env python
from fig.cli.main import main
main()

3
compose/__init__.py Normal file
View File

@@ -0,0 +1,3 @@
from __future__ import unicode_literals
__version__ = '1.3.1'

View File

@@ -1,17 +1,16 @@
from __future__ import unicode_literals
from __future__ import absolute_import
from requests.exceptions import ConnectionError, SSLError
import errno
import logging
import os
import re
import yaml
import six
from .. import config
from ..project import Project
from ..service import ConfigError
from .docopt_command import DocoptCommand
from .utils import call_silently, is_mac, is_ubuntu
from .utils import call_silently, is_mac, is_ubuntu, find_candidates_in_parent_dirs
from .docker_client import docker_client
from . import verbose_proxy
from . import errors
@@ -19,6 +18,13 @@ from .. import __version__
log = logging.getLogger(__name__)
SUPPORTED_FILENAMES = [
'docker-compose.yml',
'docker-compose.yaml',
'fig.yml',
'fig.yaml',
]
class Command(DocoptCommand):
base_dir = '.'
@@ -26,7 +32,7 @@ class Command(DocoptCommand):
def dispatch(self, *args, **kwargs):
try:
super(Command, self).dispatch(*args, **kwargs)
except SSLError, e:
except SSLError as e:
raise errors.UserError('SSL error: %s' % e)
except ConnectionError:
if call_silently(['which', 'docker']) != 0:
@@ -42,7 +48,16 @@ class Command(DocoptCommand):
raise errors.ConnectionErrorGeneric(self.get_client().base_url)
def perform_command(self, options, handler, command_options):
explicit_config_path = options.get('--file') or os.environ.get('FIG_FILE')
if options['COMMAND'] == 'help':
# Skip looking up the compose file.
handler(None, command_options)
return
if 'FIG_FILE' in os.environ:
log.warn('The FIG_FILE environment variable is deprecated.')
log.warn('Please use COMPOSE_FILE instead.')
explicit_config_path = options.get('--file') or os.environ.get('COMPOSE_FILE') or os.environ.get('FIG_FILE')
project = self.get_project(
self.get_config_path(explicit_config_path),
project_name=options.get('--project-name'),
@@ -54,36 +69,31 @@ class Command(DocoptCommand):
client = docker_client()
if verbose:
version_info = six.iteritems(client.version())
log.info("Fig version %s", __version__)
log.info("Compose version %s", __version__)
log.info("Docker base_url: %s", client.base_url)
log.info("Docker version: %s",
", ".join("%s=%s" % item for item in version_info))
return verbose_proxy.VerboseProxy('docker', client)
return client
def get_config(self, config_path):
try:
with open(config_path, 'r') as fh:
return yaml.safe_load(fh)
except IOError as e:
if e.errno == errno.ENOENT:
raise errors.FigFileNotFound(os.path.basename(e.filename))
raise errors.UserError(six.text_type(e))
def get_project(self, config_path, project_name=None, verbose=False):
try:
return Project.from_config(
return Project.from_dicts(
self.get_project_name(config_path, project_name),
self.get_config(config_path),
config.load(config_path),
self.get_client(verbose=verbose))
except ConfigError as e:
raise errors.UserError(six.text_type(e))
def get_project_name(self, config_path, project_name=None):
def normalize_name(name):
return re.sub(r'[^a-zA-Z0-9]', '', name)
return re.sub(r'[^a-z0-9]', '', name.lower())
project_name = project_name or os.environ.get('FIG_PROJECT_NAME')
if 'FIG_PROJECT_NAME' in os.environ:
log.warn('The FIG_PROJECT_NAME environment variable is deprecated.')
log.warn('Please use COMPOSE_PROJECT_NAME instead.')
project_name = project_name or os.environ.get('COMPOSE_PROJECT_NAME') or os.environ.get('FIG_PROJECT_NAME')
if project_name is not None:
return normalize_name(project_name)
@@ -97,13 +107,24 @@ class Command(DocoptCommand):
if file_path:
return os.path.join(self.base_dir, file_path)
if os.path.exists(os.path.join(self.base_dir, 'fig.yaml')):
log.warning("Fig just read the file 'fig.yaml' on startup, rather "
"than 'fig.yml'")
log.warning("Please be aware that fig.yml the expected extension "
(candidates, path) = find_candidates_in_parent_dirs(SUPPORTED_FILENAMES, self.base_dir)
if len(candidates) == 0:
raise errors.ComposeFileNotFound(SUPPORTED_FILENAMES)
winner = candidates[0]
if len(candidates) > 1:
log.warning("Found multiple config files with supported names: %s", ", ".join(candidates))
log.warning("Using %s\n", winner)
if winner == 'docker-compose.yaml':
log.warning("Please be aware that .yml is the expected extension "
"in most cases, and using .yaml can cause compatibility "
"issues in future")
"issues in future.\n")
return os.path.join(self.base_dir, 'fig.yaml')
if winner.startswith("fig."):
log.warning("%s is deprecated and will not be supported in future. "
"Please rename your config file to docker-compose.yml\n" % winner)
return os.path.join(self.base_dir, 'fig.yml')
return os.path.join(path, winner)

View File

@@ -11,7 +11,7 @@ def docker_client():
"""
cert_path = os.environ.get('DOCKER_CERT_PATH', '')
if cert_path == '':
cert_path = os.path.join(os.environ.get('HOME'), '.docker')
cert_path = os.path.join(os.environ.get('HOME', ''), '.docker')
base_url = os.environ.get('DOCKER_HOST')
tls_config = None
@@ -31,4 +31,5 @@ def docker_client():
ca_cert=ca_cert,
)
return Client(base_url=base_url, tls=tls_config)
timeout = int(os.environ.get('DOCKER_CLIENT_TIMEOUT', 60))
return Client(base_url=base_url, tls=tls_config, version='1.18', timeout=timeout)

View File

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

View File

@@ -55,8 +55,10 @@ class ConnectionErrorGeneric(UserError):
""" % url)
class FigFileNotFound(UserError):
def __init__(self, filename):
super(FigFileNotFound, self).__init__("""
Can't find %s. Are you in the right directory?
""" % filename)
class ComposeFileNotFound(UserError):
def __init__(self, supported_filenames):
super(ComposeFileNotFound, self).__init__("""
Can't find a suitable configuration file in this directory or any parent. Are you in the right directory?
Supported filenames: %s
""" % ", ".join(supported_filenames))

View File

@@ -39,11 +39,14 @@ class LogPrinter(object):
color_fns = cycle(colors.rainbow())
generators = []
def no_color(text):
return text
for container in self.containers:
if monochrome:
color_fn = lambda s: s
color_fn = no_color
else:
color_fn = color_fns.next()
color_fn = next(color_fns)
generators.append(self._make_log_generator(container, color_fn))
return generators

View File

@@ -1,25 +1,25 @@
from __future__ import print_function
from __future__ import unicode_literals
from inspect import getdoc
from operator import attrgetter
import logging
import sys
import re
import signal
from operator import attrgetter
from inspect import getdoc
from fig.packages import dockerpty
from .. import __version__
from ..project import NoSuchService, ConfigurationError
from ..service import BuildError, CannotBeScaledError
from .command import Command
from .formatter import Formatter
from .log_printer import LogPrinter
from .utils import yesno
import sys
from docker.errors import APIError
from .errors import UserError
import dockerpty
from .. import legacy
from ..project import NoSuchService, ConfigurationError
from ..service import BuildError, NeedsBuildError
from ..config import parse_environment
from .command import Command
from .docopt_command import NoSuchCommand
from .errors import UserError
from .formatter import Formatter
from .log_printer import LogPrinter
from .utils import get_version_info, yesno
log = logging.getLogger(__name__)
@@ -32,7 +32,7 @@ def main():
except KeyboardInterrupt:
log.error("\nAborting.")
sys.exit(1)
except (UserError, NoSuchService, ConfigurationError) as e:
except (UserError, NoSuchService, ConfigurationError, legacy.LegacyContainersError) as e:
log.error(e.msg)
sys.exit(1)
except NoSuchCommand as e:
@@ -46,6 +46,9 @@ def main():
except BuildError as e:
log.error("Service '%s' failed to build: %s" % (e.service.name, e.reason))
sys.exit(1)
except NeedsBuildError as e:
log.error("Service '%s' needs to be built, but --no-build was passed." % e.service.name)
sys.exit(1)
def setup_logging():
@@ -68,38 +71,39 @@ def parse_doc_section(name, source):
class TopLevelCommand(Command):
"""Punctual, lightweight development environments using Docker.
"""Define and run multi-container applications with Docker.
Usage:
fig [options] [COMMAND] [ARGS...]
fig -h|--help
docker-compose [options] [COMMAND] [ARGS...]
docker-compose -h|--help
Options:
--verbose Show more output
--version Print version and exit
-f, --file FILE Specify an alternate fig file (default: fig.yml)
-f, --file FILE Specify an alternate compose file (default: docker-compose.yml)
-p, --project-name NAME Specify an alternate project name (default: directory name)
--verbose Show more output
-v, --version Print version and exit
Commands:
build Build or rebuild services
help Get help on a command
kill Kill containers
logs View output from containers
port Print the public port for a port binding
ps List containers
pull Pulls service images
rm Remove stopped containers
run Run a one-off command
scale Set number of containers for a service
start Start services
stop Stop services
restart Restart services
up Create and start containers
build Build or rebuild services
help Get help on a command
kill Kill containers
logs View output from containers
port Print the public port for a port binding
ps List containers
pull Pulls service images
restart Restart services
rm Remove stopped containers
run Run a one-off command
scale Set number of containers for a service
start Start services
stop Stop services
up Create and start containers
migrate-to-labels Recreate containers to add labels
"""
def docopt_options(self):
options = super(TopLevelCommand, self).docopt_options()
options['version'] = "fig %s" % __version__
options['version'] = get_version_info()
return options
def build(self, project, options):
@@ -107,8 +111,8 @@ class TopLevelCommand(Command):
Build or rebuild services.
Services are built once and then tagged as `project_service`,
e.g. `figtest_db`. If you change a service's `Dockerfile` or the
contents of its build directory, you can run `fig build` to rebuild it.
e.g. `composetest_db`. If you change a service's `Dockerfile` or the
contents of its build directory, you can run `docker-compose build` to rebuild it.
Usage: build [options] [SERVICE...]
@@ -124,18 +128,22 @@ class TopLevelCommand(Command):
Usage: help COMMAND
"""
command = options['COMMAND']
if not hasattr(self, command):
raise NoSuchCommand(command, self)
raise SystemExit(getdoc(getattr(self, command)))
handler = self.get_handler(options['COMMAND'])
raise SystemExit(getdoc(handler))
def kill(self, project, options):
"""
Force stop service containers.
Usage: kill [SERVICE...]
Usage: kill [options] [SERVICE...]
Options:
-s SIGNAL SIGNAL to send to the container.
Default signal is SIGKILL.
"""
project.kill(service_names=options['SERVICE'])
signal = options.get('-s', 'SIGKILL')
project.kill(service_names=options['SERVICE'], signal=signal)
def logs(self, project, options):
"""
@@ -159,13 +167,14 @@ class TopLevelCommand(Command):
Usage: port [options] SERVICE PRIVATE_PORT
Options:
--protocol=proto tcp or udp (defaults to tcp)
--protocol=proto tcp or udp [default: tcp]
--index=index index of the container if there are multiple
instances of a service (defaults to 1)
instances of a service [default: 1]
"""
index = int(options.get('--index'))
service = project.get_service(options['SERVICE'])
try:
container = service.get_container(number=options.get('--index') or 1)
container = service.get_container(number=index)
except ValueError as e:
raise UserError(str(e))
print(container.get_local_port(
@@ -232,8 +241,8 @@ class TopLevelCommand(Command):
Usage: rm [options] [SERVICE...]
Options:
--force Don't ask to confirm removal
-v Remove volumes associated with containers
-f, --force Don't ask to confirm removal
-v Remove volumes associated with containers
"""
all_containers = project.containers(service_names=options['SERVICE'], stopped=True)
stopped_containers = [c for c in all_containers if not c.is_running]
@@ -255,34 +264,42 @@ class TopLevelCommand(Command):
For example:
$ fig run web python manage.py shell
$ docker-compose run web python manage.py shell
By default, linked services will be started, unless they are already
running. If you do not want to start linked services, use
`fig run --no-deps SERVICE COMMAND [ARGS...]`.
`docker-compose run --no-deps SERVICE COMMAND [ARGS...]`.
Usage: run [options] [-e KEY=VAL...] SERVICE [COMMAND] [ARGS...]
Options:
-d Detached mode: Run container in the background, print
new container name.
--entrypoint CMD Override the entrypoint of the image.
-e KEY=VAL Set an environment variable (can be used multiple times)
--no-deps Don't start linked services.
--rm Remove container after run. Ignored in detached mode.
-T Disable pseudo-tty allocation. By default `fig run`
allocates a TTY.
--allow-insecure-ssl Allow insecure connections to the docker
registry
-d Detached mode: Run container in the background, print
new container name.
--entrypoint CMD Override the entrypoint of the image.
-e KEY=VAL Set an environment variable (can be used multiple times)
-u, --user="" Run as specified username or uid
--no-deps Don't start linked services.
--rm Remove container after run. Ignored in detached mode.
--service-ports Run command with the service's ports enabled and mapped
to the host.
-T Disable pseudo-tty allocation. By default `docker-compose run`
allocates a TTY.
"""
service = project.get_service(options['SERVICE'])
insecure_registry = options['--allow-insecure-ssl']
if not options['--no-deps']:
deps = service.get_linked_names()
if len(deps) > 0:
project.up(
service_names=deps,
start_links=True,
recreate=False,
start_deps=True,
allow_recreate=False,
insecure_registry=insecure_registry,
)
tty = True
@@ -298,28 +315,38 @@ class TopLevelCommand(Command):
'command': command,
'tty': tty,
'stdin_open': not options['-d'],
'detach': options['-d'],
}
if options['-e']:
for option in options['-e']:
if 'environment' not in service.options:
service.options['environment'] = {}
k, v = option.split('=', 1)
service.options['environment'][k] = v
container_options['environment'] = parse_environment(options['-e'])
if options['--entrypoint']:
container_options['entrypoint'] = options.get('--entrypoint')
container = service.create_container(one_off=True, **container_options)
if options['--rm']:
container_options['restart'] = None
if options['--user']:
container_options['user'] = options.get('--user')
if not options['--service-ports']:
container_options['ports'] = []
container = service.create_container(
quiet=True,
one_off=True,
insecure_registry=insecure_registry,
**container_options
)
if options['-d']:
service.start_container(container, ports=None, one_off=True)
service.start_container(container)
print(container.name)
else:
service.start_container(container, ports=None, one_off=True)
dockerpty.start(project.client, container.id)
dockerpty.start(project.client, container.id, interactive=not options['-T'])
exit_code = container.wait()
if options['--rm']:
log.info("Removing %s..." % container.name)
project.client.remove_container(container.id)
sys.exit(exit_code)
@@ -330,7 +357,7 @@ class TopLevelCommand(Command):
Numbers are specified in the form `service=num` as arguments.
For example:
$ fig scale web=2 worker=3
$ docker-compose scale web=2 worker=3
Usage: scale [SERVICE=NUM...]
"""
@@ -343,15 +370,7 @@ class TopLevelCommand(Command):
except ValueError:
raise UserError('Number of containers for service "%s" is not a '
'number' % service_name)
try:
project.get_service(service_name).scale(num)
except CannotBeScaledError:
raise UserError(
'Service "%s" cannot be scaled because it specifies a port '
'on the host. If multiple containers for this service were '
'created, the port would clash.\n\nRemove the ":" from the '
'port definition in fig.yml so Docker can choose a random '
'port for each container.' % service_name)
project.get_service(service_name).scale(num)
def start(self, project, options):
"""
@@ -365,55 +384,80 @@ class TopLevelCommand(Command):
"""
Stop running containers without removing them.
They can be started again with `fig start`.
They can be started again with `docker-compose start`.
Usage: stop [SERVICE...]
Usage: stop [options] [SERVICE...]
Options:
-t, --timeout TIMEOUT Specify a shutdown timeout in seconds.
(default: 10)
"""
project.stop(service_names=options['SERVICE'])
timeout = options.get('--timeout')
params = {} if timeout is None else {'timeout': int(timeout)}
project.stop(service_names=options['SERVICE'], **params)
def restart(self, project, options):
"""
Restart running containers.
Usage: restart [SERVICE...]
Usage: restart [options] [SERVICE...]
Options:
-t, --timeout TIMEOUT Specify a shutdown timeout in seconds.
(default: 10)
"""
project.restart(service_names=options['SERVICE'])
timeout = options.get('--timeout')
params = {} if timeout is None else {'timeout': int(timeout)}
project.restart(service_names=options['SERVICE'], **params)
def up(self, project, options):
"""
Build, (re)create, start and attach to containers for a service.
By default, `fig up` will aggregate the output of each container, and
when it exits, all containers will be stopped. If you run `fig up -d`,
By default, `docker-compose up` will aggregate the output of each container, and
when it exits, all containers will be stopped. If you run `docker-compose up -d`,
it'll start the containers in the background and leave them running.
If there are existing containers for a service, `fig up` will stop
If there are existing containers for a service, `docker-compose up` will stop
and recreate them (preserving mounted volumes with volumes-from),
so that changes in `fig.yml` are picked up. If you do not want existing
containers to be recreated, `fig up --no-recreate` will re-use existing
so that changes in `docker-compose.yml` are picked up. If you do not want existing
containers to be recreated, `docker-compose up --no-recreate` will re-use existing
containers.
Usage: up [options] [SERVICE...]
Options:
-d Detached mode: Run containers in the background,
print new container names.
--no-color Produce monochrome output.
--no-deps Don't start linked services.
--no-recreate If containers already exist, don't recreate them.
--allow-insecure-ssl Allow insecure connections to the docker
registry
-d Detached mode: Run containers in the background,
print new container names.
--no-color Produce monochrome output.
--no-deps Don't start linked services.
--x-smart-recreate Only recreate containers whose configuration or
image needs to be updated. (EXPERIMENTAL)
--no-recreate If containers already exist, don't recreate them.
--no-build Don't build an image, even if it's missing
-t, --timeout TIMEOUT When attached, use this timeout in seconds
for the shutdown. (default: 10)
"""
insecure_registry = options['--allow-insecure-ssl']
detached = options['-d']
monochrome = options['--no-color']
start_links = not options['--no-deps']
recreate = not options['--no-recreate']
start_deps = not options['--no-deps']
allow_recreate = not options['--no-recreate']
smart_recreate = options['--x-smart-recreate']
service_names = options['SERVICE']
project.up(
service_names=service_names,
start_links=start_links,
recreate=recreate
start_deps=start_deps,
allow_recreate=allow_recreate,
smart_recreate=smart_recreate,
insecure_registry=insecure_registry,
do_build=not options['--no-build'],
)
to_attach = [c for s in project.get_services(service_names) for c in s.containers()]
@@ -431,7 +475,35 @@ class TopLevelCommand(Command):
signal.signal(signal.SIGINT, handler)
print("Gracefully stopping... (press Ctrl+C again to force)")
project.stop(service_names=service_names)
timeout = options.get('--timeout')
params = {} if timeout is None else {'timeout': int(timeout)}
project.stop(service_names=service_names, **params)
def migrate_to_labels(self, project, _options):
"""
Recreate containers to add labels
If you're coming from Compose 1.2 or earlier, you'll need to remove or
migrate your existing containers after upgrading Compose. This is
because, as of version 1.3, Compose uses Docker labels to keep track
of containers, and so they need to be recreated with labels added.
If Compose detects containers that were created without labels, it
will refuse to run so that you don't end up with two sets of them. If
you want to keep using your existing containers (for example, because
they have data volumes you want to preserve) you can migrate them with
the following command:
docker-compose migrate-to-labels
Alternatively, if you're not worried about keeping them, you can
remove them - Compose will just create new ones.
docker rm -f myapp_web_1 myapp_db_1 ...
Usage: migrate-to-labels
"""
legacy.migrate_project_to_labels(project)
def list_containers(containers):

View File

@@ -5,6 +5,9 @@ import datetime
import os
import subprocess
import platform
import ssl
from .. import __version__
def yesno(prompt, default=None):
@@ -62,6 +65,25 @@ def mkdir(path, permissions=0o700):
return path
def find_candidates_in_parent_dirs(filenames, path):
"""
Given a directory path to start, looks for filenames in the
directory, and then each parent directory successively,
until found.
Returns tuple (candidates, path).
"""
candidates = [filename for filename in filenames
if os.path.exists(os.path.join(path, filename))]
if len(candidates) == 0:
parent_dir = os.path.join(path, '..')
if os.path.abspath(parent_dir) != os.path.abspath(path):
return find_candidates_in_parent_dirs(filenames, parent_dir)
return (candidates, path)
def split_buffer(reader, separator):
"""
Given a generator which yields strings and a separator string,
@@ -101,3 +123,11 @@ def is_mac():
def is_ubuntu():
return platform.system() == 'Linux' and platform.linux_distribution()[0] == 'Ubuntu'
def get_version_info():
return '\n'.join([
'docker-compose version: %s' % __version__,
"%s version: %s" % (platform.python_implementation(), platform.python_version()),
"OpenSSL version: %s" % ssl.OPENSSL_VERSION,
])

489
compose/config.py Normal file
View File

@@ -0,0 +1,489 @@
import os
import yaml
import six
DOCKER_CONFIG_KEYS = [
'cap_add',
'cap_drop',
'cpu_shares',
'cpuset',
'command',
'detach',
'devices',
'dns',
'dns_search',
'domainname',
'entrypoint',
'env_file',
'environment',
'extra_hosts',
'read_only',
'hostname',
'image',
'labels',
'links',
'mem_limit',
'net',
'log_driver',
'pid',
'ports',
'privileged',
'restart',
'security_opt',
'stdin_open',
'tty',
'user',
'volumes',
'volumes_from',
'working_dir',
]
ALLOWED_KEYS = DOCKER_CONFIG_KEYS + [
'build',
'dockerfile',
'expose',
'external_links',
'name',
]
DOCKER_CONFIG_HINTS = {
'cpu_share': 'cpu_shares',
'add_host': 'extra_hosts',
'hosts': 'extra_hosts',
'extra_host': 'extra_hosts',
'device': 'devices',
'link': 'links',
'port': 'ports',
'privilege': 'privileged',
'priviliged': 'privileged',
'privilige': 'privileged',
'volume': 'volumes',
'workdir': 'working_dir',
}
def load(filename):
working_dir = os.path.dirname(filename)
return from_dictionary(load_yaml(filename), working_dir=working_dir, filename=filename)
def from_dictionary(dictionary, working_dir=None, filename=None):
service_dicts = []
for service_name, service_dict in list(dictionary.items()):
if not isinstance(service_dict, dict):
raise ConfigurationError('Service "%s" doesn\'t have any configuration options. All top level keys in your docker-compose.yml must map to a dictionary of configuration options.' % service_name)
loader = ServiceLoader(working_dir=working_dir, filename=filename)
service_dict = loader.make_service_dict(service_name, service_dict)
validate_paths(service_dict)
service_dicts.append(service_dict)
return service_dicts
def make_service_dict(name, service_dict, working_dir=None):
return ServiceLoader(working_dir=working_dir).make_service_dict(name, service_dict)
class ServiceLoader(object):
def __init__(self, working_dir, filename=None, already_seen=None):
self.working_dir = working_dir
self.filename = filename
self.already_seen = already_seen or []
def make_service_dict(self, name, service_dict):
if self.signature(name) in self.already_seen:
raise CircularReference(self.already_seen)
service_dict = service_dict.copy()
service_dict['name'] = name
service_dict = resolve_environment(service_dict, working_dir=self.working_dir)
service_dict = self.resolve_extends(service_dict)
return process_container_options(service_dict, working_dir=self.working_dir)
def resolve_extends(self, service_dict):
if 'extends' not in service_dict:
return service_dict
extends_options = process_extends_options(service_dict['name'], service_dict['extends'])
if self.working_dir is None:
raise Exception("No working_dir passed to ServiceLoader()")
other_config_path = expand_path(self.working_dir, extends_options['file'])
other_working_dir = os.path.dirname(other_config_path)
other_already_seen = self.already_seen + [self.signature(service_dict['name'])]
other_loader = ServiceLoader(
working_dir=other_working_dir,
filename=other_config_path,
already_seen=other_already_seen,
)
other_config = load_yaml(other_config_path)
other_service_dict = other_config[extends_options['service']]
other_service_dict = other_loader.make_service_dict(
service_dict['name'],
other_service_dict,
)
validate_extended_service_dict(
other_service_dict,
filename=other_config_path,
service=extends_options['service'],
)
return merge_service_dicts(other_service_dict, service_dict)
def signature(self, name):
return (self.filename, name)
def process_extends_options(service_name, extends_options):
error_prefix = "Invalid 'extends' configuration for %s:" % service_name
if not isinstance(extends_options, dict):
raise ConfigurationError("%s must be a dictionary" % error_prefix)
if 'service' not in extends_options:
raise ConfigurationError(
"%s you need to specify a service, e.g. 'service: web'" % error_prefix
)
for k, _ in extends_options.items():
if k not in ['file', 'service']:
raise ConfigurationError(
"%s unsupported configuration option '%s'" % (error_prefix, k)
)
return extends_options
def validate_extended_service_dict(service_dict, filename, service):
error_prefix = "Cannot extend service '%s' in %s:" % (service, filename)
if 'links' in service_dict:
raise ConfigurationError("%s services with 'links' cannot be extended" % error_prefix)
if 'volumes_from' in service_dict:
raise ConfigurationError("%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:
raise ConfigurationError("%s services with 'net: container' cannot be extended" % error_prefix)
def process_container_options(service_dict, working_dir=None):
for k in service_dict:
if k not in ALLOWED_KEYS:
msg = "Unsupported config option for %s service: '%s'" % (service_dict['name'], k)
if k in DOCKER_CONFIG_HINTS:
msg += " (did you mean '%s'?)" % DOCKER_CONFIG_HINTS[k]
raise ConfigurationError(msg)
service_dict = service_dict.copy()
if 'volumes' in service_dict:
service_dict['volumes'] = resolve_host_paths(service_dict['volumes'], working_dir=working_dir)
if 'build' in service_dict:
service_dict['build'] = resolve_build_path(service_dict['build'], working_dir=working_dir)
if 'labels' in service_dict:
service_dict['labels'] = parse_labels(service_dict['labels'])
return service_dict
def merge_service_dicts(base, override):
d = base.copy()
if 'environment' in base or 'environment' in override:
d['environment'] = merge_environment(
base.get('environment'),
override.get('environment'),
)
path_mapping_keys = ['volumes', 'devices']
for key in path_mapping_keys:
if key in base or key in override:
d[key] = merge_path_mappings(
base.get(key),
override.get(key),
)
if 'labels' in base or 'labels' in override:
d['labels'] = merge_labels(
base.get('labels'),
override.get('labels'),
)
if 'image' in override and 'build' in d:
del d['build']
if 'build' in override and 'image' in d:
del d['image']
list_keys = ['ports', 'expose', 'external_links']
for key in list_keys:
if key in base or key in override:
d[key] = base.get(key, []) + override.get(key, [])
list_or_string_keys = ['dns', 'dns_search']
for key in list_or_string_keys:
if key in base or key in override:
d[key] = to_list(base.get(key)) + to_list(override.get(key))
already_merged_keys = ['environment', 'labels'] + path_mapping_keys + list_keys + list_or_string_keys
for k in set(ALLOWED_KEYS) - set(already_merged_keys):
if k in override:
d[k] = override[k]
return d
def merge_environment(base, override):
env = parse_environment(base)
env.update(parse_environment(override))
return env
def parse_links(links):
return dict(parse_link(l) for l in links)
def parse_link(link):
if ':' in link:
source, alias = link.split(':', 1)
return (alias, source)
else:
return (link, link)
def get_env_files(options, working_dir=None):
if 'env_file' not in options:
return {}
if working_dir is None:
raise Exception("No working_dir passed to get_env_files()")
env_files = options.get('env_file', [])
if not isinstance(env_files, list):
env_files = [env_files]
return [expand_path(working_dir, path) for path in env_files]
def resolve_environment(service_dict, working_dir=None):
service_dict = service_dict.copy()
if 'environment' not in service_dict and 'env_file' not in service_dict:
return service_dict
env = {}
if 'env_file' in service_dict:
for f in get_env_files(service_dict, working_dir=working_dir):
env.update(env_vars_from_file(f))
del service_dict['env_file']
env.update(parse_environment(service_dict.get('environment')))
env = dict(resolve_env_var(k, v) for k, v in six.iteritems(env))
service_dict['environment'] = env
return service_dict
def parse_environment(environment):
if not environment:
return {}
if isinstance(environment, list):
return dict(split_env(e) for e in environment)
if isinstance(environment, dict):
return environment
raise ConfigurationError(
"environment \"%s\" must be a list or mapping," %
environment
)
def split_env(env):
if '=' in env:
return env.split('=', 1)
else:
return env, None
def resolve_env_var(key, val):
if val is not None:
return key, val
elif key in os.environ:
return key, os.environ[key]
else:
return key, ''
def env_vars_from_file(filename):
"""
Read in a line delimited file of environment variables.
"""
if not os.path.exists(filename):
raise ConfigurationError("Couldn't find env file: %s" % filename)
env = {}
for line in open(filename, 'r'):
line = line.strip()
if line and not line.startswith('#'):
k, v = split_env(line)
env[k] = v
return env
def resolve_host_paths(volumes, working_dir=None):
if working_dir is None:
raise Exception("No working_dir passed to resolve_host_paths()")
return [resolve_host_path(v, working_dir) for v in volumes]
def resolve_host_path(volume, working_dir):
container_path, host_path = split_path_mapping(volume)
if host_path is not None:
host_path = os.path.expanduser(host_path)
host_path = os.path.expandvars(host_path)
return "%s:%s" % (expand_path(working_dir, host_path), container_path)
else:
return container_path
def resolve_build_path(build_path, working_dir=None):
if working_dir is None:
raise Exception("No working_dir passed to resolve_build_path")
return expand_path(working_dir, build_path)
def validate_paths(service_dict):
if 'build' in service_dict:
build_path = service_dict['build']
if not os.path.exists(build_path) or not os.access(build_path, os.R_OK):
raise ConfigurationError("build path %s either does not exist or is not accessible." % build_path)
def merge_path_mappings(base, override):
d = dict_from_path_mappings(base)
d.update(dict_from_path_mappings(override))
return path_mappings_from_dict(d)
def dict_from_path_mappings(path_mappings):
if path_mappings:
return dict(split_path_mapping(v) for v in path_mappings)
else:
return {}
def path_mappings_from_dict(d):
return [join_path_mapping(v) for v in d.items()]
def split_path_mapping(string):
if ':' in string:
(host, container) = string.split(':', 1)
return (container, host)
else:
return (string, None)
def join_path_mapping(pair):
(container, host) = pair
if host is None:
return container
else:
return ":".join((host, container))
def merge_labels(base, override):
labels = parse_labels(base)
labels.update(parse_labels(override))
return labels
def parse_labels(labels):
if not labels:
return {}
if isinstance(labels, list):
return dict(split_label(e) for e in labels)
if isinstance(labels, dict):
return labels
raise ConfigurationError(
"labels \"%s\" must be a list or mapping" %
labels
)
def split_label(label):
if '=' in label:
return label.split('=', 1)
else:
return label, ''
def expand_path(working_dir, path):
return os.path.abspath(os.path.join(working_dir, path))
def to_list(value):
if value is None:
return []
elif isinstance(value, six.string_types):
return [value]
else:
return value
def get_service_name_from_net(net_config):
if not net_config:
return
if not net_config.startswith('container:'):
return
_, net_name = net_config.split(':', 1)
return net_name
def load_yaml(filename):
try:
with open(filename, 'r') as fh:
return yaml.safe_load(fh)
except IOError as e:
raise ConfigurationError(six.text_type(e))
class ConfigurationError(Exception):
def __init__(self, msg):
self.msg = msg
def __str__(self):
return self.msg
class CircularReference(ConfigurationError):
def __init__(self, trail):
self.trail = trail
@property
def msg(self):
lines = [
"{} in {}".format(service_name, filename)
for (filename, service_name) in self.trail
]
return "Circular reference:\n {}".format("\n extends ".join(lines))

7
compose/const.py Normal file
View File

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

View File

@@ -2,6 +2,9 @@ from __future__ import unicode_literals
from __future__ import absolute_import
import six
from functools import reduce
from .const import LABEL_CONTAINER_NUMBER, LABEL_SERVICE
class Container(object):
@@ -22,10 +25,8 @@ class Container(object):
new_dictionary = {
'Id': dictionary['Id'],
'Image': dictionary['Image'],
'Name': '/' + get_container_name(dictionary),
}
for name in dictionary.get('Names', []):
if len(name.split('/')) == 2:
new_dictionary['Name'] = name
return cls(client, new_dictionary, **kwargs)
@classmethod
@@ -45,6 +46,10 @@ class Container(object):
def image(self):
return self.dictionary['Image']
@property
def image_config(self):
return self.client.inspect_image(self.image)
@property
def short_id(self):
return self.id[:10]
@@ -55,14 +60,15 @@ class Container(object):
@property
def name_without_project(self):
return '_'.join(self.dictionary['Name'].split('_')[1:])
return '{0}_{1}'.format(self.labels.get(LABEL_SERVICE), self.number)
@property
def number(self):
try:
return int(self.name.split('_')[-1])
except ValueError:
return None
number = self.labels.get(LABEL_CONTAINER_NUMBER)
if not number:
raise ValueError("Container {0} does not have a {1} label".format(
self.short_id, LABEL_CONTAINER_NUMBER))
return int(number)
@property
def ports(self):
@@ -80,6 +86,14 @@ class Container(object):
return ', '.join(format_port(*item)
for item in sorted(six.iteritems(self.ports)))
@property
def labels(self):
return self.get('Config.Labels') or {}
@property
def log_config(self):
return self.get('HostConfig.LogConfig') or None
@property
def human_readable_state(self):
if self.is_running:
@@ -124,11 +138,11 @@ class Container(object):
def stop(self, **options):
return self.client.stop(self.id, **options)
def kill(self):
return self.client.kill(self.id)
def kill(self, **options):
return self.client.kill(self.id, **options)
def restart(self):
return self.client.restart(self.id)
def restart(self, **options):
return self.client.restart(self.id, **options)
def remove(self, **options):
return self.client.remove_container(self.id, **options)
@@ -148,6 +162,7 @@ class Container(object):
self.has_been_inspected = True
return self.dictionary
# TODO: only used by tests, move to test module
def links(self):
links = []
for container in self.client.containers():
@@ -164,9 +179,23 @@ class Container(object):
return self.client.attach_socket(self.id, **kwargs)
def __repr__(self):
return '<Container: %s>' % self.name
return '<Container: %s (%s)>' % (self.name, self.id[:6])
def __eq__(self, other):
if type(self) != type(other):
return False
return self.id == other.id
def __hash__(self):
return self.id.__hash__()
def get_container_name(container):
if not container.get('Name') and not container.get('Names'):
return None
# inspect
if 'Name' in container:
return container['Name']
# ps
shortest_name = min(container['Names'], key=lambda n: len(n.split('/')))
return shortest_name.split('/')[-1]

122
compose/legacy.py Normal file
View File

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

View File

@@ -19,7 +19,9 @@ def stream_output(output, stream):
all_events.append(event)
if 'progress' in event or 'progressDetail' in event:
image_id = event['id']
image_id = event.get('id')
if not image_id:
continue
if image_id in lines:
diff = len(lines) - lines[image_id]
@@ -72,8 +74,9 @@ def print_output_event(event, stream, is_terminal):
stream.write("%s %s%s" % (status, event['progress'], terminator))
elif 'progressDetail' in event:
detail = event['progressDetail']
if 'current' in detail:
percentage = float(detail['current']) / float(detail['total']) * 100
total = detail.get('total')
if 'current' in detail and total:
percentage = float(detail['current']) / float(total) * 100
stream.write('%s (%.1f%%)%s' % (status, percentage, terminator))
else:
stream.write('%s%s' % (status, terminator))

View File

@@ -1,10 +1,15 @@
from __future__ import unicode_literals
from __future__ import absolute_import
import logging
from functools import reduce
from docker.errors import APIError
from .config import get_service_name_from_net, ConfigurationError
from .const import LABEL_PROJECT, LABEL_SERVICE, LABEL_ONE_OFF
from .service import Service
from .container import Container
from docker.errors import APIError
from .legacy import check_for_legacy_containers
log = logging.getLogger(__name__)
@@ -15,7 +20,17 @@ def sort_service_dicts(services):
temporary_marked = set()
sorted_services = []
get_service_names = lambda links: [link.split(':')[0] for link in links]
def get_service_names(links):
return [link.split(':')[0] for link in links]
def get_service_dependents(service_dict, services):
name = service_dict['name']
return [
service for service in services
if (name in get_service_names(service.get('links', [])) or
name in service.get('volumes_from', []) or
name == get_service_name_from_net(service.get('net')))
]
def visit(n):
if n['name'] in temporary_marked:
@@ -27,8 +42,7 @@ def sort_service_dicts(services):
raise DependencyError('Circular import between %s' % ' and '.join(temporary_marked))
if n in unmarked:
temporary_marked.add(n['name'])
dependents = [m for m in services if (n['name'] in get_service_names(m.get('links', []))) or (n['name'] in m.get('volumes_from', []))]
for m in dependents:
for m in get_service_dependents(n, services):
visit(m)
temporary_marked.remove(n['name'])
unmarked.remove(n)
@@ -49,6 +63,12 @@ class Project(object):
self.services = services
self.client = client
def labels(self, one_off=False):
return [
'{0}={1}'.format(LABEL_PROJECT, self.name),
'{0}={1}'.format(LABEL_ONE_OFF, "True" if one_off else "False"),
]
@classmethod
def from_dicts(cls, name, service_dicts, client):
"""
@@ -58,19 +78,15 @@ class Project(object):
for service_dict in sort_service_dicts(service_dicts):
links = project.get_links(service_dict)
volumes_from = project.get_volumes_from(service_dict)
net = project.get_net(service_dict)
project.services.append(Service(client=client, project=name, links=links, volumes_from=volumes_from, **service_dict))
project.services.append(Service(client=client, project=name, links=links, net=net,
volumes_from=volumes_from, **service_dict))
return project
@classmethod
def from_config(cls, name, config, client):
dicts = []
for service_name, service in list(config.items()):
if not isinstance(service, dict):
raise ConfigurationError('Service "%s" doesn\'t have any configuration options. All top level keys in your fig.yml must map to a dictionary of configuration options.')
service['name'] = service_name
dicts.append(service)
return cls.from_dicts(name, dicts, client)
@property
def service_names(self):
return [service.name for service in self.services]
def get_service(self, name):
"""
@@ -83,31 +99,31 @@ class Project(object):
raise NoSuchService(name)
def get_services(self, service_names=None, include_links=False):
def get_services(self, service_names=None, include_deps=False):
"""
Returns a list of this project's services filtered
by the provided list of names, or all services if service_names is None
or [].
If include_links is specified, returns a list including the links for
If include_deps is specified, returns a list including the dependencies for
service_names, in order of dependency.
Preserves the original order of self.services where possible,
reordering as needed to resolve links.
reordering as needed to resolve dependencies.
Raises NoSuchService if any of the named services do not exist.
"""
if service_names is None or len(service_names) == 0:
return self.get_services(
service_names=[s.name for s in self.services],
include_links=include_links
service_names=self.service_names,
include_deps=include_deps
)
else:
unsorted = [self.get_service(name) for name in service_names]
services = [s for s in self.services if s in unsorted]
if include_links:
services = reduce(self._inject_links, services, [])
if include_deps:
services = reduce(self._inject_deps, services, [])
uniques = []
[uniques.append(s) for s in services if s not in uniques]
@@ -144,6 +160,28 @@ class Project(object):
del service_dict['volumes_from']
return volumes_from
def get_net(self, service_dict):
if 'net' in service_dict:
net_name = get_service_name_from_net(service_dict.get('net'))
if net_name:
try:
net = self.get_service(net_name)
except NoSuchService:
try:
net = Container.from_id(self.client, net_name)
except APIError:
raise ConfigurationError('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))
else:
net = service_dict['net']
del service_dict['net']
else:
net = None
return net
def start(self, service_names=None, **options):
for service in self.get_services(service_names):
service.start(**options)
@@ -167,21 +205,68 @@ class Project(object):
else:
log.info('%s uses an image, skipping' % service.name)
def up(self, service_names=None, start_links=True, recreate=True):
running_containers = []
def up(self,
service_names=None,
start_deps=True,
allow_recreate=True,
smart_recreate=False,
insecure_registry=False,
do_build=True):
for service in self.get_services(service_names, include_links=start_links):
if recreate:
for (_, container) in service.recreate_containers():
running_containers.append(container)
services = self.get_services(service_names, include_deps=start_deps)
plans = self._get_convergence_plans(
services,
allow_recreate=allow_recreate,
smart_recreate=smart_recreate,
)
return [
container
for service in services
for container in service.execute_convergence_plan(
plans[service.name],
insecure_registry=insecure_registry,
do_build=do_build,
)
]
def _get_convergence_plans(self,
services,
allow_recreate=True,
smart_recreate=False):
plans = {}
for service in services:
updated_dependencies = [
name
for name in service.get_dependency_names()
if name in plans
and plans[name].action == 'recreate'
]
if updated_dependencies:
log.debug(
'%s has upstream changes (%s)',
service.name, ", ".join(updated_dependencies),
)
plan = service.convergence_plan(
allow_recreate=allow_recreate,
smart_recreate=False,
)
else:
for container in service.start_or_create_containers():
running_containers.append(container)
plan = service.convergence_plan(
allow_recreate=allow_recreate,
smart_recreate=smart_recreate,
)
return running_containers
plans[service.name] = plan
return plans
def pull(self, service_names=None, insecure_registry=False):
for service in self.get_services(service_names, include_links=True):
for service in self.get_services(service_names, include_deps=True):
service.pull(insecure_registry=insecure_registry)
def remove_stopped(self, service_names=None, **options):
@@ -189,24 +274,40 @@ class Project(object):
service.remove_stopped(**options)
def containers(self, service_names=None, stopped=False, one_off=False):
return [Container.from_ps(self.client, container)
for container in self.client.containers(all=stopped)
for service in self.get_services(service_names)
if service.has_container(container, one_off=one_off)]
containers = [
Container.from_ps(self.client, container)
for container in self.client.containers(
all=stopped,
filters={'label': self.labels(one_off=one_off)})]
def _inject_links(self, acc, service):
linked_names = service.get_linked_names()
def matches_service_names(container):
if not service_names:
return True
return container.labels.get(LABEL_SERVICE) in service_names
if len(linked_names) > 0:
linked_services = self.get_services(
service_names=linked_names,
include_links=True
if not containers:
check_for_legacy_containers(
self.client,
self.name,
self.service_names,
stopped=stopped,
one_off=one_off)
return filter(matches_service_names, containers)
def _inject_deps(self, acc, service):
dep_names = service.get_dependency_names()
if len(dep_names) > 0:
dep_services = self.get_services(
service_names=list(set(dep_names)),
include_deps=True
)
else:
linked_services = []
dep_services = []
linked_services.append(service)
return acc + linked_services
dep_services.append(service)
return acc + dep_services
class NoSuchService(Exception):
@@ -218,13 +319,5 @@ class NoSuchService(Exception):
return self.msg
class ConfigurationError(Exception):
def __init__(self, msg):
self.msg = msg
def __str__(self):
return self.msg
class DependencyError(ConfigurationError):
pass

876
compose/service.py Normal file
View File

@@ -0,0 +1,876 @@
from __future__ import unicode_literals
from __future__ import absolute_import
from collections import namedtuple
import logging
import re
import sys
from operator import attrgetter
import six
from docker.errors import APIError
from docker.utils import create_host_config, LogConfig
from . import __version__
from .config import DOCKER_CONFIG_KEYS, merge_environment
from .const import (
LABEL_CONTAINER_NUMBER,
LABEL_ONE_OFF,
LABEL_PROJECT,
LABEL_SERVICE,
LABEL_VERSION,
LABEL_CONFIG_HASH,
)
from .container import Container
from .legacy import check_for_legacy_containers
from .progress_stream import stream_output, StreamOutputError
from .utils import json_hash
log = logging.getLogger(__name__)
DOCKER_START_KEYS = [
'cap_add',
'cap_drop',
'devices',
'dns',
'dns_search',
'env_file',
'extra_hosts',
'read_only',
'net',
'log_driver',
'pid',
'privileged',
'restart',
'volumes_from',
'security_opt',
]
VALID_NAME_CHARS = '[a-zA-Z0-9]'
class BuildError(Exception):
def __init__(self, service, reason):
self.service = service
self.reason = reason
class ConfigError(ValueError):
pass
class NeedsBuildError(Exception):
def __init__(self, service):
self.service = service
VolumeSpec = namedtuple('VolumeSpec', 'external internal mode')
ServiceName = namedtuple('ServiceName', 'project service number')
ConvergencePlan = namedtuple('ConvergencePlan', 'action containers')
class Service(object):
def __init__(self, name, client=None, project='default', links=None, external_links=None, volumes_from=None, net=None, **options):
if not re.match('^%s+$' % VALID_NAME_CHARS, name):
raise ConfigError('Invalid service name "%s" - only %s are allowed' % (name, VALID_NAME_CHARS))
if not re.match('^%s+$' % VALID_NAME_CHARS, project):
raise ConfigError('Invalid project name "%s" - only %s are allowed' % (project, VALID_NAME_CHARS))
if 'image' in options and 'build' in options:
raise ConfigError('Service %s has both an image and build path specified. A service can either be built to image or use an existing image, not both.' % name)
if 'image' not in options and 'build' not in options:
raise ConfigError('Service %s has neither an image nor a build path specified. Exactly one must be provided.' % name)
self.name = name
self.client = client
self.project = project
self.links = links or []
self.external_links = external_links or []
self.volumes_from = volumes_from or []
self.net = net or None
self.options = options
def containers(self, stopped=False, one_off=False):
containers = [
Container.from_ps(self.client, container)
for container in self.client.containers(
all=stopped,
filters={'label': self.labels(one_off=one_off)})]
if not containers:
check_for_legacy_containers(
self.client,
self.project,
[self.name],
stopped=stopped,
one_off=one_off)
return containers
def get_container(self, number=1):
"""Return a :class:`compose.container.Container` for this service. The
container must be active, and match `number`.
"""
labels = self.labels() + ['{0}={1}'.format(LABEL_CONTAINER_NUMBER, number)]
for container in self.client.containers(filters={'label': labels}):
return Container.from_ps(self.client, container)
raise ValueError("No container found for %s_%s" % (self.name, number))
def start(self, **options):
for c in self.containers(stopped=True):
self.start_container_if_stopped(c, **options)
def stop(self, **options):
for c in self.containers():
log.info("Stopping %s..." % c.name)
c.stop(**options)
def kill(self, **options):
for c in self.containers():
log.info("Killing %s..." % c.name)
c.kill(**options)
def restart(self, **options):
for c in self.containers():
log.info("Restarting %s..." % c.name)
c.restart(**options)
def scale(self, desired_num):
"""
Adjusts the number of containers to the specified number and ensures
they are running.
- creates containers until there are at least `desired_num`
- stops containers until there are at most `desired_num` running
- starts containers until there are at least `desired_num` running
- removes all stopped containers
"""
if not self.can_be_scaled():
log.warn('Service %s specifies a port on the host. If multiple containers '
'for this service are created on a single host, the port will clash.'
% self.name)
# Create enough containers
containers = self.containers(stopped=True)
while len(containers) < desired_num:
containers.append(self.create_container())
running_containers = []
stopped_containers = []
for c in containers:
if c.is_running:
running_containers.append(c)
else:
stopped_containers.append(c)
running_containers.sort(key=lambda c: c.number)
stopped_containers.sort(key=lambda c: c.number)
# Stop containers
while len(running_containers) > desired_num:
c = running_containers.pop()
log.info("Stopping %s..." % c.name)
c.stop(timeout=1)
stopped_containers.append(c)
# Start containers
while len(running_containers) < desired_num:
c = stopped_containers.pop(0)
log.info("Starting %s..." % c.name)
self.start_container(c)
running_containers.append(c)
self.remove_stopped()
def remove_stopped(self, **options):
for c in self.containers(stopped=True):
if not c.is_running:
log.info("Removing %s..." % c.name)
c.remove(**options)
def create_container(self,
one_off=False,
insecure_registry=False,
do_build=True,
previous_container=None,
number=None,
quiet=False,
**override_options):
"""
Create a container for this service. If the image doesn't exist, attempt to pull
it.
"""
self.ensure_image_exists(
do_build=do_build,
insecure_registry=insecure_registry,
)
container_options = self._get_container_create_options(
override_options,
number or self._next_container_number(one_off=one_off),
one_off=one_off,
previous_container=previous_container,
)
if 'name' in container_options and not quiet:
log.info("Creating %s..." % container_options['name'])
return Container.create(self.client, **container_options)
def ensure_image_exists(self,
do_build=True,
insecure_registry=False):
if self.image():
return
if self.can_be_built():
if do_build:
self.build()
else:
raise NeedsBuildError(self)
else:
self.pull(insecure_registry=insecure_registry)
def image(self):
try:
return self.client.inspect_image(self.image_name)
except APIError as e:
if e.response.status_code == 404 and e.explanation and 'No such image' in str(e.explanation):
return None
else:
raise
@property
def image_name(self):
if self.can_be_built():
return self.full_name
else:
return self.options['image']
def converge(self,
allow_recreate=True,
smart_recreate=False,
insecure_registry=False,
do_build=True):
"""
If a container for this service doesn't exist, create and start one. If there are
any, stop them, create+start new ones, and remove the old containers.
"""
plan = self.convergence_plan(
allow_recreate=allow_recreate,
smart_recreate=smart_recreate,
)
return self.execute_convergence_plan(
plan,
insecure_registry=insecure_registry,
do_build=do_build,
)
def convergence_plan(self,
allow_recreate=True,
smart_recreate=False):
containers = self.containers(stopped=True)
if not containers:
return ConvergencePlan('create', [])
if smart_recreate and not self._containers_have_diverged(containers):
stopped = [c for c in containers if not c.is_running]
if stopped:
return ConvergencePlan('start', stopped)
return ConvergencePlan('noop', containers)
if not allow_recreate:
return ConvergencePlan('start', containers)
return ConvergencePlan('recreate', containers)
def _containers_have_diverged(self, containers):
config_hash = self.config_hash()
has_diverged = False
for c in containers:
container_config_hash = c.labels.get(LABEL_CONFIG_HASH, None)
if container_config_hash != config_hash:
log.debug(
'%s has diverged: %s != %s',
c.name, container_config_hash, config_hash,
)
has_diverged = True
return has_diverged
def execute_convergence_plan(self,
plan,
insecure_registry=False,
do_build=True):
(action, containers) = plan
if action == 'create':
container = self.create_container(
insecure_registry=insecure_registry,
do_build=do_build,
)
self.start_container(container)
return [container]
elif action == 'recreate':
return [
self.recreate_container(
c,
insecure_registry=insecure_registry,
)
for c in containers
]
elif action == 'start':
for c in containers:
self.start_container_if_stopped(c)
return containers
elif action == 'noop':
for c in containers:
log.info("%s is up-to-date" % c.name)
return containers
else:
raise Exception("Invalid action: {}".format(action))
def recreate_container(self,
container,
insecure_registry=False):
"""Recreate a container.
The original container is renamed to a temporary name so that data
volumes can be copied to the new container, before the original
container is removed.
"""
log.info("Recreating %s..." % container.name)
try:
container.stop()
except APIError as e:
if (e.response.status_code == 500
and e.explanation
and 'no such process' in str(e.explanation)):
pass
else:
raise
# Use a hopefully unique container name by prepending the short id
self.client.rename(
container.id,
'%s_%s' % (container.short_id, container.name))
new_container = self.create_container(
insecure_registry=insecure_registry,
do_build=False,
previous_container=container,
number=container.labels.get(LABEL_CONTAINER_NUMBER),
quiet=True,
)
self.start_container(new_container)
container.remove()
return new_container
def start_container_if_stopped(self, container):
if container.is_running:
return container
else:
log.info("Starting %s..." % container.name)
return self.start_container(container)
def start_container(self, container):
container.start()
return container
def config_hash(self):
return json_hash(self.config_dict())
def config_dict(self):
return {
'options': self.options,
'image_id': self.image()['Id'],
}
def get_dependency_names(self):
net_name = self.get_net_name()
return (self.get_linked_names() +
self.get_volumes_from_names() +
([net_name] if net_name else []))
def get_linked_names(self):
return [s.name for (s, _) in self.links]
def get_volumes_from_names(self):
return [s.name for s in self.volumes_from if isinstance(s, Service)]
def get_net_name(self):
if isinstance(self.net, Service):
return self.net.name
else:
return
def get_container_name(self, number, one_off=False):
# TODO: Implement issue #652 here
return build_container_name(self.project, self.name, number, one_off)
# TODO: this would benefit from github.com/docker/docker/pull/11943
# to remove the need to inspect every container
def _next_container_number(self, one_off=False):
numbers = [
Container.from_ps(self.client, container).number
for container in self.client.containers(
all=True,
filters={'label': self.labels(one_off=one_off)})
]
return 1 if not numbers else max(numbers) + 1
def _get_links(self, link_to_self):
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))
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))
for external_link in self.external_links:
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
def _get_volumes_from(self):
volumes_from = []
for volume_source in self.volumes_from:
if isinstance(volume_source, Service):
containers = volume_source.containers(stopped=True)
if not containers:
volumes_from.append(volume_source.create_container().id)
else:
volumes_from.extend(map(attrgetter('id'), containers))
elif isinstance(volume_source, Container):
volumes_from.append(volume_source.id)
return volumes_from
def _get_net(self):
if not self.net:
return None
if isinstance(self.net, Service):
containers = self.net.containers()
if len(containers) > 0:
net = 'container:' + containers[0].id
else:
log.warning("Warning: Service %s is trying to use reuse the network stack "
"of another service that is not running." % (self.net.name))
net = None
elif isinstance(self.net, Container):
net = 'container:' + self.net.id
else:
net = self.net
return net
def _get_container_create_options(
self,
override_options,
number,
one_off=False,
previous_container=None):
add_config_hash = (not one_off and not override_options)
container_options = dict(
(k, self.options[k])
for k in DOCKER_CONFIG_KEYS if k in self.options)
container_options.update(override_options)
container_options['name'] = self.get_container_name(number, one_off)
if add_config_hash:
config_hash = self.config_hash()
if 'labels' not in container_options:
container_options['labels'] = {}
container_options['labels'][LABEL_CONFIG_HASH] = config_hash
log.debug("Added config hash: %s" % config_hash)
if 'detach' not in container_options:
container_options['detach'] = True
# If a qualified hostname was given, split it into an
# unqualified hostname and a domainname unless domainname
# was also given explicitly. This matches the behavior of
# the official Docker CLI in that scenario.
if ('hostname' in container_options
and 'domainname' not in container_options
and '.' in container_options['hostname']):
parts = container_options['hostname'].partition('.')
container_options['hostname'] = parts[0]
container_options['domainname'] = parts[2]
if 'ports' in container_options or 'expose' in self.options:
ports = []
all_ports = container_options.get('ports', []) + self.options.get('expose', [])
for port in all_ports:
port = str(port)
if ':' in port:
port = port.split(':')[-1]
if '/' in port:
port = tuple(port.split('/'))
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(
(parse_volume_spec(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)
container_options['image'] = self.image_name
container_options['labels'] = build_container_labels(
container_options.get('labels', {}),
self.labels(one_off=one_off),
number)
# Delete options which are only used when starting
for key in DOCKER_START_KEYS:
container_options.pop(key, None)
container_options['host_config'] = self._get_container_host_config(
override_options,
one_off=one_off)
return container_options
def _get_container_host_config(self, override_options, one_off=False):
options = dict(self.options, **override_options)
port_bindings = build_port_bindings(options.get('ports') or [])
privileged = options.get('privileged', False)
cap_add = options.get('cap_add', None)
cap_drop = options.get('cap_drop', None)
log_config = LogConfig(type=options.get('log_driver', 'json-file'))
pid = options.get('pid', None)
security_opt = options.get('security_opt', None)
dns = options.get('dns', None)
if isinstance(dns, six.string_types):
dns = [dns]
dns_search = options.get('dns_search', None)
if isinstance(dns_search, six.string_types):
dns_search = [dns_search]
restart = parse_restart_spec(options.get('restart', None))
extra_hosts = build_extra_hosts(options.get('extra_hosts', None))
read_only = options.get('read_only', None)
devices = options.get('devices', None)
return create_host_config(
links=self._get_links(link_to_self=one_off),
port_bindings=port_bindings,
binds=options.get('binds'),
volumes_from=self._get_volumes_from(),
privileged=privileged,
network_mode=self._get_net(),
devices=devices,
dns=dns,
dns_search=dns_search,
restart_policy=restart,
cap_add=cap_add,
cap_drop=cap_drop,
log_config=log_config,
extra_hosts=extra_hosts,
read_only=read_only,
pid_mode=pid,
security_opt=security_opt
)
def build(self, no_cache=False):
log.info('Building %s...' % self.name)
path = six.binary_type(self.options['build'])
build_output = self.client.build(
path=path,
tag=self.image_name,
stream=True,
rm=True,
pull=False,
nocache=no_cache,
dockerfile=self.options.get('dockerfile', None),
)
try:
all_events = stream_output(build_output, sys.stdout)
except StreamOutputError as e:
raise BuildError(self, unicode(e))
# Ensure the HTTP connection is not reused for another
# streaming command, as the Docker daemon can sometimes
# complain about it
self.client.close()
image_id = None
for event in all_events:
if 'stream' in event:
match = re.search(r'Successfully built ([0-9a-f]+)', event.get('stream', ''))
if match:
image_id = match.group(1)
if image_id is None:
raise BuildError(self, event if all_events else 'Unknown')
return image_id
def can_be_built(self):
return 'build' in self.options
@property
def full_name(self):
"""
The tag to give to images built for this service.
"""
return '%s_%s' % (self.project, self.name)
def labels(self, one_off=False):
return [
'{0}={1}'.format(LABEL_PROJECT, self.project),
'{0}={1}'.format(LABEL_SERVICE, self.name),
'{0}={1}'.format(LABEL_ONE_OFF, "True" if one_off else "False")
]
def can_be_scaled(self):
for port in self.options.get('ports', []):
if ':' in str(port):
return False
return True
def pull(self, insecure_registry=False):
if 'image' not in self.options:
return
repo, tag = parse_repository_tag(self.options['image'])
tag = tag or 'latest'
log.info('Pulling %s (%s:%s)...' % (self.name, repo, tag))
output = self.client.pull(
repo,
tag=tag,
stream=True,
insecure_registry=insecure_registry)
stream_output(output, sys.stdout)
# Names
def build_container_name(project, service, number, one_off=False):
bits = [project, service]
if one_off:
bits.append('run')
return '_'.join(bits + [str(number)])
# Images
def parse_repository_tag(s):
if ":" not in s:
return s, ""
repo, tag = s.rsplit(":", 1)
if "/" in tag:
return s, ""
return repo, tag
# Volumes
def merge_volume_bindings(volumes_option, previous_container):
"""Return a list of volume bindings for a container. Container data volumes
are replaced by those from the previous container.
"""
volume_bindings = dict(
build_volume_binding(parse_volume_spec(volume))
for volume in volumes_option or []
if ':' in volume)
if previous_container:
volume_bindings.update(
get_container_data_volumes(previous_container, volumes_option))
return volume_bindings.values()
def get_container_data_volumes(container, volumes_option):
"""Find the container data volumes that are in `volumes_option`, and return
a mapping of volume bindings for those volumes.
"""
volumes = []
volumes_option = volumes_option or []
container_volumes = container.get('Volumes') or {}
image_volumes = container.image_config['ContainerConfig'].get('Volumes') or {}
for volume in set(volumes_option + image_volumes.keys()):
volume = parse_volume_spec(volume)
# No need to preserve host volumes
if volume.external:
continue
volume_path = container_volumes.get(volume.internal)
# New volume, doesn't exist in the old container
if not volume_path:
continue
# Copy existing volume from old container
volume = volume._replace(external=volume_path)
volumes.append(build_volume_binding(volume))
return dict(volumes)
def build_volume_binding(volume_spec):
return volume_spec.internal, "{}:{}:{}".format(*volume_spec)
def parse_volume_spec(volume_config):
parts = volume_config.split(':')
if len(parts) > 3:
raise ConfigError("Volume %s has incorrect format, should be "
"external:internal[:mode]" % volume_config)
if len(parts) == 1:
return VolumeSpec(None, parts[0], 'rw')
if len(parts) == 2:
parts.append('rw')
external, internal, mode = parts
if mode not in ('rw', 'ro'):
raise ConfigError("Volume %s has invalid mode (%s), should be "
"one of: rw, ro." % (volume_config, mode))
return VolumeSpec(external, internal, mode)
# Ports
def build_port_bindings(ports):
port_bindings = {}
for port in ports:
internal_port, external = split_port(port)
if internal_port in port_bindings:
port_bindings[internal_port].append(external)
else:
port_bindings[internal_port] = [external]
return port_bindings
def split_port(port):
parts = str(port).split(':')
if not 1 <= len(parts) <= 3:
raise ConfigError('Invalid port "%s", should be '
'[[remote_ip:]remote_port:]port[/protocol]' % port)
if len(parts) == 1:
internal_port, = parts
return internal_port, None
if len(parts) == 2:
external_port, internal_port = parts
return internal_port, external_port
external_ip, external_port, internal_port = parts
return internal_port, (external_ip, external_port or None)
# Labels
def build_container_labels(label_options, service_labels, number, one_off=False):
labels = label_options or {}
labels.update(label.split('=', 1) for label in service_labels)
labels[LABEL_CONTAINER_NUMBER] = str(number)
labels[LABEL_VERSION] = __version__
return labels
# Restart policy
def parse_restart_spec(restart_config):
if not restart_config:
return None
parts = restart_config.split(':')
if len(parts) > 2:
raise ConfigError("Restart %s has incorrect format, should be "
"mode[:max_retry]" % restart_config)
if len(parts) == 2:
name, max_retry_count = parts
else:
name, = parts
max_retry_count = 0
return {'Name': name, 'MaximumRetryCount': int(max_retry_count)}
# Extra hosts
def build_extra_hosts(extra_hosts_config):
if not extra_hosts_config:
return {}
if isinstance(extra_hosts_config, list):
extra_hosts_dict = {}
for extra_hosts_line in extra_hosts_config:
if not isinstance(extra_hosts_line, six.string_types):
raise ConfigError(
"extra_hosts_config \"%s\" must be either a list of strings or a string->string mapping," %
extra_hosts_config
)
host, ip = extra_hosts_line.split(':')
extra_hosts_dict.update({host.strip(): ip.strip()})
extra_hosts_config = extra_hosts_dict
if isinstance(extra_hosts_config, dict):
return extra_hosts_config
raise ConfigError(
"extra_hosts_config \"%s\" must be either a list of strings or a string->string mapping," %
extra_hosts_config
)

9
compose/utils.py Normal file
View File

@@ -0,0 +1,9 @@
import json
import hashlib
def json_hash(obj):
dump = json.dumps(obj, sort_keys=True, separators=(',', ':'))
h = hashlib.sha256()
h.update(dump)
return h.hexdigest()

View File

@@ -0,0 +1,363 @@
#!bash
#
# bash completion for docker-compose
#
# This work is based on the completion for the docker command.
#
# This script provides completion of:
# - commands and their options
# - service names
# - filepaths
#
# To enable the completions either:
# - place this file in /etc/bash_completion.d
# or
# - copy this file to e.g. ~/.docker-compose-completion.sh and add the line
# below to your .bashrc after bash completion features are loaded
# . ~/.docker-compose-completion.sh
# For compatibility reasons, Compose and therefore its completion supports several
# stack compositon files as listed here, in descending priority.
# Support for these filenames might be dropped in some future version.
__docker-compose_compose_file() {
local file
for file in docker-compose.y{,a}ml fig.y{,a}ml ; do
[ -e $file ] && {
echo $file
return
}
done
echo docker-compose.yml
}
# Extracts all service names from 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
}
# All services, even those without an existing container
__docker-compose_services_all() {
COMPREPLY=( $(compgen -W "$(___docker-compose_all_services_in_compose_file)" -- "$cur") )
}
# 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)}" | awk -F: -v key=": +$1:" '$0 ~ key {print $1}'
}
# All services that are defined by a Dockerfile reference
__docker-compose_services_from_build() {
COMPREPLY=( $(compgen -W "$(___docker-compose_services_with_key build)" -- "$cur") )
}
# All services that are defined by an image
__docker-compose_services_from_image() {
COMPREPLY=( $(compgen -W "$(___docker-compose_services_with_key image)" -- "$cur") )
}
# The services for which containers have been created, optionally filtered
# 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") )
}
# The services for which at least one running container exists
__docker-compose_services_running() {
__docker-compose_services_with '.State.Running'
}
# The services for which at least one stopped container exists
__docker-compose_services_stopped() {
__docker-compose_services_with 'not .State.Running'
}
_docker-compose_build() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--no-cache" -- "$cur" ) )
;;
*)
__docker-compose_services_from_build
;;
esac
}
_docker-compose_docker-compose() {
case "$prev" in
--file|-f)
_filedir "y?(a)ml"
return
;;
--project-name|-p)
return
;;
esac
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--help -h --verbose --version -v --file -f --project-name -p" -- "$cur" ) )
;;
*)
COMPREPLY=( $( compgen -W "${commands[*]}" -- "$cur" ) )
;;
esac
}
_docker-compose_help() {
COMPREPLY=( $( compgen -W "${commands[*]}" -- "$cur" ) )
}
_docker-compose_kill() {
case "$prev" in
-s)
COMPREPLY=( $( compgen -W "SIGHUP SIGINT SIGKILL SIGUSR1 SIGUSR2" -- "$(echo $cur | tr '[:lower:]' '[:upper:]')" ) )
return
;;
esac
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "-s" -- "$cur" ) )
;;
*)
__docker-compose_services_running
;;
esac
}
_docker-compose_logs() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--no-color" -- "$cur" ) )
;;
*)
__docker-compose_services_all
;;
esac
}
_docker-compose_port() {
case "$prev" in
--protocol)
COMPREPLY=( $( compgen -W "tcp udp" -- "$cur" ) )
return;
;;
--index)
return;
;;
esac
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--protocol --index" -- "$cur" ) )
;;
*)
__docker-compose_services_all
;;
esac
}
_docker-compose_ps() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "-q" -- "$cur" ) )
;;
*)
__docker-compose_services_all
;;
esac
}
_docker-compose_pull() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--allow-insecure-ssl" -- "$cur" ) )
;;
*)
__docker-compose_services_from_image
;;
esac
}
_docker-compose_restart() {
case "$prev" in
-t | --timeout)
return
;;
esac
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "-t --timeout" -- "$cur" ) )
;;
*)
__docker-compose_services_running
;;
esac
}
_docker-compose_rm() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--force -f -v" -- "$cur" ) )
;;
*)
__docker-compose_services_stopped
;;
esac
}
_docker-compose_run() {
case "$prev" in
-e)
COMPREPLY=( $( compgen -e -- "$cur" ) )
compopt -o nospace
return
;;
--entrypoint|--user|-u)
return
;;
esac
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--allow-insecure-ssl -d --entrypoint -e --no-deps --rm --service-ports -T --user -u" -- "$cur" ) )
;;
*)
__docker-compose_services_all
;;
esac
}
_docker-compose_scale() {
case "$prev" in
=)
COMPREPLY=("$cur")
;;
*)
COMPREPLY=( $(compgen -S "=" -W "$(___docker-compose_all_services_in_compose_file)" -- "$cur") )
compopt -o nospace
;;
esac
}
_docker-compose_start() {
__docker-compose_services_stopped
}
_docker-compose_stop() {
case "$prev" in
-t | --timeout)
return
;;
esac
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "-t --timeout" -- "$cur" ) )
;;
*)
__docker-compose_services_running
;;
esac
}
_docker-compose_up() {
case "$prev" in
-t | --timeout)
return
;;
esac
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--allow-insecure-ssl -d --no-build --no-color --no-deps --no-recreate -t --timeout --x-smart-recreate" -- "$cur" ) )
;;
*)
__docker-compose_services_all
;;
esac
}
_docker-compose() {
local previous_extglob_setting=$(shopt -p extglob)
shopt -s extglob
local commands=(
build
help
kill
logs
migrate-to-labels
port
ps
pull
restart
rm
run
scale
start
stop
up
)
COMPREPLY=()
local cur prev words cword
_get_comp_words_by_ref -n : cur prev words cword
# search subcommand and invoke its handler.
# special treatment of some top-level options
local command='docker-compose'
local counter=1
local compose_file compose_project
while [ $counter -lt $cword ]; do
case "${words[$counter]}" in
-f|--file)
(( counter++ ))
compose_file="${words[$counter]}"
;;
-p|--project-name)
(( counter++ ))
compose_project="${words[$counter]}"
;;
-*)
;;
*)
command="${words[$counter]}"
break
;;
esac
(( counter++ ))
done
local completions_func=_docker-compose_${command}
declare -F $completions_func >/dev/null && $completions_func
eval "$previous_extglob_setting"
return 0
}
complete -F _docker-compose docker-compose

View File

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

View File

@@ -1 +0,0 @@
/_site

View File

@@ -1 +0,0 @@
www.fig.sh

View File

@@ -1,10 +1,24 @@
FROM ubuntu:13.10
RUN apt-get -qq update && apt-get install -y ruby1.8 bundler python
RUN locale-gen en_US.UTF-8
ADD Gemfile /code/
ADD Gemfile.lock /code/
WORKDIR /code
RUN bundle install
ADD . /code
EXPOSE 4000
CMD bundle exec jekyll build
FROM docs/base:hugo
MAINTAINER Mary Anthony <mary@docker.com> (@moxiegirl)
# To get the git info for this repo
COPY . /src
COPY . /docs/content/compose/
# Sed to process GitHub Markdown
# 1-2 Remove comment code from metadata block
# 3 Change ](/word to ](/project/ in links
# 4 Change ](word.md) to ](/project/word)
# 5 Remove .md extension from link text
# 6 Change ](../ to ](/project/word)
# 7 Change ](../../ to ](/project/ --> not implemented
#
#
RUN find /docs/content/compose -type f -name "*.md" -exec sed -i.old \
-e '/^<!.*metadata]>/g' \
-e '/^<!.*end-metadata.*>/g' \
-e 's/\(\]\)\([(]\)\(\/\)/\1\2\/compose\//g' \
-e 's/\(\][(]\)\([A-z].*\)\(\.md\)/\1\/compose\/\2/g' \
-e 's/\([(]\)\(.*\)\(\.md\)/\1\2/g' \
-e 's/\(\][(]\)\(\.\.\/\)/\1\/compose\//g' {} \;

View File

@@ -1,3 +0,0 @@
source 'https://rubygems.org'
gem 'github-pages'

View File

@@ -1,62 +0,0 @@
GEM
remote: https://rubygems.org/
specs:
RedCloth (4.2.9)
blankslate (2.1.2.4)
classifier (1.3.3)
fast-stemmer (>= 1.0.0)
colorator (0.1)
commander (4.1.5)
highline (~> 1.6.11)
fast-stemmer (1.0.2)
ffi (1.9.3)
github-pages (12)
RedCloth (= 4.2.9)
jekyll (= 1.4.2)
kramdown (= 1.2.0)
liquid (= 2.5.4)
maruku (= 0.7.0)
rdiscount (= 2.1.7)
redcarpet (= 2.3.0)
highline (1.6.20)
jekyll (1.4.2)
classifier (~> 1.3)
colorator (~> 0.1)
commander (~> 4.1.3)
liquid (~> 2.5.2)
listen (~> 1.3)
maruku (~> 0.7.0)
pygments.rb (~> 0.5.0)
redcarpet (~> 2.3.0)
safe_yaml (~> 0.9.7)
toml (~> 0.1.0)
kramdown (1.2.0)
liquid (2.5.4)
listen (1.3.1)
rb-fsevent (>= 0.9.3)
rb-inotify (>= 0.9)
rb-kqueue (>= 0.2)
maruku (0.7.0)
parslet (1.5.0)
blankslate (~> 2.0)
posix-spawn (0.3.8)
pygments.rb (0.5.4)
posix-spawn (~> 0.3.6)
yajl-ruby (~> 1.1.0)
rb-fsevent (0.9.4)
rb-inotify (0.9.3)
ffi (>= 0.5.0)
rb-kqueue (0.2.0)
ffi (>= 0.5.0)
rdiscount (2.1.7)
redcarpet (2.3.0)
safe_yaml (0.9.7)
toml (0.1.0)
parslet (~> 1.5.0)
yajl-ruby (1.1.0)
PLATFORMS
ruby
DEPENDENCIES
github-pages

55
docs/Makefile Normal file
View File

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

77
docs/README.md Normal file
View File

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

View File

@@ -1,3 +0,0 @@
markdown: redcarpet
encoding: utf-8

View File

@@ -1,71 +0,0 @@
<!DOCTYPE html>
<html lang="en-gb">
<head>
<meta charset="utf-8">
<title>{{ page.title }}</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href='http://fonts.googleapis.com/css?family=Lilita+One|Lato:300,400,700' rel='stylesheet' type='text/css'>
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="css/fig.css?{{ site.time | date:'%Y%m%d%U%H%N%S' }}">
<link rel="canonical" href="http://www.fig.sh{% if page.url =="/index.html" %}/{% else %}{{ page.url }}{% endif %}">
</head>
<body>
<div class="container">
<div class="logo mobile-logo">
<a href="index.html">
<img src="img/logo.png">
Fig
</a>
</div>
<div class="content">{{ content }}</div>
<div class="sidebar">
<h1 class="logo">
<a href="index.html">
<img src="img/logo.png">
Fig
</a>
</h1>
<ul class="nav">
<li><a href="index.html">Home</a></li>
<li><a href="install.html">Install</a></li>
<li><a href="rails.html">Get started with Rails</a></li>
<li><a href="django.html">Get started with Django</a></li>
<li><a href="wordpress.html">Get started with Wordpress</a></li>
</ul>
<ul class="nav">
<li>Reference:</li>
<ul>
<li><a href="yml.html">fig.yml</a></li>
<li><a href="cli.html">Commands</a></li>
<li><a href="env.html">Environment variables</a></li>
</ul>
</ul>
<ul class="nav">
<li><a href="https://github.com/docker/fig">Fig on GitHub</a></li>
<li><a href="http://webchat.freenode.net/?channels=%23docker-fig&uio=d4">#docker-fig on Freenode</a></li>
</ul>
<p>Fig is a project from <a href="https://www.docker.com">Docker</a>.</p>
<div class="badges">
<iframe src="http://ghbtns.com/github-btn.html?user=docker&amp;repo=fig&amp;type=watch&amp;count=true" allowtransparency="true" frameborder="0" scrolling="0" width="100" height="20"></iframe>
<a href="https://twitter.com/share" class="twitter-share-button" data-url="http://orchardup.github.io/fig/">Tweet</a>
</div>
</div>
</div>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-43996733-3', 'orchardup.github.io');
ga('send', 'pageview');
</script>
</body>
</html>

View File

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

69
docs/completion.md Normal file
View File

@@ -0,0 +1,69 @@
<!--[metadata]>
+++
title = "Command Completion"
description = "Compose CLI reference"
keywords = ["fig, composition, compose, docker, orchestration, cli, reference"]
[menu.main]
parent="smn_workw_compose"
weight=3
+++
<![end-metadata]-->
# Command Completion
Compose comes with [command completion](http://en.wikipedia.org/wiki/Command-line_completion)
for the bash and zsh shell.
## Installing Command Completion
### Bash
Make sure bash completion is installed. If you use a current Linux in a non-minimal installation, bash completion should be available.
On a Mac, install with `brew install bash-completion`
Place the completion script in `/etc/bash_completion.d/` (`/usr/local/etc/bash_completion.d/` on a Mac), using e.g.
curl -L https://raw.githubusercontent.com/docker/compose/$(docker-compose --version | awk '{print $2}')/contrib/completion/bash/docker-compose > /etc/bash_completion.d/docker-compose
Completion will be available upon next login.
### Zsh
Place the completion script in your `/path/to/zsh/completion`, using e.g. `~/.zsh/completion/`
mkdir -p ~/.zsh/completion
curl -L https://raw.githubusercontent.com/docker/compose/$(docker-compose --version | awk '{print $2}')/contrib/completion/zsh/_docker-compose > ~/.zsh/completion/_docker-compose
Include the directory in your `$fpath`, e.g. by adding in `~/.zshrc`
fpath=(~/.zsh/completion $fpath)
Make sure `compinit` is loaded or do it by adding in `~/.zshrc`
autoload -Uz compinit && compinit -i
Then reload your shell
exec $SHELL -l
## Available completions
Depending on what you typed on the command line so far, it will complete
- available docker-compose commands
- options that are available for a particular command
- service names that make sense in a given context (e.g. services with running or stopped instances or services based on images vs. services based on Dockerfiles). For `docker-compose scale`, completed service names will automatically have "=" appended.
- arguments for selected options, e.g. `docker-compose kill -s` will complete some signals like SIGHUP and SIGUSR1.
Enjoy working with Compose faster and with less typos!
## Compose documentation
- [User guide](/)
- [Installing Compose](install.md)
- [Get started with Django](django.md)
- [Get started with Rails](rails.md)
- [Get started with Wordpress](wordpress.md)
- [Command line reference](cli.md)
- [Yaml file reference](yml.md)
- [Compose environment variables](env.md)

File diff suppressed because one or more lines are too long

View File

@@ -1,187 +0,0 @@
body {
padding-top: 20px;
padding-bottom: 60px;
font-family: 'Lato', sans-serif;
font-weight: 300;
font-size: 18px;
color: #362;
}
h1, h2, h3, h4, h5, h6 {
font-family: 'Lato', sans-serif;
font-weight: 400;
color: #25594D;
}
h2, h3, h4, h5, h6 {
margin-top: 1.5em;
}
p {
margin: 20px 0;
}
a, a:hover, a:visited {
color: #4D9900;
text-decoration: underline;
}
pre, code {
border: none;
background: #D5E1B4;
}
code, pre code {
color: #484F40;
}
pre {
border-bottom: 2px solid #bec9a1;
font-size: 14px;
}
code {
font-size: 0.84em;
}
pre code {
background: none;
}
img {
max-width: 100%;
}
.container {
margin-left: 0;
}
.logo {
font-family: 'Lilita One', sans-serif;
font-size: 64px;
margin: 20px 0 40px 0;
}
.logo a {
color: #a41211;
text-decoration: none;
}
.logo img {
width: 60px;
vertical-align: -8px;
}
.mobile-logo {
text-align: center;
}
.sidebar {
font-size: 15px;
color: #777;
}
.sidebar a {
color: #a41211;
}
.sidebar p {
margin: 10px 0;
}
@media (max-width: 767px) {
.sidebar {
text-align: center;
margin-top: 40px;
}
.sidebar .logo {
display: none;
}
}
@media (min-width: 768px) {
.mobile-logo {
display: none;
}
.logo {
margin-top: 30px;
margin-bottom: 30px;
}
.content h1 {
margin: 60px 0 55px 0;
}
.sidebar {
position: fixed;
top: 0;
left: 0;
bottom: 0;
width: 280px;
overflow-y: auto;
padding-left: 40px;
padding-right: 10px;
border-right: 1px solid #ccc;
}
.content {
margin-left: 320px;
max-width: 650px;
}
}
.nav {
margin: 15px 0;
}
.nav li a {
display: block;
padding: 5px 0;
line-height: 1.2;
text-decoration: none;
}
.nav li a:hover, .nav li a:focus {
text-decoration: underline;
background: none;
}
.nav ul {
padding-left: 20px;
list-style: none;
}
.badges {
margin: 40px 0;
}
a.btn {
background: #25594D;
color: white;
text-transform: uppercase;
text-decoration: none;
}
a.btn:hover {
color: white;
}
.strapline {
font-size: 30px;
}
@media (min-width: 768px) {
.strapline {
font-size: 40px;
display: block;
line-height: 1.2;
margin-top: 25px;
margin-bottom: 35px;
}
}
strong {
font-weight: 700;
}

View File

@@ -1,14 +1,29 @@
---
layout: default
title: Getting started with Fig and Django
---
<!--[metadata]>
+++
title = "Quickstart Guide: Compose and Django"
description = "Getting started with Docker Compose and Django"
keywords = ["documentation, docs, docker, compose, orchestration, containers"]
[menu.main]
parent="smn_workw_compose"
weight=4
+++
<![end-metadata]-->
Getting started with Fig and Django
===================================
Let's use Fig to set up and run a Django/PostgreSQL app. Before starting, you'll need to have [Fig installed](install.html).
## Quickstart Guide: Compose and Django
Let's set up the three files that'll get us started. First, our app is going to be running inside a Docker container which contains all of its dependencies. We can define what goes inside that Docker container using a file called `Dockerfile`. It'll contain this to start with:
This Quick-start Guide will demonstrate how to use Compose to set up and run a
simple Django/PostgreSQL app. Before starting, you'll need to have
[Compose installed](install.md).
### Define the project
Start by setting up the three files you'll need to build the app. First, since
your app is going to run inside a Docker container containing all of its
dependencies, you'll need to define exactly what needs to be included in the
container. This is done using a file called `Dockerfile`. To begin with, the
Dockerfile consists of:
FROM python:2.7
ENV PYTHONUNBUFFERED 1
@@ -18,14 +33,21 @@ Let's set up the three files that'll get us started. First, our app is going to
RUN pip install -r requirements.txt
ADD . /code/
That'll install our application inside an image with Python installed alongside all of our Python dependencies. For more information on how to write Dockerfiles, see the [Docker user guide](https://docs.docker.com/userguide/dockerimages/#building-an-image-from-a-dockerfile) and the [Dockerfile reference](http://docs.docker.com/reference/builder/).
This Dockerfile will define an image that is used to build a container that
includes your application and has Python installed alongside all of your Python
dependencies. For more information on how to write Dockerfiles, see the
[Docker user guide](https://docs.docker.com/userguide/dockerimages/#building-an-image-from-a-dockerfile) and the [Dockerfile reference](http://docs.docker.com/reference/builder/).
Second, we define our Python dependencies in a file called `requirements.txt`:
Second, you'll define your Python dependencies in a file called
`requirements.txt`:
Django
psycopg2
Simple enough. Finally, this is all tied together with a file called `fig.yml`. It describes the services that our app comprises of (a web server and database), what Docker images they use, how they link together, what volumes will be mounted inside the containers and what ports they expose.
Finally, this is all tied together with a file called `docker-compose.yml`. It
describes the services that comprise your app (here, a web server and database),
which Docker images they use, how they link together, what volumes will be
mounted inside the containers, and what ports they expose.
db:
image: postgres
@@ -39,20 +61,28 @@ Simple enough. Finally, this is all tied together with a file called `fig.yml`.
links:
- db
See the [`fig.yml` reference](yml.html) for more information on how it works.
See the [`docker-compose.yml` reference](yml.html) for more information on how
this file works.
We can now start a Django project using `fig run`:
### Build the project
$ fig run web django-admin.py startproject figexample .
You can now start a Django project with `docker-compose run`:
First, Fig will build an image for the `web` service using the `Dockerfile`. It will then run `django-admin.py startproject figexample .` inside a container using that image.
$ docker-compose run web django-admin.py startproject composeexample .
First, Compose will build an image for the `web` service using the `Dockerfile`.
It will then run `django-admin.py startproject composeexample .` inside a
container built using that image.
This will generate a Django app inside the current directory:
$ ls
Dockerfile fig.yml figexample manage.py requirements.txt
Dockerfile docker-compose.yml composeexample manage.py requirements.txt
First thing we need to do is set up the database connection. Replace the `DATABASES = ...` definition in `figexample/settings.py` to read:
### Connect the database
Now you need to set up the database connection. Replace the `DATABASES = ...`
definition in `composeexample/settings.py` to read:
DATABASES = {
'default': {
@@ -64,9 +94,11 @@ First thing we need to do is set up the database connection. Replace the `DATABA
}
}
These settings are determined by the [postgres](https://registry.hub.docker.com/_/postgres/) Docker image we are using.
These settings are determined by the
[postgres](https://registry.hub.docker.com/_/postgres/) Docker image specified
in the Dockerfile.
Then, run `fig up`:
Then, run `docker-compose up`:
Recreating myapp_db_1...
Recreating myapp_web_1...
@@ -79,13 +111,26 @@ Then, run `fig up`:
myapp_web_1 |
myapp_web_1 | 0 errors found
myapp_web_1 | January 27, 2014 - 12:12:40
myapp_web_1 | Django version 1.6.1, using settings 'figexample.settings'
myapp_web_1 | Django version 1.6.1, using settings 'composeexample.settings'
myapp_web_1 | Starting development server at http://0.0.0.0:8000/
myapp_web_1 | Quit the server with CONTROL-C.
And your Django app should be running at port 8000 on your docker daemon (if you're using boot2docker, `boot2docker ip` will tell you its address).
Your Django app should nw be running at port 8000 on your Docker daemon (if
you're using Boot2docker, `boot2docker ip` will tell you its address).
You can also run management commands with Docker. To set up your database, for example, run `fig up` and in another terminal run:
You can also run management commands with Docker. To set up your database, for
example, run `docker-compose up` and in another terminal run:
$ fig run web python manage.py syncdb
$ docker-compose run web python manage.py syncdb
## More Compose documentation
- [User guide](/)
- [Installing Compose](install.md)
- [Get started with Django](django.md)
- [Get started with Rails](rails.md)
- [Get started with Wordpress](wordpress.md)
- [Command line reference](cli.md)
- [Yaml file reference](yml.md)
- [Compose environment variables](env.md)
- [Compose command line completion](completion.md)

View File

@@ -1,16 +1,22 @@
---
layout: default
title: Fig environment variables reference
---
<!--[metadata]>
+++
title = "Compose environment variables reference"
description = "Compose CLI reference"
keywords = ["fig, composition, compose, docker, orchestration, cli, reference"]
[menu.main]
parent="smn_compose_ref"
weight=3
+++
<![end-metadata]-->
Environment variables reference
# Compose environment variables reference
===============================
**Note:** Environment variables are no longer the recommended method for connecting to linked services. Instead, you should use the link name (by default, the name of the linked service) as the hostname to connect to. See the [fig.yml documentation](yml.html#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](yml.md#links) for details.
Fig 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.
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.
To see what environment variables are available to a service, run `fig run SERVICE env`.
To see what environment variables are available to a service, run `docker-compose run SERVICE env`.
<b><i>name</i>\_PORT</b><br>
Full URL, e.g. `DB_PORT=tcp://172.17.0.5:5432`
@@ -31,3 +37,14 @@ Protocol (tcp or udp), e.g. `DB_PORT_5432_TCP_PROTO=tcp`
Fully qualified container name, e.g. `DB_1_NAME=/myapp_web_1/myapp_db_1`
[Docker links]: http://docs.docker.com/userguide/dockerlinks/
## Compose documentation
- [User guide](/)
- [Installing Compose](install.md)
- [Get started with Django](django.md)
- [Get started with Rails](rails.md)
- [Get started with Wordpress](wordpress.md)
- [Command line reference](cli.md)
- [Yaml file reference](yml.md)
- [Compose command line completion](completion.md)

381
docs/extends.md Normal file
View File

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

View File

@@ -1,8 +0,0 @@
jekyll:
build: .
ports:
- "4000:4000"
volumes:
- .:/code
environment:
- LANG=en_US.UTF-8

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

View File

@@ -1,58 +1,83 @@
---
layout: default
title: Fig | Fast, isolated development environments using Docker
---
<!--[metadata]>
+++
title = "Overview of Docker Compose"
description = "Introduction and Overview of Compose"
keywords = ["documentation, docs, docker, compose, orchestration, containers"]
[menu.main]
parent="smn_workw_compose"
+++
<![end-metadata]-->
<strong class="strapline">Fast, isolated development environments using Docker.</strong>
Define your app's environment with Docker so it can be reproduced anywhere:
# Overview of Docker Compose
FROM python:2.7
ADD . /code
WORKDIR /code
RUN pip install -r requirements.txt
Compose is a tool for defining and running multi-container applications with
Docker. With Compose, you define a multi-container application in a single
file, then spin your application up in a single command which does everything
that needs to be done to get it running.
Define the services that make up your app so they can be run together in an isolated environment:
Compose is great for development environments, staging servers, and CI. We don't
recommend that you use it in production yet.
Using Compose is basically a three-step process.
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:
```yaml
web:
build: .
command: python app.py
links:
- db
ports:
- "8000:8000"
db:
image: postgres
- "5000:5000"
volumes:
- .:/code
links:
- redis
redis:
image: redis
```
(No more installing Postgres on your laptop!)
Compose has commands for managing the whole lifecycle of your application:
Then type `fig up`, and Fig will start and run your entire app:
* 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
![example fig run](https://orchardup.com/static/images/fig-example-large.gif)
## Compose documentation
There are commands to:
- [Installing Compose](install.md)
- [Get started with Django](django.md)
- [Get started with Rails](rails.md)
- [Get started with Wordpress](wordpress.md)
- [Command line reference](cli.md)
- [Yaml file reference](yml.md)
- [Compose environment variables](env.md)
- [Compose command line completion](completion.md)
- start, stop and rebuild services
- view the status of running services
- tail running services' log output
- run a one-off command on a service
## Quick start
Let's get started with a walkthrough of getting a simple Python web app running
on Compose. It assumes a little knowledge of Python, but the concepts
demonstrated here should be understandable even if you're not familiar with
Python.
Quick start
-----------
### Installation and set-up
Let's get a basic Python web app running on Fig. It assumes a little knowledge of Python, but the concepts should be clear if you're not familiar with it.
First, [install Docker and Compose](install.md).
First, [install Docker and Fig](install.html).
Next, you'll want to make a directory for the project:
You'll want to make a directory for the project:
$ mkdir composetest
$ cd composetest
$ mkdir figtest
$ cd figtest
Inside this directory, create `app.py`, a simple web app that uses the Flask framework and increments a value in Redis:
Inside this directory, create `app.py`, a simple web app that uses the Flask
framework and increments a value in Redis:
```python
from flask import Flask
@@ -70,25 +95,41 @@ if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True)
```
We define our Python dependencies in a file called `requirements.txt`:
Next, define the Python dependencies in a file called `requirements.txt`:
flask
redis
Next, we want to create a Docker image containing all of our app's dependencies. We specify how to build one using a file called `Dockerfile`:
### Create a Docker image
Now, create a Docker image containing all of your app's dependencies. You
specify how to build the image using a file called
[`Dockerfile`](http://docs.docker.com/reference/builder/):
FROM python:2.7
ADD . /code
WORKDIR /code
RUN pip install -r requirements.txt
CMD python app.py
This tells Docker to install Python, our code and our Python dependencies inside a Docker image. For more information on how to write Dockerfiles, see the [Docker user guide](https://docs.docker.com/userguide/dockerimages/#building-an-image-from-a-dockerfile) and the [Dockerfile reference](http://docs.docker.com/reference/builder/).
This tells Docker to:
We then define a set of services using `fig.yml`:
* Build an image starting with the Python 2.7 image.
* Add the current directory `.` into the path `/code` in the image.
* Set the working directory to `/code`.
* Install your Python dependencies.
* Set the default command for the container to `python app.py`
For more information on how to write Dockerfiles, see the [Docker user guide](https://docs.docker.com/userguide/dockerimages/#building-an-image-from-a-dockerfile) and the [Dockerfile reference](http://docs.docker.com/reference/builder/).
You can test that this builds by running `docker build -t web .`.
### Define services
Next, define a set of services using `docker-compose.yml`:
web:
build: .
command: python app.py
ports:
- "5000:5000"
volumes:
@@ -100,41 +141,94 @@ We then define a set of services using `fig.yml`:
This defines two services:
- `web`, which is built from `Dockerfile` in the current directory. It also says to run the command `python app.py` inside the image, forward the exposed port 5000 on the container to port 5000 on the host machine, connect up the Redis service, and mount the current directory inside the container so we can work on code without having to rebuild the image.
- `redis`, which uses the public image [redis](https://registry.hub.docker.com/_/redis/).
#### web
Now if we run `fig up`, it'll pull a Redis image, build an image for our own code, and start everything up:
* Builds from the `Dockerfile` in the current directory.
* Forwards the exposed port 5000 on the container to port 5000 on the host machine.
* Connects the web container to the Redis service via a link.
* Mounts the current directory on the host to `/code` inside the container allowing you to modify the code without having to rebuild the image.
$ fig up
#### redis
* Uses the public [Redis](https://registry.hub.docker.com/_/redis/) image which gets pulled from the Docker Hub registry.
### Build and run your app with Compose
Now, when you run `docker-compose up`, Compose will pull a Redis image, build an image for your code, and start everything up:
$ docker-compose up
Pulling image redis...
Building web...
Starting figtest_redis_1...
Starting figtest_web_1...
Starting composetest_redis_1...
Starting composetest_web_1...
redis_1 | [8] 02 Jan 18:43:35.576 # Server started, Redis version 2.8.3
web_1 | * Running on http://0.0.0.0:5000/
The web app should now be listening on port 5000 on your docker daemon (if you're using boot2docker, `boot2docker ip` will tell you its address).
The web app should now be listening on port 5000 on your Docker daemon host (if
you're using Boot2docker, `boot2docker ip` will tell you its address). In a browser,
open `http://ip-from-boot2docker:5000` and you should get a message in your browser saying:
If you want to run your services in the background, you can pass the `-d` flag to `fig up` and use `fig ps` to see what is currently running:
`Hello World! I have been seen 1 times.`
$ fig up -d
Starting figtest_redis_1...
Starting figtest_web_1...
$ fig ps
Name Command State Ports
Refreshing the page will increment the number.
If you want to run your services in the background, you can pass the `-d` flag
(for daemon mode) to `docker-compose up` and use `docker-compose ps` to see what
is currently running:
$ docker-compose up -d
Starting composetest_redis_1...
Starting composetest_web_1...
$ docker-compose ps
Name Command State Ports
-------------------------------------------------------------------
figtest_redis_1 /usr/local/bin/run Up
figtest_web_1 /bin/sh -c python app.py Up 5000->5000/tcp
composetest_redis_1 /usr/local/bin/run Up
composetest_web_1 /bin/sh -c python app.py Up 5000->5000/tcp
`fig run` allows you to run one-off commands for your services. For example, to see what environment variables are available to the `web` service:
The `docker-compose run` command allows you to run one-off commands for your
services. For example, to see what environment variables are available to the
`web` service:
$ fig run web env
$ docker-compose run web env
See `docker-compose --help` to see other available commands.
See `fig --help` other commands that are available.
If you started Compose with `docker-compose up -d`, you'll probably want to stop
your services once you've finished with them:
If you started Fig with `fig up -d`, you'll probably want to stop your services once you've finished with them:
$ docker-compose stop
$ fig stop
At this point, you have seen the basics of how Compose works.
That's more-or-less how Fig works. See the reference section below for full details on the commands, configuration file and environment variables. If you have any thoughts or suggestions, [open an issue on GitHub](https://github.com/docker/fig).
- Next, try the quick start guide for [Django](django.md),
[Rails](rails.md), or [Wordpress](wordpress.md).
- See the reference guides for complete details on the [commands](cli.md), the
[configuration file](yml.md) and [environment variables](env.md).
## Release Notes
### Version 1.2.0 (April 7, 2015)
For complete information on this release, see the [1.2.0 Milestone project page](https://github.com/docker/compose/wiki/1.2.0-Milestone-Project-Page).
In addition to bug fixes and refinements, this release adds the following:
* The `extends` keyword, which adds the ability to extend services by sharing common configurations. For details, see
[PR #1088](https://github.com/docker/compose/pull/1088).
* Better integration with Swarm. Swarm will now schedule inter-dependent
containers on the same host. For details, see
[PR #972](https://github.com/docker/compose/pull/972).
## Getting help
Docker Compose is still in its infancy and under active development. If you need
help, would like to contribute, or simply want to talk about the project with
like-minded individuals, we have a number of open channels for communication.
* To report bugs or file feature requests: please use the [issue tracker on Github](https://github.com/docker/compose/issues).
* To talk about the project with people in real time: please join the `#docker-compose` channel on IRC.
* To contribute code or documentation changes: please submit a [pull request on Github](https://github.com/docker/compose/pulls).
For more information and resources, please visit the [Getting Help project page](https://docs.docker.com/project/get-help/).

View File

@@ -1,27 +1,67 @@
---
layout: default
title: Installing Fig
---
<!--[metadata]>
+++
title = "Docker Compose"
description = "How to install Docker Compose"
keywords = ["compose, orchestration, install, installation, docker, documentation"]
[menu.main]
parent="mn_install"
weight=4
+++
<![end-metadata]-->
Installing Fig
==============
First, install Docker version 1.3 or greater.
# Install Docker Compose
If you're on OS X, you can use the [OS X installer](https://docs.docker.com/installation/mac/) to install both Docker and boot2docker. Once boot2docker is running, set the environment variables that'll configure Docker and Fig to talk to it:
To install Compose, you'll need to install Docker first. You'll then install
Compose with a `curl` command.
$(boot2docker shellinit)
## Install Docker
To persist the environment variables across shell sessions, you can add that line to your `~/.bashrc` file.
First, install Docker version 1.6 or greater:
There are also guides for [Ubuntu](https://docs.docker.com/installation/ubuntulinux/) and [other platforms](https://docs.docker.com/installation/) in Dockers documentation.
- [Instructions for Mac OS X](http://docs.docker.com/installation/mac/)
- [Instructions for Ubuntu](http://docs.docker.com/installation/ubuntulinux/)
- [Instructions for other systems](http://docs.docker.com/installation/)
Next, install Fig:
## Install Compose
curl -L https://github.com/docker/fig/releases/download/1.0.0/fig-`uname -s`-`uname -m` > /usr/local/bin/fig; chmod +x /usr/local/bin/fig
To install Compose, run the following commands:
Releases are available for OS X and 64-bit Linux. Fig is also available as a Python package if you're on another platform (or if you prefer that sort of thing):
curl -L https://github.com/docker/compose/releases/download/1.3.1/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
$ sudo pip install -U fig
> Note: If you get a "Permission denied" error, your `/usr/local/bin` directory probably isn't writable and you'll need to install Compose as the superuser. Run `sudo -i`, then the two commands above, then `exit`.
That should be all you need! Run `fig --version` to see if it worked.
Optionally, you can also install [command completion](completion.md) for the
bash shell.
Compose is available for OS X and 64-bit Linux. If you're on another platform,
Compose can also be installed as a Python package:
$ sudo pip install -U docker-compose
No further steps are required; Compose should now be successfully installed.
You can test the installation by running `docker-compose --version`.
### Upgrading
If you're coming from Compose 1.2 or earlier, you'll need to remove or migrate your existing containers after upgrading Compose. This is because, as of version 1.3, Compose uses Docker labels to keep track of containers, and so they need to be recreated with labels added.
If Compose detects containers that were created without labels, it will refuse to run so that you don't end up with two sets of them. If you want to keep using your existing containers (for example, because they have data volumes you want to preserve) you can migrate them with the following command:
docker-compose migrate-to-labels
Alternatively, if you're not worried about keeping them, you can remove them - Compose will just create new ones.
docker rm -f myapp_web_1 myapp_db_1 ...
## Compose documentation
- [User guide](/)
- [Get started with Django](django.md)
- [Get started with Rails](rails.md)
- [Get started with Wordpress](wordpress.md)
- [Command line reference](cli.md)
- [Yaml file reference](yml.md)
- [Compose environment variables](env.md)
- [Compose command line completion](completion.md)

96
docs/production.md Normal file
View File

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

View File

@@ -1,16 +1,27 @@
---
layout: default
title: Getting started with Fig and Rails
---
<!--[metadata]>
+++
title = "Quickstart Guide: Compose and Rails"
description = "Getting started with Docker Compose and Rails"
keywords = ["documentation, docs, docker, compose, orchestration, containers"]
[menu.main]
parent="smn_workw_compose"
weight=5
+++
<![end-metadata]-->
Getting started with Fig and Rails
==================================
## Quickstart Guide: Compose and Rails
We're going to use Fig to set up and run a Rails/PostgreSQL app. Before starting, you'll need to have [Fig installed](install.html).
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).
Let's set up the three files that'll get us started. First, our app is going to be running inside a Docker container which contains all of its dependencies. We can define what goes inside that Docker container using a file called `Dockerfile`. It'll contain this to start with:
### Define the project
FROM ruby
Start by setting up the three files you'll need to build the app. First, since
your app is going to run inside a Docker container containing all of its
dependencies, you'll need to define exactly what needs to be included in the
container. This is done using a file called `Dockerfile`. To begin with, the
Dockerfile consists of:
FROM ruby:2.2.0
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev
RUN mkdir /myapp
WORKDIR /myapp
@@ -18,14 +29,14 @@ Let's set up the three files that'll get us started. First, our app is going to
RUN bundle install
ADD . /myapp
That'll put our application code inside an image with Ruby, Bundler and all our dependencies. For more information on how to write Dockerfiles, see the [Docker user guide](https://docs.docker.com/userguide/dockerimages/#building-an-image-from-a-dockerfile) and the [Dockerfile reference](http://docs.docker.com/reference/builder/).
That'll put your application code inside an image that will build a container with Ruby, Bundler and all your dependencies inside it. For more information on how to write Dockerfiles, see the [Docker user guide](https://docs.docker.com/userguide/dockerimages/#building-an-image-from-a-dockerfile) and the [Dockerfile reference](http://docs.docker.com/reference/builder/).
Next, we have a bootstrap `Gemfile` which just loads Rails. It'll be overwritten in a moment by `rails new`.
Next, create a bootstrap `Gemfile` which just loads Rails. It'll be overwritten in a moment by `rails new`.
source 'https://rubygems.org'
gem 'rails', '4.0.2'
gem 'rails', '4.2.0'
Finally, `fig.yml` is where the magic happens. It describes what services our app comprises (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 we need to link them together and expose the web app's port.
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
@@ -33,7 +44,7 @@ Finally, `fig.yml` is where the magic happens. It describes what services our ap
- "5432"
web:
build: .
command: bundle exec rackup -p 3000
command: bundle exec rails s -p 3000 -b '0.0.0.0'
volumes:
- .:/myapp
ports:
@@ -41,30 +52,44 @@ Finally, `fig.yml` is where the magic happens. It describes what services our ap
links:
- db
With those files in place, we can now generate the Rails skeleton app using `fig run`:
### Build the project
$ fig run web rails new . --force --database=postgresql --skip-bundle
With those three files in place, you can now generate the Rails skeleton app
using `docker-compose run`:
First, Fig will build the image for the `web` service using the `Dockerfile`. Then it'll run `rails new` inside a new container, using that image. Once it's done, you should have a fresh app generated:
$ docker-compose run web rails new . --force --database=postgresql --skip-bundle
First, Compose will build the image for the `web` service using the
`Dockerfile`. Then it'll run `rails new` inside a new container, using that
image. Once it's done, you should have generated a fresh app:
$ ls
Dockerfile app fig.yml tmp
Dockerfile app docker-compose.yml tmp
Gemfile bin lib vendor
Gemfile.lock config log
README.rdoc config.ru public
Rakefile db test
Uncomment the line in your new `Gemfile` which loads `therubyracer`, so we've got a Javascript runtime:
Uncomment the line in your new `Gemfile` which loads `therubyracer`, so you've
got a Javascript runtime:
gem 'therubyracer', platforms: :ruby
Now that we've got a new `Gemfile`, we need to build the image again. (This, and changes to the Dockerfile itself, should be the only times you'll need to rebuild).
Now that you've got a new `Gemfile`, you need to build the image again. (This,
and changes to the Dockerfile itself, should be the only times you'll need to
rebuild.)
$ fig build
$ docker-compose build
The app is now bootable, but we're not quite there yet. By default, Rails expects a database to be running on `localhost` - we need to point it at the `db` container instead. We also need to change the database and username to align with the defaults set by the `postgres` image.
### Connect the database
Open up your newly-generated `database.yml`. Replace its contents with the following:
The app is now bootable, but you're not quite there yet. By default, Rails
expects a database to be running on `localhost` - so you need to point it at the
`db` container instead. You also need to change the database and username to
align with the defaults set by the `postgres` image.
Open up your newly-generated `database.yml` file. Replace its contents with the
following:
development: &default
adapter: postgresql
@@ -79,20 +104,32 @@ Open up your newly-generated `database.yml`. Replace its contents with the follo
<<: *default
database: myapp_test
We can now boot the app.
You can now boot the app with:
$ fig up
$ docker-compose up
If all's well, you should see some PostgreSQL output, and then—after a few seconds—the familiar refrain:
If all's well, you should see some PostgreSQL output, and then—after a few
seconds—the familiar refrain:
myapp_web_1 | [2014-01-17 17:16:29] INFO WEBrick 1.3.1
myapp_web_1 | [2014-01-17 17:16:29] INFO ruby 2.0.0 (2013-11-22) [x86_64-linux-gnu]
myapp_web_1 | [2014-01-17 17:16:29] INFO ruby 2.2.0 (2014-12-25) [x86_64-linux-gnu]
myapp_web_1 | [2014-01-17 17:16:29] INFO WEBrick::HTTPServer#start: pid=1 port=3000
Finally, we just need to create the database. In another terminal, run:
Finally, you need to create the database. In another terminal, run:
$ fig run web rake db:create
$ docker-compose run web rake db:create
And we're rolling—your app should now be running on port 3000 on your docker daemon (if you're using boot2docker, `boot2docker ip` will tell you its address).
That's it. Your app should now be running on port 3000 on your Docker daemon (if
you're using Boot2docker, `boot2docker ip` will tell you its address).
![Screenshot of Rails' stock index.html](https://orchardup.com/static/images/fig-rails-screenshot.png)
## More Compose documentation
- [User guide](/)
- [Installing Compose](install.md)
- [Get started with Django](django.md)
- [Get started with Rails](rails.md)
- [Get started with Wordpress](wordpress.md)
- [Command line reference](cli.md)
- [Yaml file reference](yml.md)
- [Compose environment variables](env.md)
- [Compose command line completion](completion.md)

View File

@@ -1,25 +1,47 @@
---
layout: default
title: Getting started with Fig and Wordpress
---
<!--[metadata]>
+++
title = "Quickstart Guide: Compose and Wordpress"
description = "Getting started with Compose and Wordpress"
keywords = ["documentation, docs, docker, compose, orchestration, containers"]
[menu.main]
parent="smn_workw_compose"
weight=6
+++
<![end-metadata]-->
Getting started with Fig and Wordpress
======================================
Fig makes it nice and easy to run Wordpress in an isolated environment. [Install Fig](install.html), then download Wordpress into the current directory:
# Quickstart Guide: Compose and Wordpress
$ curl http://wordpress.org/wordpress-3.8.1.tar.gz | tar -xvzf -
You can use Compose to easily run Wordpress in an isolated environment built
with Docker containers.
This will create a directory called `wordpress`, which you can rename to the name of your project if you wish. Inside that directory, we create `Dockerfile`, a file that defines what environment your app is going to run in:
## Define the project
First, [Install Compose](install.md) and then download Wordpress into the
current directory:
$ curl https://wordpress.org/latest.tar.gz | tar -xvzf -
This will create a directory called `wordpress`. If you wish, you can rename it
to the name of your project.
Next, inside that directory, create a `Dockerfile`, a file that defines what
environment your app is going to run in. For more information on how to write
Dockerfiles, see the
[Docker user guide](https://docs.docker.com/userguide/dockerimages/#building-an-image-from-a-dockerfile) and the
[Dockerfile reference](http://docs.docker.com/reference/builder/). In this case,
your Dockerfile should be:
```
FROM orchardup/php5
ADD . /code
```
This instructs Docker on how to build an image that contains PHP and Wordpress. For more information on how to write Dockerfiles, see the [Docker user guide](https://docs.docker.com/userguide/dockerimages/#building-an-image-from-a-dockerfile) and the [Dockerfile reference](http://docs.docker.com/reference/builder/).
This tells Docker how to build an image defining a container that contains PHP
and Wordpress.
Next up, `fig.yml` starts our web service and a separate MySQL instance:
Next you'll create a `docker-compose.yml` file that will start your web service
and a separate MySQL instance:
```
web:
@@ -37,7 +59,9 @@ db:
MYSQL_DATABASE: wordpress
```
Two supporting files are needed to get this working - first up, `wp-config.php` is the standard Wordpress config file with a single change to make it read the MySQL host and port from the environment variables passed in by Fig:
Two supporting files are needed to get this working - first, `wp-config.php` is
the standard Wordpress config file with a single change to point the database
configuration at the `db` container:
```
<?php
@@ -67,7 +91,7 @@ if ( !defined('ABSPATH') )
require_once(ABSPATH . 'wp-settings.php');
```
Finally, `router.php` tells PHP's built-in web server how to run Wordpress:
Second, `router.php` tells PHP's built-in web server how to run Wordpress:
```
<?php
@@ -87,5 +111,22 @@ if(file_exists($root.$path))
}
}else include_once 'index.php';
```
### Build the project
With those four files in place, run `fig up` inside your Wordpress directory and it'll pull and build the images we need, and then start the web and database containers. You'll then be able to visit Wordpress at port 8000 on your docker daemon (if you're using boot2docker, `boot2docker ip` will tell you its address).
With those four files in place, run `docker-compose up` inside your Wordpress
directory and it'll pull and build the needed images, and then start the web and
database containers. You'll then be able to visit Wordpress at port 8000 on your
Docker daemon (if you're using Boot2docker, `boot2docker ip` will tell you its
address).
## More Compose documentation
- [User guide](/)
- [Installing Compose](install.md)
- [Get started with Django](django.md)
- [Get started with Rails](rails.md)
- [Get started with Wordpress](wordpress.md)
- [Command line reference](cli.md)
- [Yaml file reference](yml.md)
- [Compose environment variables](env.md)
- [Compose command line completion](completion.md)

View File

@@ -1,18 +1,28 @@
---
layout: default
title: fig.yml reference
---
<!--[metadata]>
+++
title = "docker-compose.yml reference"
description = "docker-compose.yml reference"
keywords = ["fig, composition, compose, docker"]
[menu.main]
parent="smn_compose_ref"
+++
<![end-metadata]-->
fig.yml reference
=================
Each service defined in `fig.yml` must specify exactly one of `image` or `build`. Other keys are optional, and are analogous to their `docker run` command-line counterparts.
# docker-compose.yml reference
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 `fig.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.
###image
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`.
Tag or partial image ID. Can be local or remote - Fig will attempt to pull if it doesn't exist locally.
### image
Tag or partial image ID. Can be local or remote - Compose will attempt to
pull if it doesn't exist locally.
```
image: ubuntu
@@ -22,12 +32,26 @@ image: a4bc65fd
### build
Path to a directory containing a Dockerfile. Fig will build and tag it with a generated name, and use that image thereafter.
Path to a directory containing a Dockerfile. When the value supplied is a
relative path, it is interpreted as relative to the location of the yml file
itself. This directory is also the build context that is sent to the Docker daemon.
Compose will build and tag it with a generated name, and use that image thereafter.
```
build: /path/to/build/dir
```
### dockerfile
Alternate Dockerfile.
Compose will use an alternate file to build with.
```
dockerfile: Dockerfile-alternate
```
### command
Override the default command.
@@ -39,7 +63,9 @@ command: bundle exec thin -p 3000
<a name="links"></a>
### 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).
Link to containers in another service. Either specify both the service name and
the link alias (`SERVICE:ALIAS`), or just the service name (which will also be
used for the alias).
```
links:
@@ -48,7 +74,8 @@ links:
- redis
```
An entry with the alias' name will be created in `/etc/hosts` inside containers for this service, e.g:
An entry with the alias' name will be created in `/etc/hosts` inside containers
for this service, e.g:
```
172.17.2.186 db
@@ -56,13 +83,49 @@ An entry with the alias' name will be created in `/etc/hosts` inside containers
172.17.2.187 redis
```
Environment variables will also be created - see the [environment variable reference](env.html) for details.
Environment variables will also be created - see the [environment variable
reference](env.md) for details.
### external_links
Link to containers started outside this `docker-compose.yml` or even outside
of Compose, especially for containers that provide shared or common services.
`external_links` follow semantics similar to `links` when specifying both the
container name and the link alias (`CONTAINER:ALIAS`).
```
external_links:
- redis_1
- project_db_1:mysql
- project_db_1:postgresql
```
### extra_hosts
Add hostname mappings. Use the same values as the docker client `--add-host` parameter.
```
extra_hosts:
- "somehost:162.242.195.82"
- "otherhost:50.31.209.229"
```
An entry with the ip address and hostname will be created in `/etc/hosts` inside containers for this service, e.g:
```
162.242.195.82 somehost
50.31.209.229 otherhost
```
### ports
Expose ports. Either specify both ports (`HOST:CONTAINER`), or just the container port (a random host port will be chosen).
Expose ports. Either specify both ports (`HOST:CONTAINER`), or just the container
port (a random host port will be chosen).
**Note:** When mapping ports in the `HOST:CONTAINER` format, you may experience erroneous results when using a container port lower than 60, because YAML will parse numbers in the format `xx:yy` as sexagesimal (base 60). For this reason, we recommend always explicitly specifying your port mappings as strings.
> **Note:** When mapping ports in the `HOST:CONTAINER` format, you may experience
> erroneous results when using a container port lower than 60, because YAML will
> parse numbers in the format `xx:yy` as sexagesimal (base 60). For this reason,
> we recommend always explicitly specifying your port mappings as strings.
```
ports:
@@ -74,7 +137,8 @@ ports:
### expose
Expose ports without publishing them to the host machine - they'll only be accessible to linked services. Only the internal port can be specified.
Expose ports without publishing them to the host machine - they'll only be
accessible to linked services. Only the internal port can be specified.
```
expose:
@@ -108,7 +172,8 @@ volumes_from:
Add environment variables. You can use either an array or a dictionary.
Environment variables with only a key are resolved to their values on the machine Fig is running on, which can be helpful for secret or host-specific values.
Environment variables with only a key are resolved to their values on the
machine Compose is running on, which can be helpful for secret or host-specific values.
```
environment:
@@ -120,6 +185,109 @@ environment:
- SESSION_SECRET
```
### env_file
Add environment variables from a file. Can be a single value or a list.
If you have specified a Compose file with `docker-compose -f FILE`, paths in
`env_file` are relative to the directory that file is in.
Environment variables specified in `environment` override these values.
```
env_file: .env
env_file:
- ./common.env
- ./apps/web.env
- /opt/secrets.env
```
Compose expects each line in an env file to be in `VAR=VAL` format. Lines
beginning with `#` (i.e. comments) are ignored, as are blank lines.
```
# Set Rails/Rack environment
RACK_ENV=development
```
### extends
Extend another service, in the current file or another, optionally overriding
configuration.
Here's a simple example. Suppose we have 2 files - **common.yml** and
**development.yml**. We can use `extends` to define a service in
**development.yml** which uses configuration defined in **common.yml**:
**common.yml**
```
webapp:
build: ./webapp
environment:
- DEBUG=false
- SEND_EMAILS=false
```
**development.yml**
```
web:
extends:
file: common.yml
service: webapp
ports:
- "8000:8000"
links:
- db
environment:
- DEBUG=true
db:
image: postgres
```
Here, the `web` service in **development.yml** inherits the configuration of
the `webapp` service in **common.yml** - the `build` and `environment` keys -
and adds `ports` and `links` configuration. It overrides one of the defined
environment variables (DEBUG) with a new value, and the other one
(SEND_EMAILS) is left untouched.
For more on `extends`, see the [tutorial](extends.md#example) and
[reference](extends.md#reference).
### labels
Add metadata to containers using [Docker labels](http://docs.docker.com/userguide/labels-custom-metadata/). You can use either an array or a dictionary.
It's recommended that you use reverse-DNS notation to prevent your labels from conflicting with those used by other software.
```
labels:
com.example.description: "Accounting webapp"
com.example.department: "Finance"
com.example.label-with-empty-value: ""
labels:
- "com.example.description=Accounting webapp"
- "com.example.department=Finance"
- "com.example.label-with-empty-value"
```
### log driver
Specify a logging driver for the service's containers, as with the ``--log-driver`` option for docker run ([documented here](http://docs.docker.com/reference/run/#logging-drivers-log-driver)).
Allowed values are currently ``json-file``, ``syslog`` and ``none``. The list will change over time as more drivers are added to the Docker engine.
The default value is json-file.
```
log_driver: "json-file"
log_driver: "syslog"
log_driver: "none"
```
### net
Networking mode. Use the same values as the docker client `--net` parameter.
@@ -130,6 +298,16 @@ net: "none"
net: "container:[name or id]"
net: "host"
```
### pid
```
pid: "host"
```
Sets the PID mode to the host PID mode. This turns on sharing between
container and the host operating system the PID address space. Containers
launched with this flag will be able to access and manipulate other
containers in the bare-metal machine's namespace and vise-versa.
### dns
@@ -142,11 +320,60 @@ dns:
- 9.9.9.9
```
### working\_dir, entrypoint, user, hostname, domainname, mem\_limit, privileged
### cap_add, cap_drop
Each of these is a single value, analogous to its [docker run](https://docs.docker.com/reference/run/) counterpart.
Add or drop container capabilities.
See `man 7 capabilities` for a full list.
```
cap_add:
- ALL
cap_drop:
- NET_ADMIN
- SYS_ADMIN
```
### dns_search
Custom DNS search domains. Can be a single value or a list.
```
dns_search: example.com
dns_search:
- dc1.example.com
- dc2.example.com
```
### devices
List of device mappings. Uses the same format as the `--device` docker
client create option.
```
devices:
- "/dev/ttyUSB0:/dev/ttyUSB0"
```
### security_opt
Override the default labeling scheme for each container.
```
security_opt:
- label:user:USER
- label:role:ROLE
```
### working\_dir, entrypoint, user, hostname, domainname, mem\_limit, privileged, restart, stdin\_open, tty, cpu\_shares, cpuset, read\_only
Each of these is a single value, analogous to its
[docker run](https://docs.docker.com/reference/run/) counterpart.
```
cpu_shares: 73
cpuset: 0,1
working_dir: /code
entrypoint: /code/entrypoint.sh
user: postgresql
@@ -156,4 +383,21 @@ domainname: foo.com
mem_limit: 1000000000
privileged: true
restart: always
stdin_open: true
tty: true
read_only: true
```
## Compose documentation
- [User guide](/)
- [Installing Compose](install.md)
- [Get started with Django](django.md)
- [Get started with Rails](rails.md)
- [Get started with Wordpress](wordpress.md)
- [Command line reference](cli.md)
- [Compose environment variables](env.md)
- [Compose command line completion](completion.md)

View File

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

View File

@@ -1,27 +0,0 @@
# dockerpty.
#
# Copyright 2014 Chris Corbyn <chris@w3style.co.uk>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from .pty import PseudoTerminal
def start(client, container):
"""
Present the PTY of the container inside the current process.
This is just a wrapper for PseudoTerminal(client, container).start()
"""
PseudoTerminal(client, container).start()

View File

@@ -1,294 +0,0 @@
# dockerpty: io.py
#
# Copyright 2014 Chris Corbyn <chris@w3style.co.uk>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import fcntl
import errno
import struct
import select as builtin_select
def set_blocking(fd, blocking=True):
"""
Set the given file-descriptor blocking or non-blocking.
Returns the original blocking status.
"""
old_flag = fcntl.fcntl(fd, fcntl.F_GETFL)
if blocking:
new_flag = old_flag &~ os.O_NONBLOCK
else:
new_flag = old_flag | os.O_NONBLOCK
fcntl.fcntl(fd, fcntl.F_SETFL, new_flag)
return not bool(old_flag & os.O_NONBLOCK)
def select(read_streams, timeout=0):
"""
Select the streams from `read_streams` that are ready for reading.
Uses `select.select()` internally but returns a flat list of streams.
"""
write_streams = []
exception_streams = []
try:
return builtin_select.select(
read_streams,
write_streams,
exception_streams,
timeout,
)[0]
except builtin_select.error as e:
# POSIX signals interrupt select()
if e[0] == errno.EINTR:
return []
else:
raise e
class Stream(object):
"""
Generic Stream class.
This is a file-like abstraction on top of os.read() and os.write(), which
add consistency to the reading of sockets and files alike.
"""
"""
Recoverable IO/OS Errors.
"""
ERRNO_RECOVERABLE = [
errno.EINTR,
errno.EDEADLK,
errno.EWOULDBLOCK,
]
def __init__(self, fd):
"""
Initialize the Stream for the file descriptor `fd`.
The `fd` object must have a `fileno()` method.
"""
self.fd = fd
def fileno(self):
"""
Return the fileno() of the file descriptor.
"""
return self.fd.fileno()
def set_blocking(self, value):
if hasattr(self.fd, 'setblocking'):
self.fd.setblocking(value)
return True
else:
return set_blocking(self.fd, value)
def read(self, n=4096):
"""
Return `n` bytes of data from the Stream, or None at end of stream.
"""
try:
if hasattr(self.fd, 'recv'):
return self.fd.recv(n)
return os.read(self.fd.fileno(), n)
except EnvironmentError as e:
if e.errno not in Stream.ERRNO_RECOVERABLE:
raise e
def write(self, data):
"""
Write `data` to the Stream.
"""
if not data:
return None
while True:
try:
if hasattr(self.fd, 'send'):
self.fd.send(data)
return len(data)
os.write(self.fd.fileno(), data)
return len(data)
except EnvironmentError as e:
if e.errno not in Stream.ERRNO_RECOVERABLE:
raise e
def __repr__(self):
return "{cls}({fd})".format(cls=type(self).__name__, fd=self.fd)
class Demuxer(object):
"""
Wraps a multiplexed Stream to read in data demultiplexed.
Docker multiplexes streams together when there is no PTY attached, by
sending an 8-byte header, followed by a chunk of data.
The first 4 bytes of the header denote the stream from which the data came
(i.e. 0x01 = stdout, 0x02 = stderr). Only the first byte of these initial 4
bytes is used.
The next 4 bytes indicate the length of the following chunk of data as an
integer in big endian format. This much data must be consumed before the
next 8-byte header is read.
"""
def __init__(self, stream):
"""
Initialize a new Demuxer reading from `stream`.
"""
self.stream = stream
self.remain = 0
def fileno(self):
"""
Returns the fileno() of the underlying Stream.
This is useful for select() to work.
"""
return self.stream.fileno()
def set_blocking(self, value):
return self.stream.set_blocking(value)
def read(self, n=4096):
"""
Read up to `n` bytes of data from the Stream, after demuxing.
Less than `n` bytes of data may be returned depending on the available
payload, but the number of bytes returned will never exceed `n`.
Because demuxing involves scanning 8-byte headers, the actual amount of
data read from the underlying stream may be greater than `n`.
"""
size = self._next_packet_size(n)
if size <= 0:
return
else:
return self.stream.read(size)
def write(self, data):
"""
Delegates the the underlying Stream.
"""
return self.stream.write(data)
def _next_packet_size(self, n=0):
size = 0
if self.remain > 0:
size = min(n, self.remain)
self.remain -= size
else:
data = self.stream.read(8)
if data is None:
return 0
if len(data) == 8:
__, actual = struct.unpack('>BxxxL', data)
size = min(n, actual)
self.remain = actual - size
return size
def __repr__(self):
return "{cls}({stream})".format(cls=type(self).__name__,
stream=self.stream)
class Pump(object):
"""
Stream pump class.
A Pump wraps two Streams, reading from one and and writing its data into
the other, much like a pipe but manually managed.
This abstraction is used to facilitate piping data between the file
descriptors associated with the tty and those associated with a container's
allocated pty.
Pumps are selectable based on the 'read' end of the pipe.
"""
def __init__(self, from_stream, to_stream):
"""
Initialize a Pump with a Stream to read from and another to write to.
"""
self.from_stream = from_stream
self.to_stream = to_stream
def fileno(self):
"""
Returns the `fileno()` of the reader end of the Pump.
This is useful to allow Pumps to function with `select()`.
"""
return self.from_stream.fileno()
def set_blocking(self, value):
return self.from_stream.set_blocking(value)
def flush(self, n=4096):
"""
Flush `n` bytes of data from the reader Stream to the writer Stream.
Returns the number of bytes that were actually flushed. A return value
of zero is not an error.
If EOF has been reached, `None` is returned.
"""
try:
return self.to_stream.write(self.from_stream.read(n))
except OSError as e:
if e.errno != errno.EPIPE:
raise e
def __repr__(self):
return "{cls}(from={from_stream}, to={to_stream})".format(
cls=type(self).__name__,
from_stream=self.from_stream,
to_stream=self.to_stream)

View File

@@ -1,235 +0,0 @@
# dockerpty: pty.py
#
# Copyright 2014 Chris Corbyn <chris@w3style.co.uk>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import sys
import signal
from ssl import SSLError
from . import io
from . import tty
class WINCHHandler(object):
"""
WINCH Signal handler to keep the PTY correctly sized.
"""
def __init__(self, pty):
"""
Initialize a new WINCH handler for the given PTY.
Initializing a handler has no immediate side-effects. The `start()`
method must be invoked for the signals to be trapped.
"""
self.pty = pty
self.original_handler = None
def __enter__(self):
"""
Invoked on entering a `with` block.
"""
self.start()
return self
def __exit__(self, *_):
"""
Invoked on exiting a `with` block.
"""
self.stop()
def start(self):
"""
Start trapping WINCH signals and resizing the PTY.
This method saves the previous WINCH handler so it can be restored on
`stop()`.
"""
def handle(signum, frame):
if signum == signal.SIGWINCH:
self.pty.resize()
self.original_handler = signal.signal(signal.SIGWINCH, handle)
def stop(self):
"""
Stop trapping WINCH signals and restore the previous WINCH handler.
"""
if self.original_handler is not None:
signal.signal(signal.SIGWINCH, self.original_handler)
class PseudoTerminal(object):
"""
Wraps the pseudo-TTY (PTY) allocated to a docker container.
The PTY is managed via the current process' TTY until it is closed.
Example:
import docker
from dockerpty import PseudoTerminal
client = docker.Client()
container = client.create_container(
image='busybox:latest',
stdin_open=True,
tty=True,
command='/bin/sh',
)
# hijacks the current tty until the pty is closed
PseudoTerminal(client, container).start()
Care is taken to ensure all file descriptors are restored on exit. For
example, you can attach to a running container from within a Python REPL
and when the container exits, the user will be returned to the Python REPL
without adverse effects.
"""
def __init__(self, client, container):
"""
Initialize the PTY using the docker.Client instance and container dict.
"""
self.client = client
self.container = container
self.raw = None
def start(self, **kwargs):
"""
Present the PTY of the container inside the current process.
This will take over the current process' TTY until the container's PTY
is closed.
"""
pty_stdin, pty_stdout, pty_stderr = self.sockets()
mappings = [
(io.Stream(sys.stdin), pty_stdin),
(pty_stdout, io.Stream(sys.stdout)),
(pty_stderr, io.Stream(sys.stderr)),
]
pumps = [io.Pump(a, b) for (a, b) in mappings if a and b]
if not self.container_info()['State']['Running']:
self.client.start(self.container, **kwargs)
flags = [p.set_blocking(False) for p in pumps]
try:
with WINCHHandler(self):
self._hijack_tty(pumps)
finally:
if flags:
for (pump, flag) in zip(pumps, flags):
io.set_blocking(pump, flag)
def israw(self):
"""
Returns True if the PTY should operate in raw mode.
If the container was not started with tty=True, this will return False.
"""
if self.raw is None:
info = self.container_info()
self.raw = sys.stdout.isatty() and info['Config']['Tty']
return self.raw
def sockets(self):
"""
Returns a tuple of sockets connected to the pty (stdin,stdout,stderr).
If any of the sockets are not attached in the container, `None` is
returned in the tuple.
"""
info = self.container_info()
def attach_socket(key):
if info['Config']['Attach{0}'.format(key.capitalize())]:
socket = self.client.attach_socket(
self.container,
{key: 1, 'stream': 1, 'logs': 1},
)
stream = io.Stream(socket)
if info['Config']['Tty']:
return stream
else:
return io.Demuxer(stream)
else:
return None
return map(attach_socket, ('stdin', 'stdout', 'stderr'))
def resize(self, size=None):
"""
Resize the container's PTY.
If `size` is not None, it must be a tuple of (height,width), otherwise
it will be determined by the size of the current TTY.
"""
if not self.israw():
return
size = size or tty.size(sys.stdout)
if size is not None:
rows, cols = size
try:
self.client.resize(self.container, height=rows, width=cols)
except IOError: # Container already exited
pass
def container_info(self):
"""
Thin wrapper around client.inspect_container().
"""
return self.client.inspect_container(self.container)
def _hijack_tty(self, pumps):
with tty.Terminal(sys.stdin, raw=self.israw()):
self.resize()
while True:
_ready = io.select(pumps, timeout=60)
try:
if all([p.flush() is None for p in pumps]):
break
except SSLError as e:
if 'The operation did not complete' not in e.strerror:
raise e

View File

@@ -1,130 +0,0 @@
# dockerpty: tty.py
#
# Copyright 2014 Chris Corbyn <chris@w3style.co.uk>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import
import os
import termios
import tty
import fcntl
import struct
def size(fd):
"""
Return a tuple (rows,cols) representing the size of the TTY `fd`.
The provided file descriptor should be the stdout stream of the TTY.
If the TTY size cannot be determined, returns None.
"""
if not os.isatty(fd.fileno()):
return None
try:
dims = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, 'hhhh'))
except:
try:
dims = (os.environ['LINES'], os.environ['COLUMNS'])
except:
return None
return dims
class Terminal(object):
"""
Terminal provides wrapper functionality to temporarily make the tty raw.
This is useful when streaming data from a pseudo-terminal into the tty.
Example:
with Terminal(sys.stdin, raw=True):
do_things_in_raw_mode()
"""
def __init__(self, fd, raw=True):
"""
Initialize a terminal for the tty with stdin attached to `fd`.
Initializing the Terminal has no immediate side effects. The `start()`
method must be invoked, or `with raw_terminal:` used before the
terminal is affected.
"""
self.fd = fd
self.raw = raw
self.original_attributes = None
def __enter__(self):
"""
Invoked when a `with` block is first entered.
"""
self.start()
return self
def __exit__(self, *_):
"""
Invoked when a `with` block is finished.
"""
self.stop()
def israw(self):
"""
Returns True if the TTY should operate in raw mode.
"""
return self.raw
def start(self):
"""
Saves the current terminal attributes and makes the tty raw.
This method returns None immediately.
"""
if os.isatty(self.fd.fileno()) and self.israw():
self.original_attributes = termios.tcgetattr(self.fd)
tty.setraw(self.fd)
def stop(self):
"""
Restores the terminal attributes back to before setting raw mode.
If the raw terminal was not started, does nothing.
"""
if self.original_attributes is not None:
termios.tcsetattr(
self.fd,
termios.TCSADRAIN,
self.original_attributes,
)
def __repr__(self):
return "{cls}({fd}, raw={raw})".format(
cls=type(self).__name__,
fd=self.fd,
raw=self.raw)

View File

@@ -1,522 +0,0 @@
from __future__ import unicode_literals
from __future__ import absolute_import
from collections import namedtuple
import logging
import re
import os
from operator import attrgetter
import sys
from docker.errors import APIError
from .container import Container
from .progress_stream import stream_output, StreamOutputError
log = logging.getLogger(__name__)
DOCKER_CONFIG_KEYS = ['image', 'command', 'hostname', 'domainname', 'user', 'detach', 'stdin_open', 'tty', 'mem_limit', 'ports', 'environment', 'dns', 'volumes', 'entrypoint', 'privileged', 'volumes_from', 'net', 'working_dir']
DOCKER_CONFIG_HINTS = {
'link' : 'links',
'port' : 'ports',
'privilege' : 'privileged',
'priviliged': 'privileged',
'privilige' : 'privileged',
'volume' : 'volumes',
'workdir' : 'working_dir',
}
VALID_NAME_CHARS = '[a-zA-Z0-9]'
class BuildError(Exception):
def __init__(self, service, reason):
self.service = service
self.reason = reason
class CannotBeScaledError(Exception):
pass
class ConfigError(ValueError):
pass
VolumeSpec = namedtuple('VolumeSpec', 'external internal mode')
ServiceName = namedtuple('ServiceName', 'project service number')
class Service(object):
def __init__(self, name, client=None, project='default', links=None, volumes_from=None, **options):
if not re.match('^%s+$' % VALID_NAME_CHARS, name):
raise ConfigError('Invalid service name "%s" - only %s are allowed' % (name, VALID_NAME_CHARS))
if not re.match('^%s+$' % VALID_NAME_CHARS, project):
raise ConfigError('Invalid project name "%s" - only %s are allowed' % (project, VALID_NAME_CHARS))
if 'image' in options and 'build' in options:
raise ConfigError('Service %s has both an image and build path specified. A service can either be built to image or use an existing image, not both.' % name)
supported_options = DOCKER_CONFIG_KEYS + ['build', 'expose']
for k in options:
if k not in supported_options:
msg = "Unsupported config option for %s service: '%s'" % (name, k)
if k in DOCKER_CONFIG_HINTS:
msg += " (did you mean '%s'?)" % DOCKER_CONFIG_HINTS[k]
raise ConfigError(msg)
self.name = name
self.client = client
self.project = project
self.links = links or []
self.volumes_from = volumes_from or []
self.options = options
def containers(self, stopped=False, one_off=False):
return [Container.from_ps(self.client, container)
for container in self.client.containers(all=stopped)
if self.has_container(container, one_off=one_off)]
def has_container(self, container, one_off=False):
"""Return True if `container` was created to fulfill this service."""
name = get_container_name(container)
if not name or not is_valid_name(name, one_off):
return False
project, name, _number = parse_name(name)
return project == self.project and name == self.name
def get_container(self, number=1):
"""Return a :class:`fig.container.Container` for this service. The
container must be active, and match `number`.
"""
for container in self.client.containers():
if not self.has_container(container):
continue
_, _, container_number = parse_name(get_container_name(container))
if container_number == number:
return Container.from_ps(self.client, container)
raise ValueError("No container found for %s_%s" % (self.name, number))
def start(self, **options):
for c in self.containers(stopped=True):
self.start_container_if_stopped(c, **options)
def stop(self, **options):
for c in self.containers():
log.info("Stopping %s..." % c.name)
c.stop(**options)
def kill(self, **options):
for c in self.containers():
log.info("Killing %s..." % c.name)
c.kill(**options)
def restart(self, **options):
for c in self.containers():
log.info("Restarting %s..." % c.name)
c.restart(**options)
def scale(self, desired_num):
"""
Adjusts the number of containers to the specified number and ensures they are running.
- creates containers until there are at least `desired_num`
- stops containers until there are at most `desired_num` running
- starts containers until there are at least `desired_num` running
- removes all stopped containers
"""
if not self.can_be_scaled():
raise CannotBeScaledError()
# Create enough containers
containers = self.containers(stopped=True)
while len(containers) < desired_num:
containers.append(self.create_container())
running_containers = []
stopped_containers = []
for c in containers:
if c.is_running:
running_containers.append(c)
else:
stopped_containers.append(c)
running_containers.sort(key=lambda c: c.number)
stopped_containers.sort(key=lambda c: c.number)
# Stop containers
while len(running_containers) > desired_num:
c = running_containers.pop()
log.info("Stopping %s..." % c.name)
c.stop(timeout=1)
stopped_containers.append(c)
# Start containers
while len(running_containers) < desired_num:
c = stopped_containers.pop(0)
log.info("Starting %s..." % c.name)
self.start_container(c)
running_containers.append(c)
self.remove_stopped()
def remove_stopped(self, **options):
for c in self.containers(stopped=True):
if not c.is_running:
log.info("Removing %s..." % c.name)
c.remove(**options)
def create_container(self, one_off=False, **override_options):
"""
Create a container for this service. If the image doesn't exist, attempt to pull
it.
"""
container_options = self._get_container_create_options(override_options, one_off=one_off)
try:
return Container.create(self.client, **container_options)
except APIError as e:
if e.response.status_code == 404 and e.explanation and 'No such image' in str(e.explanation):
log.info('Pulling image %s...' % container_options['image'])
output = self.client.pull(container_options['image'], stream=True)
stream_output(output, sys.stdout)
return Container.create(self.client, **container_options)
raise
def recreate_containers(self, **override_options):
"""
If a container for this service doesn't exist, create and start one. If there are
any, stop them, create+start new ones, and remove the old containers.
"""
containers = self.containers(stopped=True)
if not containers:
log.info("Creating %s..." % self._next_container_name(containers))
container = self.create_container(**override_options)
self.start_container(container)
return [(None, container)]
else:
tuples = []
for c in containers:
log.info("Recreating %s..." % c.name)
tuples.append(self.recreate_container(c, **override_options))
return tuples
def recreate_container(self, container, **override_options):
"""Recreate a container. An intermediate container is created so that
the new container has the same name, while still supporting
`volumes-from` the original container.
"""
try:
container.stop()
except APIError as e:
if (e.response.status_code == 500
and e.explanation
and 'no such process' in str(e.explanation)):
pass
else:
raise
intermediate_container = Container.create(
self.client,
image=container.image,
entrypoint=['/bin/echo'],
command=[],
)
intermediate_container.start(volumes_from=container.id)
intermediate_container.wait()
container.remove()
options = dict(override_options)
new_container = self.create_container(**options)
self.start_container(new_container, intermediate_container=intermediate_container)
intermediate_container.remove()
return (intermediate_container, new_container)
def start_container_if_stopped(self, container, **options):
if container.is_running:
return container
else:
log.info("Starting %s..." % container.name)
return self.start_container(container, **options)
def start_container(self, container=None, intermediate_container=None, **override_options):
container = container or self.create_container(**override_options)
options = dict(self.options, **override_options)
ports = dict(split_port(port) for port in options.get('ports') or [])
volume_bindings = dict(
build_volume_binding(parse_volume_spec(volume))
for volume in options.get('volumes') or []
if ':' in volume)
privileged = options.get('privileged', False)
net = options.get('net', 'bridge')
dns = options.get('dns', None)
container.start(
links=self._get_links(link_to_self=options.get('one_off', False)),
port_bindings=ports,
binds=volume_bindings,
volumes_from=self._get_volumes_from(intermediate_container),
privileged=privileged,
network_mode=net,
dns=dns,
)
return container
def start_or_create_containers(self):
containers = self.containers(stopped=True)
if not containers:
log.info("Creating %s..." % self._next_container_name(containers))
new_container = self.create_container()
return [self.start_container(new_container)]
else:
return [self.start_container_if_stopped(c) for c in containers]
def get_linked_names(self):
return [s.name for (s, _) in self.links]
def _next_container_name(self, all_containers, one_off=False):
bits = [self.project, self.name]
if one_off:
bits.append('run')
return '_'.join(bits + [str(self._next_container_number(all_containers))])
def _next_container_number(self, all_containers):
numbers = [parse_name(c.name).number for c in all_containers]
return 1 if not numbers else max(numbers) + 1
def _get_links(self, link_to_self):
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))
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))
return links
def _get_volumes_from(self, intermediate_container=None):
volumes_from = []
for volume_source in self.volumes_from:
if isinstance(volume_source, Service):
containers = volume_source.containers(stopped=True)
if not containers:
volumes_from.append(volume_source.create_container().id)
else:
volumes_from.extend(map(attrgetter('id'), containers))
elif isinstance(volume_source, Container):
volumes_from.append(volume_source.id)
if intermediate_container:
volumes_from.append(intermediate_container.id)
return volumes_from
def _get_container_create_options(self, override_options, one_off=False):
container_options = dict((k, self.options[k]) for k in DOCKER_CONFIG_KEYS if k in self.options)
container_options.update(override_options)
container_options['name'] = self._next_container_name(
self.containers(stopped=True, one_off=one_off),
one_off)
# If a qualified hostname was given, split it into an
# unqualified hostname and a domainname unless domainname
# was also given explicitly. This matches the behavior of
# the official Docker CLI in that scenario.
if ('hostname' in container_options
and 'domainname' not in container_options
and '.' in container_options['hostname']):
parts = container_options['hostname'].partition('.')
container_options['hostname'] = parts[0]
container_options['domainname'] = parts[2]
if 'ports' in container_options or 'expose' in self.options:
ports = []
all_ports = container_options.get('ports', []) + self.options.get('expose', [])
for port in all_ports:
port = str(port)
if ':' in port:
port = port.split(':')[-1]
if '/' in port:
port = tuple(port.split('/'))
ports.append(port)
container_options['ports'] = ports
if 'volumes' in container_options:
container_options['volumes'] = dict(
(parse_volume_spec(v).internal, {})
for v in container_options['volumes'])
if 'environment' in container_options:
if isinstance(container_options['environment'], list):
container_options['environment'] = dict(split_env(e) for e in container_options['environment'])
container_options['environment'] = dict(resolve_env(k, v) for k, v in container_options['environment'].iteritems())
if self.can_be_built():
if len(self.client.images(name=self._build_tag_name())) == 0:
self.build()
container_options['image'] = self._build_tag_name()
# Delete options which are only used when starting
for key in ['privileged', 'net', 'dns']:
if key in container_options:
del container_options[key]
return container_options
def build(self, no_cache=False):
log.info('Building %s...' % self.name)
build_output = self.client.build(
self.options['build'],
tag=self._build_tag_name(),
stream=True,
rm=True,
nocache=no_cache,
)
try:
all_events = stream_output(build_output, sys.stdout)
except StreamOutputError, e:
raise BuildError(self, unicode(e))
image_id = None
for event in all_events:
if 'stream' in event:
match = re.search(r'Successfully built ([0-9a-f]+)', event.get('stream', ''))
if match:
image_id = match.group(1)
if image_id is None:
raise BuildError(self)
return image_id
def can_be_built(self):
return 'build' in self.options
def _build_tag_name(self):
"""
The tag to give to images built for this service.
"""
return '%s_%s' % (self.project, self.name)
def can_be_scaled(self):
for port in self.options.get('ports', []):
if ':' in str(port):
return False
return True
def pull(self, insecure_registry=False):
if 'image' in self.options:
log.info('Pulling %s (%s)...' % (self.name, self.options.get('image')))
self.client.pull(
self.options.get('image'),
insecure_registry=insecure_registry
)
NAME_RE = re.compile(r'^([^_]+)_([^_]+)_(run_)?(\d+)$')
def is_valid_name(name, one_off=False):
match = NAME_RE.match(name)
if match is None:
return False
if one_off:
return match.group(3) == 'run_'
else:
return match.group(3) is None
def parse_name(name):
match = NAME_RE.match(name)
(project, service_name, _, suffix) = match.groups()
return ServiceName(project, service_name, int(suffix))
def get_container_name(container):
if not container.get('Name') and not container.get('Names'):
return None
# inspect
if 'Name' in container:
return container['Name']
# ps
for name in container['Names']:
if len(name.split('/')) == 2:
return name[1:]
def parse_volume_spec(volume_config):
parts = volume_config.split(':')
if len(parts) > 3:
raise ConfigError("Volume %s has incorrect format, should be "
"external:internal[:mode]" % volume_config)
if len(parts) == 1:
return VolumeSpec(None, parts[0], 'rw')
if len(parts) == 2:
parts.append('rw')
external, internal, mode = parts
if mode not in ('rw', 'ro'):
raise ConfigError("Volume %s has invalid mode (%s), should be "
"one of: rw, ro." % (volume_config, mode))
return VolumeSpec(external, internal, mode)
def build_volume_binding(volume_spec):
internal = {'bind': volume_spec.internal, 'ro': volume_spec.mode == 'ro'}
external = os.path.expanduser(volume_spec.external)
return os.path.abspath(os.path.expandvars(external)), internal
def split_port(port):
parts = str(port).split(':')
if not 1 <= len(parts) <= 3:
raise ConfigError('Invalid port "%s", should be '
'[[remote_ip:]remote_port:]port[/protocol]' % port)
if len(parts) == 1:
internal_port, = parts
return internal_port, None
if len(parts) == 2:
external_port, internal_port = parts
return internal_port, external_port
external_ip, external_port, internal_port = parts
return internal_port, (external_ip, external_port or None)
def split_env(env):
if '=' in env:
return env.split('=', 1)
else:
return env, None
def resolve_env(key, val):
if val is not None:
return key, val
elif key in os.environ:
return key, os.environ[key]
else:
return key, ''

View File

@@ -1,5 +1,6 @@
mock >= 1.0.1
nose
nose==1.3.4
git+https://github.com/pyinstaller/pyinstaller.git@12e40471c77f588ea5be352f7219c873ddaae056#egg=pyinstaller
unittest2
flake8
unittest2==0.8.0
flake8==2.3.0
pep8==1.6.1

View File

@@ -1,7 +1,8 @@
PyYAML==3.10
docker-py==0.5.3
docker-py==1.2.3
dockerpty==0.3.4
docopt==0.6.1
requests==2.2.1
requests==2.6.1
six==1.7.3
texttable==0.8.1
texttable==0.8.2
websocket-client==0.11.0

View File

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

View File

@@ -1,5 +0,0 @@
#!/bin/bash
set -ex
pushd docs
fig run jekyll jekyll build
popd

View File

@@ -1,8 +1,12 @@
#!/bin/sh
#!/bin/bash
set -ex
mkdir -p `pwd`/dist
chmod 777 `pwd`/dist
docker build -t fig .
docker run -u user -v `pwd`/dist:/code/dist fig pyinstaller -F bin/fig
mv dist/fig dist/fig-Linux-x86_64
docker run -u user -v `pwd`/dist:/code/dist fig dist/fig-Linux-x86_64 --version
TAG="docker-compose"
docker build -t "$TAG" .
docker run \
--rm \
--user=user \
--volume="$(pwd):/code" \
--entrypoint="script/build-linux-inner" \
"$TAG"

10
script/build-linux-inner Executable file
View File

@@ -0,0 +1,10 @@
#!/bin/bash
set -ex
mkdir -p `pwd`/dist
chmod 777 `pwd`/dist
pyinstaller -F bin/docker-compose
mv dist/docker-compose dist/docker-compose-Linux-x86_64
dist/docker-compose-Linux-x86_64 --version

View File

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

15
script/ci Executable file
View File

@@ -0,0 +1,15 @@
#!/bin/bash
# This should be run inside a container built from the Dockerfile
# at the root of the repo:
#
# $ TAG="docker-compose:$(git rev-parse --short HEAD)"
# $ docker build -t "$TAG" .
# $ docker run --rm --volume="/var/run/docker.sock:/var/run/docker.sock" --volume="$(pwd)/.git:/code/.git" -e "TAG=$TAG" --entrypoint="script/ci" "$TAG"
set -e
export DOCKER_VERSIONS=all
. script/test-versions
>&2 echo "Building Linux binary"
su -c script/build-linux-inner user

View File

@@ -1,3 +1,3 @@
#!/bin/sh
find . -type f -name '*.pyc' -delete
rm -rf docs/_site build dist fig.egg-info
rm -rf docs/_site build dist docker-compose.egg-info

View File

@@ -1,29 +0,0 @@
#!/bin/bash
set -ex
script/build-docs
pushd docs/_site
export GIT_DIR=.git-gh-pages
export GIT_WORK_TREE=.
if [ ! -d "$GIT_DIR" ]; then
git init
fi
if !(git remote | grep origin); then
git remote add origin git@github.com:docker/fig.git
fi
git fetch origin
git reset --soft origin/gh-pages
echo ".git-gh-pages" > .gitignore
git add -A .
git commit -m "update" || echo "didn't commit"
git push origin master:gh-pages
popd

21
script/dev Executable file
View File

@@ -0,0 +1,21 @@
#!/bin/bash
# This is a script for running Compose inside a Docker container. It's handy for
# development.
#
# $ ln -s `pwd`/script/dev /usr/local/bin/docker-compose
# $ cd /a/compose/project
# $ docker-compose up
#
set -e
# Follow symbolic links
if [ -h "$0" ]; then
DIR=$(readlink "$0")
else
DIR=$0
fi
DIR="$(dirname "$DIR")"/..
docker build -t docker-compose $DIR
exec docker run -i -t -v /var/run/docker.sock:/var/run/docker.sock -v `pwd`:`pwd` -w `pwd` docker-compose $@

88
script/dind Executable file
View File

@@ -0,0 +1,88 @@
#!/bin/bash
set -e
# DinD: a wrapper script which allows docker to be run inside a docker container.
# Original version by Jerome Petazzoni <jerome@docker.com>
# See the blog post: http://blog.docker.com/2013/09/docker-can-now-run-within-docker/
#
# This script should be executed inside a docker container in privilieged mode
# ('docker run --privileged', introduced in docker 0.6).
# Usage: dind CMD [ARG...]
# apparmor sucks and Docker needs to know that it's in a container (c) @tianon
export container=docker
# First, make sure that cgroups are mounted correctly.
CGROUP=/cgroup
mkdir -p "$CGROUP"
if ! mountpoint -q "$CGROUP"; then
mount -n -t tmpfs -o uid=0,gid=0,mode=0755 cgroup $CGROUP || {
echo >&2 'Could not make a tmpfs mount. Did you use --privileged?'
exit 1
}
fi
if [ -d /sys/kernel/security ] && ! mountpoint -q /sys/kernel/security; then
mount -t securityfs none /sys/kernel/security || {
echo >&2 'Could not mount /sys/kernel/security.'
echo >&2 'AppArmor detection and -privileged mode might break.'
}
fi
# Mount the cgroup hierarchies exactly as they are in the parent system.
for SUBSYS in $(cut -d: -f2 /proc/1/cgroup); do
mkdir -p "$CGROUP/$SUBSYS"
if ! mountpoint -q $CGROUP/$SUBSYS; then
mount -n -t cgroup -o "$SUBSYS" cgroup "$CGROUP/$SUBSYS"
fi
# The two following sections address a bug which manifests itself
# by a cryptic "lxc-start: no ns_cgroup option specified" when
# trying to start containers withina container.
# The bug seems to appear when the cgroup hierarchies are not
# mounted on the exact same directories in the host, and in the
# container.
# Named, control-less cgroups are mounted with "-o name=foo"
# (and appear as such under /proc/<pid>/cgroup) but are usually
# mounted on a directory named "foo" (without the "name=" prefix).
# Systemd and OpenRC (and possibly others) both create such a
# cgroup. To avoid the aforementioned bug, we symlink "foo" to
# "name=foo". This shouldn't have any adverse effect.
name="${SUBSYS#name=}"
if [ "$name" != "$SUBSYS" ]; then
ln -s "$SUBSYS" "$CGROUP/$name"
fi
# Likewise, on at least one system, it has been reported that
# systemd would mount the CPU and CPU accounting controllers
# (respectively "cpu" and "cpuacct") with "-o cpuacct,cpu"
# but on a directory called "cpu,cpuacct" (note the inversion
# in the order of the groups). This tries to work around it.
if [ "$SUBSYS" = 'cpuacct,cpu' ]; then
ln -s "$SUBSYS" "$CGROUP/cpu,cpuacct"
fi
done
# Note: as I write those lines, the LXC userland tools cannot setup
# a "sub-container" properly if the "devices" cgroup is not in its
# own hierarchy. Let's detect this and issue a warning.
if ! grep -q :devices: /proc/1/cgroup; then
echo >&2 'WARNING: the "devices" cgroup should be in its own hierarchy.'
fi
if ! grep -qw devices /proc/1/cgroup; then
echo >&2 'WARNING: it looks like the "devices" cgroup is not mounted.'
fi
# Mount /tmp
mount -t tmpfs none /tmp
if [ $# -gt 0 ]; then
exec "$@"
fi
echo >&2 'ERROR: No command specified.'
echo >&2 'You probably want to run hack/make.sh, or maybe a shell?'

11
script/docs Executable file
View File

@@ -0,0 +1,11 @@
#!/bin/sh
set -ex
# import the existing docs build cmds from docker/docker
DOCSPORT=8000
GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
DOCKER_DOCS_IMAGE="compose-docs$GIT_BRANCH"
DOCKER_RUN_DOCS="docker run --rm -it -e NOCACHE"
docker build -t "$DOCKER_DOCS_IMAGE" -f docs/Dockerfile .
$DOCKER_RUN_DOCS -p $DOCSPORT:8000 "$DOCKER_DOCS_IMAGE" mkdocs serve

View File

@@ -1,2 +0,0 @@
#!/bin/bash
open file://`pwd`/docs/_site/index.html

53
script/prepare-osx Executable file
View File

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

4
script/shell Executable file
View File

@@ -0,0 +1,4 @@
#!/bin/sh
set -ex
docker build -t docker-compose .
exec docker run -v /var/run/docker.sock:/var/run/docker.sock -v `pwd`:/code -ti --rm --entrypoint bash docker-compose

View File

@@ -1,12 +1,17 @@
#!/bin/sh
#!/bin/bash
# See CONTRIBUTING.md for usage.
set -ex
target="tests"
TAG="docker-compose:$(git rev-parse --short HEAD)"
if [[ -n "$@" ]]; then
target="$@"
fi
docker build -t fig .
docker run -v /var/run/docker.sock:/var/run/docker.sock fig flake8 --exclude=packages fig
docker run -v /var/run/docker.sock:/var/run/docker.sock fig nosetests $target
docker build -t "$TAG" .
docker run \
--rm \
--volume="/var/run/docker.sock:/var/run/docker.sock" \
-e DOCKER_VERSIONS \
-e "TAG=$TAG" \
-e "affinity:image==$TAG" \
--entrypoint="script/test-versions" \
"$TAG" \
"$@"

26
script/test-versions Executable file
View File

@@ -0,0 +1,26 @@
#!/bin/bash
# This should be run inside a container built from the Dockerfile
# at the root of the repo - script/test will do it automatically.
set -e
>&2 echo "Running lint checks"
flake8 compose tests setup.py
if [ "$DOCKER_VERSIONS" == "" ]; then
DOCKER_VERSIONS="default"
elif [ "$DOCKER_VERSIONS" == "all" ]; then
DOCKER_VERSIONS="$ALL_DOCKER_VERSIONS"
fi
for version in $DOCKER_VERSIONS; do
>&2 echo "Running tests against Docker $version"
docker run \
--rm \
--privileged \
--volume="/var/lib/docker" \
-e "DOCKER_VERSION=$version" \
--entrypoint="script/dind" \
"$TAG" \
script/wrapdocker nosetests "$@"
done

View File

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

18
script/wrapdocker Executable file
View File

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

View File

@@ -27,13 +27,15 @@ def find_version(*file_paths):
install_requires = [
'docopt >= 0.6.1, < 0.7',
'PyYAML >= 3.10, < 4',
'requests >= 2.2.1, < 3',
'requests >= 2.6.1, < 2.7',
'texttable >= 0.8.1, < 0.9',
'websocket-client >= 0.11.0, < 0.12',
'docker-py >= 0.5, < 0.6',
'websocket-client >= 0.11.0, < 1.0',
'docker-py >= 1.2.3, < 1.3',
'dockerpty >= 0.3.4, < 0.4',
'six >= 1.3.0, < 2',
]
tests_require = [
'mock >= 1.0.1',
'nose',
@@ -47,19 +49,19 @@ if sys.version_info < (2, 7):
setup(
name='fig',
version=find_version("fig", "__init__.py"),
description='Punctual, lightweight development environments using Docker',
url='http://www.fig.sh/',
name='docker-compose',
version=find_version("compose", "__init__.py"),
description='Multi-container orchestration for Docker',
url='https://www.docker.com/',
author='Docker, Inc.',
license='Apache License 2.0',
packages=find_packages(exclude=[ 'tests.*', 'tests' ]),
packages=find_packages(exclude=['tests.*', 'tests']),
include_package_data=True,
test_suite='nose.collector',
install_requires=install_requires,
tests_require=tests_require,
entry_points="""
[console_scripts]
fig=fig.cli.main:main
docker-compose=compose.cli.main:main
""",
)

View File

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

View File

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

2
tests/fixtures/build-ctx/Dockerfile vendored Normal file
View File

@@ -0,0 +1,2 @@
FROM busybox:latest
CMD echo "success"

View File

@@ -0,0 +1,2 @@
foo:
build: ../build-ctx/

View File

@@ -0,0 +1,5 @@
implicit:
image: composetest_test
explicit:
image: composetest_test
command: [ "/bin/true" ]

View File

@@ -1,5 +0,0 @@
implicit:
image: figtest_test
explicit:
image: figtest_test
command: [ "/bin/true" ]

View File

@@ -0,0 +1,3 @@
FROM busybox
VOLUME /data
CMD top

View File

@@ -0,0 +1,2 @@
service:
build: .

View File

@@ -1,2 +0,0 @@
service:
build: tests/fixtures/dockerfile_with_entrypoint

View File

@@ -0,0 +1,4 @@
web:
image: busybox
command: /bin/true
env_file: ./test.env

1
tests/fixtures/env-file/test.env vendored Normal file
View File

@@ -0,0 +1 @@
FOO=1

11
tests/fixtures/env/one.env vendored Normal file
View File

@@ -0,0 +1,11 @@
# Keep the blank lines and comments in this file, please
ONE=2
TWO=1
# (thanks)
THREE=3
FOO=bar
# FOO=somethingelse

4
tests/fixtures/env/resolve.env vendored Normal file
View File

@@ -0,0 +1,4 @@
FILE_DEF=F1
FILE_DEF_EMPTY=
ENV_DEF
NO_DEF

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