Compare commits

..

267 Commits

Author SHA1 Message Date
Milas Bowman
68cf8d5ca0 Merge pull request #9588 from milas/compose-go-v1.2.8
deps: upgrade to compose-go v1.2.8
2022-06-23 10:53:26 -04:00
Milas Bowman
94d6dff89d deps: upgrade to compose-go v1.2.8
https://github.com/compose-spec/compose-go/releases/tag/v1.2.8

Signed-off-by: Milas Bowman <milas.bowman@docker.com>
2022-06-23 10:45:45 -04:00
Guillaume Lours
e5dcb8a8f8 Merge pull request #9568 from milas/e2e-init-output
e2e: improve test output on failures
2022-06-18 20:59:56 +02:00
Milas Bowman
ab25aabfb9 Merge pull request #9311 from quite/no-ansi-be-kind
Don't use ansi even when deprecated option is requested
2022-06-16 17:15:19 -04:00
Milas Bowman
7f441eb013 e2e: improve test output on failures
This is mostly marking a bunch of the run methods as helpers so
that the internal assertions they do will show the line number of
the calling test instead.

There's also some small tweaks around the plugin initialization to
help with the output in the event that it fails to make it easier
to debug what went wrong.

Signed-off-by: Milas Bowman <milas.bowman@docker.com>
2022-06-16 17:05:43 -04:00
Guillaume Lours
4270987d7c Merge pull request #9567 from milas/e2e-cmd
e2e: ensure all Compose cmds standalone compatible
2022-06-16 23:02:14 +02:00
Milas Bowman
3248b7b335 e2e: use Compose for exec test
This was using `docker exec` on Compose containers instead of
`docker compose exec` (and `docker-compose exec` for standalone).

Thanks to @glours for catching!

Signed-off-by: Milas Bowman <milas.bowman@docker.com>
2022-06-16 16:44:21 -04:00
Milas Bowman
4ea3ba77b2 Merge pull request #9559 from venthur/fix_spell
fixed some spelling mistakes
2022-06-16 15:56:44 -04:00
Milas Bowman
9622395c8a e2e: ensure all Compose cmds standalone compatible
The E2E tests can be run in plugin (`docker compose`) or standalone
(`docker-compose`) mode. Existing logic was in place to ensure that
the helper method is always used, which will invoke the right one
based on how tests are being executed.

However, this logic was too easy to (unintentionally) bypass given
the myriad of ways that commands can be run. The check has been
made stricter and pushed to a lower-level to more aggressively
catch instances.

As a result, a bunch of calls to `RunDockerCmd` are now updated
to be `RunDockerComposeCmd`, which will ensure that the invocation
is correct based on test mode.

Signed-off-by: Milas Bowman <milas.bowman@docker.com>
2022-06-16 15:51:54 -04:00
Guillaume Lours
bec4ea4ab0 Merge pull request #9566 from milas/e2e-ps-stdout
e2e: fix spurious `ps` failures
2022-06-16 18:07:22 +02:00
Guillaume Lours
3f10753178 Merge pull request #9563 from milas/e2e-env
e2e: isolate test command env from system env
2022-06-16 18:05:39 +02:00
Milas Bowman
152c2d9a33 e2e: fix spurious ps failures
Use the command `stdout` instead of combined `stdout` + `stderr`
for assertions to avoid failures from any CLI logging such as
warnings, which will be on `stderr`.

Signed-off-by: Milas Bowman <milas.bowman@docker.com>
2022-06-16 11:28:29 -04:00
Milas Bowman
a261682ca8 e2e: fix per-command env overrides
Signed-off-by: Milas Bowman <milas.bowman@docker.com>
2022-06-16 11:27:39 -04:00
Nick Sieger
71b89c2c1b Merge pull request #9564 from docker/nicksieger/9562
down: fix COMPOSE_REMOVE_ORPHANS env name and add test
2022-06-16 09:01:00 -05:00
Milas Bowman
ccd87311e8 e2e: always set HOME + USER for cmd env
Signed-off-by: Milas Bowman <milas.bowman@docker.com>
2022-06-16 09:44:40 -04:00
Nick Sieger
49fb4ca25f down: fix COMPOSE_REMOVE_ORPHANS env name and add test
Fixes #9562.

Signed-off-by: Nick Sieger <nick@nicksieger.com>
2022-06-16 08:40:40 -05:00
Milas Bowman
3ae6c52e8a e2e: add extra tools needed for ddev test
Signed-off-by: Milas Bowman <milas.bowman@docker.com>
2022-06-16 09:38:25 -04:00
Milas Bowman
1c41df8f56 e2e: robustness changes for ddev test
The most important change here is to ensure that the correct Compose
standalone binary is used by `ddev`. Since it invokes Compose itself,
we need to ensure that `PATH` is set appropriately such that it finds
the binary we want to test rather than something from the system.

As part of this, the rest of the environment has been isolated, which
should make the test more reliable, and avoids polluting `~/.ddev`
with test artifacts by using a tmpdir as `HOME` for the test instead
of the user's real home folder.

Signed-off-by: Milas Bowman <milas.bowman@docker.com>
2022-06-16 08:59:02 -04:00
Milas Bowman
ea8341865d e2e: isolate test command env from system env
When running Docker / Compose commands, do NOT inherit the system
environment to ensure that the tests are reproducible regardless
of host settings.

Additionally, per-command environment overrides are provided to
the command instead of using `os.SetEnv`, as this is not safe when
running tests in parallel (`testing.T::SetEnv` will actually error
if used in this way!)

Signed-off-by: Milas Bowman <milas.bowman@docker.com>
2022-06-16 08:30:57 -04:00
Guillaume Lours
de0f23315b Merge pull request #9561 from milas/e2e-subtests
e2e: fix subtests and block parallel unsafe tests
2022-06-16 11:15:30 +02:00
Milas Bowman
d9065050fd e2e: fix subtests and block parallel unsafe tests
The big mechanical change here is to NOT store `t` as a field on
the `CLI` object (which has been renamed as well to fix the odd
capitalization). The way the tests are structured meant that the
"subtests" were using the _parent_ `*testing.T` instance, which
causes various oddities including confusing messages on failure
about a child test causing a panic in the parent.

Additionally, a few tests have been blocked from running in
parallel because they are sharing `compose.yaml` fixtures and
can fail as a result (e.g. due to a port conflict on the host).
I'll fix these in follow-up PRs but want to focus on correctness
for the tests before optimizing them.

Signed-off-by: Milas Bowman <milas.bowman@docker.com>
2022-06-15 16:27:30 -04:00
Nicolas De Loof
005fc25823 add support for setting secret from env variable
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-06-15 17:06:48 +02:00
Bastian Venthur
8a5d555de7 fixed some spelling mistakes
that's all :)

Signed-off-by: Bastian Venthur <venthur@debian.org>
2022-06-14 21:33:36 +02:00
Nick Sieger
a48f1e8c13 Merge pull request #9547 from docker/nicksieger/e2e-ps-unmarshal
e2e: unmarshal json into container summaries
2022-06-14 10:33:53 -05:00
Guillaume Lours
57975094cc Merge pull request #9552 from glours/fix-deploy-cpus-issue
Fix deploy cpus issue and support pids limit
2022-06-14 11:41:35 +02:00
Laura Brehm
80b7a8d274 Only start direct dependencies of service on compose run ...
Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2022-06-14 07:32:15 +02:00
Guillaume Lours
e111b651b3 add deploy.resourses.limits.pids to the managed resources
Signed-off-by: Guillaume Lours <guillaume.lours@docker.com>
2022-06-13 17:21:48 +02:00
Guillaume Lours
638d78516a parse deploy.resources.limit.cpus as float before converting
Signed-off-by: Guillaume Lours <guillaume.lours@docker.com>
2022-06-13 17:21:48 +02:00
Guillaume Lours
8862f95858 Merge pull request #9514 from ulyssessouza/fix-bindmounts
Fix bind mounts when in project volumes definition
2022-06-09 11:15:24 +02:00
Guillaume Lours
c2533e2341 Merge pull request #9546 from docker/dependabot/go_modules/github.com/containerd/containerd-1.6.6
Bump github.com/containerd/containerd from 1.6.2 to 1.6.6
2022-06-08 22:44:37 +02:00
dependabot[bot]
08d8fddb8d Bump github.com/containerd/containerd from 1.6.2 to 1.6.6
Bumps [github.com/containerd/containerd](https://github.com/containerd/containerd) from 1.6.2 to 1.6.6.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v1.6.2...v1.6.6)

---
updated-dependencies:
- dependency-name: github.com/containerd/containerd
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-08 20:15:39 +00:00
Guillaume Lours
5bd1a9bda3 Merge pull request #9545 from docker/nicksieger/ps-e2e-test
e2e: add test for ps
2022-06-08 22:13:37 +02:00
Nick Sieger
ebb45b400c e2e: unmarshal json into container summaries
Signed-off-by: Nick Sieger <nick@nicksieger.com>
2022-06-08 15:13:31 -05:00
Guillaume Lours
78605e58b2 Merge pull request #9541 from docker/dependabot/go_modules/github.com/AlecAivazis/survey/v2-2.3.5
Bump github.com/AlecAivazis/survey/v2 from 2.3.2 to 2.3.5
2022-06-08 21:09:27 +02:00
Guillaume Lours
0ea3504fd5 Merge pull request #9544 from docker/dependabot/go_modules/github.com/docker/buildx-0.8.2
Bump github.com/docker/buildx from 0.8.1 to 0.8.2
2022-06-08 21:07:42 +02:00
Nick Sieger
c83133f73b e2e: add test for ps
Signed-off-by: Nick Sieger <nick@nicksieger.com>
2022-06-08 14:05:20 -05:00
Guillaume Lours
0f6366afac Merge pull request #9539 from docker/nicksieger/9527
Fix ps ports output; includes unit test
2022-06-08 21:02:32 +02:00
dependabot[bot]
26554884db Bump github.com/docker/buildx from 0.8.1 to 0.8.2
Bumps [github.com/docker/buildx](https://github.com/docker/buildx) from 0.8.1 to 0.8.2.
- [Release notes](https://github.com/docker/buildx/releases)
- [Commits](https://github.com/docker/buildx/compare/v0.8.1...v0.8.2)

---
updated-dependencies:
- dependency-name: github.com/docker/buildx
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-08 19:00:51 +00:00
dependabot[bot]
a9d3bfdacd Bump github.com/AlecAivazis/survey/v2 from 2.3.2 to 2.3.5
Bumps [github.com/AlecAivazis/survey/v2](https://github.com/AlecAivazis/survey) from 2.3.2 to 2.3.5.
- [Release notes](https://github.com/AlecAivazis/survey/releases)
- [Commits](https://github.com/AlecAivazis/survey/compare/v2.3.2...v2.3.5)

---
updated-dependencies:
- dependency-name: github.com/AlecAivazis/survey/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-08 19:00:49 +00:00
Guillaume Lours
0275fac355 Merge pull request #9538 from docker/dependabot/go_modules/github.com/stretchr/testify-1.7.2
Bump github.com/stretchr/testify from 1.7.0 to 1.7.2
2022-06-08 20:59:59 +02:00
Guillaume Lours
fc5d7a45e0 Merge pull request #9485 from docker/dependabot/go_modules/github.com/hashicorp/go-version-1.5.0
Bump github.com/hashicorp/go-version from 1.3.0 to 1.5.0
2022-06-08 20:59:21 +02:00
Guillaume Lours
9465e433fc Merge pull request #9307 from docker/dependabot/go_modules/github.com/cnabio/cnab-to-oci-0.3.3
Bump github.com/cnabio/cnab-to-oci from 0.3.1-beta1 to 0.3.3
2022-06-08 20:59:13 +02:00
dependabot[bot]
5b02eadb74 Bump github.com/cnabio/cnab-to-oci from 0.3.1-beta1 to 0.3.3
Bumps [github.com/cnabio/cnab-to-oci](https://github.com/cnabio/cnab-to-oci) from 0.3.1-beta1 to 0.3.3.
- [Release notes](https://github.com/cnabio/cnab-to-oci/releases)
- [Commits](https://github.com/cnabio/cnab-to-oci/compare/v0.3.1-beta1...v0.3.3)

---
updated-dependencies:
- dependency-name: github.com/cnabio/cnab-to-oci
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-08 18:52:18 +00:00
dependabot[bot]
331930c37d Bump github.com/stretchr/testify from 1.7.0 to 1.7.2
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.7.0 to 1.7.2.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.7.0...v1.7.2)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-08 18:52:16 +00:00
Guillaume Lours
6e15989ec8 Merge pull request #9262 from docker/dependabot/go_modules/github.com/theupdateframework/notary-0.7.0
Bump github.com/theupdateframework/notary from 0.6.1 to 0.7.0
2022-06-08 20:51:10 +02:00
Nick Sieger
b2c0d25005 ps: use DisplayablePorts from docker/cli
Fixes #9527.

Signed-off-by: Nick Sieger <nick@nicksieger.com>
2022-06-07 17:11:16 -05:00
Nick Sieger
3599fc8533 mocks: create mocks for compose api.Service
Signed-off-by: Nick Sieger <nick@nicksieger.com>
2022-06-07 17:09:06 -05:00
Ulysses Souza
919f351b4b Fix bind mounts when in project volumes definition
Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
2022-06-02 16:37:24 +02:00
Guillaume Lours
d5528f3a54 Merge pull request #9520 from milas/bugfix-network-ambiguity
fix: prevent network name ambiguity
2022-06-02 12:44:12 +02:00
Guillaume Lours
c64dbbca6c Merge pull request #9523 from laurazard/add-links-container-create
Add links to container create request.
2022-06-02 12:38:41 +02:00
Guillaume Lours
e90c6ba332 Merge pull request #9522 from ulyssessouza/bump-runc-1.1.2
Bump runc -> 1.1.2
2022-06-02 12:27:08 +02:00
Guillaume Lours
51b4651ea9 Merge pull request #9525 from thaJeztah/update_golang_1.18.3
update golang to 1.18.3
2022-06-02 10:03:29 +02:00
Sebastiaan van Stijn
d2639a8638 update golang to 1.18.3
go1.18.3 (released 2022-06-01) includes security fixes to the crypto/rand,
crypto/tls, os/exec, and path/filepath packages, as well as bug fixes to the
compiler, and the crypto/tls and text/template/parse packages. See the Go
1.18.3 milestone on our issue tracker for details:

https://github.com/golang/go/issues?q=milestone%3AGo1.18.3+label%3ACherryPickApproved

Hello gophers,

We have just released Go versions 1.18.3 and 1.17.11, minor point releases.

These minor releases include 4 security fixes following the security policy:

- crypto/rand: rand.Read hangs with extremely large buffers
  On Windows, rand.Read will hang indefinitely if passed a buffer larger than
  1 << 32 - 1 bytes.

  Thanks to Davis Goodin and Quim Muntal, working at Microsoft on the Go toolset,
  for reporting this issue.

  This is [CVE-2022-30634][CVE-2022-30634] and Go issue https://go.dev/issue/52561.
- crypto/tls: session tickets lack random ticket_age_add
  Session tickets generated by crypto/tls did not contain a randomly generated
  ticket_age_add. This allows an attacker that can observe TLS handshakes to
  correlate successive connections by comparing ticket ages during session
  resumption.

  Thanks to GitHub user nervuri for reporting this.

  This is [CVE-2022-30629][CVE-2022-30629] and Go issue https://go.dev/issue/52814.
- `os/exec`: empty `Cmd.Path` can result in running unintended binary on Windows

  If, on Windows, `Cmd.Run`, `cmd.Start`, `cmd.Output`, or `cmd.CombinedOutput`
  are executed when Cmd.Path is unset and, in the working directory, there are
  binaries named either "..com" or "..exe", they will be executed.

  Thanks to Chris Darroch, brian m. carlson, and Mikhail Shcherbakov for reporting
  this.

  This is [CVE-2022-30580][CVE-2022-30580] and Go issue https://go.dev/issue/52574.
- `path/filepath`: Clean(`.\c:`) returns `c:` on Windows

  On Windows, the `filepath.Clean` function could convert an invalid path to a
  valid, absolute path. For example, Clean(`.\c:`) returned `c:`.

  Thanks to Unrud for reporting this issue.

  This is [CVE-2022-29804][CVE-2022-29804] and Go issue https://go.dev/issue/52476.

[CVE-2022-30634]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-30634
[CVE-2022-30629]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-30629
[CVE-2022-30580]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-30580
[CVE-2022-29804]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-29804

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-06-02 09:56:16 +02:00
Laura Brehm
81182fca53 Add links to container create request.
In v1, links were sent alongside the rest of the container create request, as part of `HostConfig`. In v2, links are usually set on the connect container to network request that happens after the create. However, this only happens if the service has one or more networks defined for it. If the services are configured to use the default bridge network, this request is not made and so links are never configured.

Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2022-06-02 02:51:25 +02:00
Ulysses Souza
6e7c949787 Bump runc -> 1.1.2
Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
2022-06-02 00:08:58 +02:00
Milas Bowman
7f32f02817 fix: prevent network name ambiguity
`NetworkInspect` will match a network ID by prefix. While rare,
it's possible that users might use a network name that is also
a valid network ID prefix for a pre-existing Docker network.
(In the reported case, the network was named `db`, for example.)

Fixes #9496.

Signed-off-by: Milas Bowman <milas@tilt.dev>
2022-06-01 15:28:42 -04:00
Guillaume Lours
335decceda Merge pull request #9504 from docker/nicksieger/9427
fix: bring up services with deps with --no-deps
2022-05-31 23:39:19 +02:00
Nick Sieger
28c0fbfdc0 e2e: reproduce bug with links
Signed-off-by: Nick Sieger <nick@nicksieger.com>
2022-05-31 16:32:29 -05:00
Nick Sieger
cacff89cef fix: bring up services with deps with --no-deps
Don't fail on not finding dependent services because they were put in the
disabled slice.

Fixes #9427.

Signed-off-by: Nick Sieger <nick@nicksieger.com>
2022-05-31 16:19:21 -05:00
Guillaume Lours
6756732fe4 Merge pull request #9512 from ulyssessouza/import-dotenv-to-osenv
Import dotenv file to os environment
2022-05-30 18:00:45 +02:00
Ulysses Souza
67c13cf821 Import dotenv file to os environment
Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
2022-05-30 17:36:41 +02:00
Guillaume Lours
dbafb02377 Merge pull request #9499 from glours/add-envs-e2e-tests
add e2e tests to verify env variables priority
2022-05-30 17:19:46 +02:00
Guillaume Lours
a1b3f95709 add e2e tests to verify env variables priority
Signed-off-by: Guillaume Lours <guillaume.lours@docker.com>
2022-05-30 17:11:49 +02:00
Guillaume Lours
5b6b674da9 Merge pull request #9495 from maxcleme/chore/bump_compose_go
bump compose-go to 1.2.6
2022-05-23 15:01:05 +02:00
Maxime CLEMENT
31d9490a0b bump compose-go to 1.2.6
Signed-off-by: Maxime CLEMENT <maxime.clement@docker.com>
2022-05-23 14:51:09 +02:00
Guillaume Lours
6b71073ae2 Merge pull request #9453 from glours/go-18
update golang version to 1.18
2022-05-23 10:41:03 +02:00
Guillaume Lours
e806acce88 Merge pull request #9481 from glours/add-tags-to-build
add tags property to build section
2022-05-23 10:23:40 +02:00
Guillaume Lours
71600a52bf update golang version to 1.18
Signed-off-by: Guillaume Lours <guillaume.lours@docker.com>
2022-05-20 22:13:55 +02:00
Guillaume Lours
285a9c94f7 Merge pull request #9476 from maxcleme/9469-fix-flickering-prompt
fix: prevent flickering prompt when pulling same image from N services
2022-05-20 22:09:02 +02:00
Guillaume Lours
22194f6ef7 Merge pull request #9493 from ulyssessouza/fix-local-e2e-compose-standalone
Fix local run of `make e2e-compose-standalone`
2022-05-20 21:41:42 +02:00
Ulysses Souza
e51fd0a844 Fix local run of make e2e-compose-standalone
Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
2022-05-20 21:31:16 +02:00
Ulysses Souza
b961d49859 Merge pull request #9033 from ulyssessouza/add-e2e-ddev
Add ddev's e2e test
2022-05-20 20:02:58 +02:00
Guillaume Lours
9cae9eb0fe Merge pull request #9488 from ndeloof/attach_profiles
attach _only_ to services declared by project applying profiles
2022-05-20 14:38:08 +02:00
Nicolas De Loof
8d03e29994 attach _only_ to services declared by project applying profiles
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-05-20 10:36:06 +02:00
Guillaume Lours
7ee7becd01 fix TestLocalComposeUp which fail locally
Signed-off-by: Guillaume Lours <guillaume.lours@docker.com>
2022-05-20 07:36:34 +02:00
Randy Fay
97d46a14ef Fix problems with ddev e2e test and minor cleanup, add tmate (#27)
* Add tmate for debugging
* Use -parallel=1 for standaone tests

Signed-off-by: Randy Fay <randy@randyfay.com>
2022-05-19 14:14:03 +02:00
Ulysses Souza
a5a1c5f2f1 Add ddev's e2e test
Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
2022-05-19 14:14:03 +02:00
dependabot[bot]
9db90b35bb Bump github.com/hashicorp/go-version from 1.3.0 to 1.5.0
Bumps [github.com/hashicorp/go-version](https://github.com/hashicorp/go-version) from 1.3.0 to 1.5.0.
- [Release notes](https://github.com/hashicorp/go-version/releases)
- [Changelog](https://github.com/hashicorp/go-version/blob/main/CHANGELOG.md)
- [Commits](https://github.com/hashicorp/go-version/compare/v1.3.0...v1.5.0)

---
updated-dependencies:
- dependency-name: github.com/hashicorp/go-version
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-19 09:30:59 +00:00
Guillaume Lours
a2770b66ff add tags property to build section
Signed-off-by: Guillaume Lours <guillaume.lours@docker.com>
2022-05-18 14:43:54 +02:00
Maxime CLEMENT
48b150beff fix: prevent flickering prompt when pulling same image from N services
Signed-off-by: Maxime CLEMENT <maxime.clement@docker.com>
2022-05-18 08:58:06 +02:00
dependabot[bot]
674aa6a1e4 Bump github.com/theupdateframework/notary from 0.6.1 to 0.7.0
Bumps [github.com/theupdateframework/notary](https://github.com/theupdateframework/notary) from 0.6.1 to 0.7.0.
- [Release notes](https://github.com/theupdateframework/notary/releases)
- [Changelog](https://github.com/notaryproject/notary/blob/master/CHANGELOG.md)
- [Commits](https://github.com/theupdateframework/notary/compare/v0.6.1...v0.7.0)

---
updated-dependencies:
- dependency-name: github.com/theupdateframework/notary
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-17 15:22:26 +00:00
Guillaume Lours
7e3564b7ad Merge pull request #9475 from ndeloof/bump-compose-go
bump compose-go to 1.2.5
2022-05-17 17:21:00 +02:00
Nicolas De Loof
65b827d08f bump compose-go to 1.2.5
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-05-17 17:11:32 +02:00
Jan Vitturi
84f2168f80 Fix extra space printed with --no-log-prefix option
Signed-off-by: Jan Vitturi <vitturi.jan@gmail.com>
2022-05-17 15:20:21 +02:00
Guillaume Lours
a603e27117 cp command from service to host: use the first container found to copy source on the host
Signed-off-by: Guillaume Lours <guillaume.lours@docker.com>
2022-05-10 10:50:40 +02:00
Guillaume Lours
6d9d75406c update usage of the index flag of the cp command
Signed-off-by: Guillaume Lours <guillaume.lours@docker.com>
2022-05-10 10:50:40 +02:00
Guillaume Lours
a964d5587b align cp command index management with exec command
Signed-off-by: Guillaume Lours <guillaume.lours@docker.com>
2022-05-10 10:50:40 +02:00
Guillaume Lours
a983cf551d cp command: copy to all containers of a service as default behaviour
Signed-off-by: Guillaume Lours <guillaume.lours@docker.com>
2022-05-10 10:50:40 +02:00
Guillaume Lours
2f47e4582c Merge pull request #9440 from ndeloof/down_error
compose down exit=0 if nothing to remove
2022-05-06 10:42:26 +02:00
Nicolas De Loof
78b06764a1 compose down exit=0 if nothing to remove
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-05-04 13:51:21 +02:00
Daniel Lublin
710b637e4a Don't use ansi even when deprecated option is requested
Signed-off-by: Daniel Lublin <daniel@lublin.se>
2022-05-03 14:45:46 +02:00
Guillaume Lours
4cebef1bf1 Merge pull request #9423 from quite/clarify-workdir
Clarify what default work dir is when multiple compose files
2022-05-03 14:02:03 +02:00
Daniel Lublin
d89c143c39 Clarify what default work dir is when multiple compose files
Signed-off-by: Daniel Lublin <daniel@lublin.se>
2022-05-03 13:14:34 +02:00
Guillaume Lours
028cb4dd89 Merge pull request #9158 from Jille/compose-down-noargs
down: Reject all arguments
2022-05-02 15:37:01 +02:00
Jille Timmermans
147c2d8fae down: Reject all arguments
The down command silently ignored all arguments, which might cause
confusion and/or outages if someone expects `docker-compose down
$service` to be the opposite of `docker-compose up $service`, rather
than turning down everything.

Signed-off-by: Jille Timmermans <jille@quis.cx>
2022-05-02 15:28:30 +02:00
Ulysses Souza
69e21d89f0 Fix relative paths on envfile label
Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
2022-04-30 10:14:39 +02:00
Eric Fan
41b3967cb5 Fix cannot setup IPAM gateway
Signed-off-by: Eric Fan <ericfan@qnap.com>
2022-04-24 17:30:27 +02:00
Nicolas De Loof
00fd1c1530 inspect image ID after pull to se com.docker.compose.image
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-04-21 22:42:29 +02:00
Guillaume Lours
14ca112862 Merge pull request #9392 from snehakpersistent/ppc-support
add support for ppc64le for docker compose
2022-04-14 17:43:44 +02:00
Sneha Kanekar1
e0286360a8 add support for ppc64le
Signed-off-by: Sneha Kanekar1 <sneha.kanekar1@ibm.com>
2022-04-14 17:25:01 +05:30
MaxPeal
5d809a2e89 create also a checksums.txt file, add --binary
create also a checksums.txt file
and switch shasum to --binary,
to Fix problems with the verification on different OS systems
fixes https://github.com/docker/compose/issues/9388

Signed-off-by: MaxPeal <30347730+MaxPeal@users.noreply.github.com>
2022-04-14 09:06:35 +02:00
Nicolas De Loof
0dffd5ba9f add support for build.secrets
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-04-13 22:45:53 +02:00
Nicolas De Loof
e01687430e when using bind API, use compose-go to (re)build volume string
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-04-13 22:45:53 +02:00
Guillaume Lours
a32fdff979 Merge pull request #9385 from ndeloof/lowercase_project_name
project name MUST be lowercase
2022-04-13 10:08:53 +02:00
Nicolas De Loof
9668f600d1 project name MUST be lowercase
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-04-13 09:49:01 +02:00
Nicolas De Loof
672e2367fb don't ignore error
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-04-12 16:00:36 +02:00
Nicolas De Loof
625a48dcf7 pull to respect pull_policy
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-04-12 07:59:58 +02:00
Guillaume Lours
03aadcc85a Merge pull request #9368 from ndeloof/links_dependencies
include services declared by `links` as implicit dependencies
2022-04-11 11:59:25 +02:00
Nicolas De Loof
500555b834 linter
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-04-08 11:41:46 +02:00
Nicolas De Loof
e766352d81 include services declared by links as implicit dependencies
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-04-08 10:32:36 +02:00
Nicolas De Loof
db698562a9 use project we just created to start services
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-04-07 13:38:30 +02:00
Guillaume Lours
7fea9f7e8b use project instead of DownOptions.project to list service images in pkg.compose.down
Signed-off-by: Guillaume Lours <guillaume.lours@docker.com>
2022-04-05 08:24:25 +02:00
Eric Freese
d871cb98e5 Fix search/replace typo in --no-TTY documentation
Commit abbba74b27 looks to have
accidentally replaced `pseudo-tty` with `pseudo-noTty` in several
places. In other places, it looks like it replaced `pseudo-tty` with
`pseudo-TTY`, so I used the uppercased version in these places as well.

For example, running version 2.3.3, I get this output:

```
% docker-compose run --help

...

Options:
  ...
  -T, --no-TTY                Disable pseudo-noTty allocation. By default docker compose run allocates a TTY
  ...
```

Signed-off-by: Eric Freese <ericdfreese@gmail.com>
2022-04-04 19:06:03 +02:00
Guillaume Lours
8d815ff587 Merge pull request #9348 from ndeloof/attach_tty
get Tty from container to know adequate way to attach to
2022-04-04 14:44:07 +02:00
Nicolas De Loof
c9fbb9ba9c inspect container before attachment to use the adequate logic regarding TTY
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-04-04 14:39:38 +02:00
Guillaume Lours
f2d9acd3d4 use ssh config when building from compose up
Signed-off-by: Guillaume Lours <guillaume.lours@docker.com>
2022-04-04 10:16:28 +02:00
Guillaume Lours
fcff36fc8a now we use directly the Docker CLI to run autoremove flag should be pass as is
Signed-off-by: Guillaume Lours <guillaume.lours@docker.com>
2022-04-02 08:45:51 +02:00
Guillaume Lours
804ef4af5b Merge pull request #9309 from thaJeztah/vendor_containerd_1.6.2
vendor: github.com/containerd/containerd v1.6.2
2022-03-31 19:02:47 +02:00
Guillaume Lours
0309a735a9 Merge pull request #9336 from ulyssessouza/fix-empty-down
Fix down command without any resource to delete
2022-03-31 18:34:56 +02:00
Ulysses Souza
56e48f8360 Fix down command without any resource to delete
A command down without any resource to delete was
trying to remove a default network that doesn't exist

Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
2022-03-31 18:30:29 +02:00
Ulysses Souza
c7473c68ae Merge pull request #9335 from ulyssessouza/remove-deadcode
Remove dead warning code
2022-03-31 17:04:29 +02:00
Ulysses Souza
5e63f12ae8 Merge pull request #9332 from ulyssessouza/fix-project-name
Takes COMPOSE_PROJECT_NAME into consideration on commands
2022-03-31 17:04:12 +02:00
Ulysses Souza
60363c36df Remove dead warning code
Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
2022-03-31 16:42:04 +02:00
Guillaume Lours
e700f0a5d7 Merge pull request #9334 from mschoettle/ssh-option-fix-typo
fix typo in ssh option description
2022-03-31 16:35:47 +02:00
Matthias Schoettle
6dbd6ffe11 fix typo in ssh option description
Signed-off-by: Matthias Schoettle <git@mattsch.com>
2022-03-31 10:14:13 -04:00
Ulysses Souza
eee0e8bed9 Takes COMPOSE_PROJECT_NAME into consideration on commands
Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
2022-03-31 14:28:38 +02:00
Guillaume Lours
934b596e00 add e2e tests to check usage of ssh build property
Signed-off-by: Guillaume Lours <guillaume.lours@docker.com>
2022-03-31 12:47:15 +02:00
Guillaume Lours
ff73827a6f Add support of ssh authentications defined in compose file or via cli flags
Signed-off-by: Guillaume Lours <guillaume.lours@docker.com>
2022-03-31 12:47:15 +02:00
Guillaume Lours
3c12b94519 Merge pull request #9331 from glours/e2e-start-stop-race
fix race condition on start-stop e2e tests running in parrallel
2022-03-31 11:47:10 +02:00
Guillaume Lours
9b02add4df fix race condition on start-stop e2e tests running in parrallel
Signed-off-by: Guillaume Lours <guillaume.lours@docker.com>
2022-03-31 11:16:18 +02:00
Guillaume Lours
a73a2c9240 Merge pull request #9222 from ndeloof/cache
support cache_from|to|no|pull
2022-03-28 09:52:30 +02:00
Nicolas De Loof
890b6808a2 support cache_from|to|no|pull
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-03-28 09:36:24 +02:00
Sebastiaan van Stijn
950cb1af0d go.mod: github.com/docker/buildx v0.8.1
Fix possible panic on handling build context scanning errors

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-03-28 09:09:05 +02:00
Sebastiaan van Stijn
d078e30642 go.mod: pin k8s dependencies to the same version as in buildx
Buildx uses replace rules to pin these dependencies to the version
that's used in buildkit. Newer versions of these dependencies are
incompatible with some go versions, and we do not need the newer
versions for where it's used.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-03-28 09:09:05 +02:00
Guillaume Lours
b7fd6eb7d8 pass interactive flag to ExecOptions for the RunExec command
Signed-off-by: Guillaume Lours <guillaume.lours@docker.com>
2022-03-26 07:23:31 +01:00
Sebastiaan van Stijn
5ace5bdeda vendor: github.com/containerd/containerd v1.6.2
includes a fix for CVE-2022-24769 (but doesn't affect the code we use)

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-03-24 04:13:02 +01:00
Guillaume Lours
be187bae64 use a temp directory to generate doc to be sure working tree is clean
Signed-off-by: Guillaume Lours <guillaume.lours@docker.com>
2022-03-18 10:42:05 +01:00
Guillaume Lours
099715fb6f update run no-TTY flag description as auto-detected by default
Signed-off-by: Guillaume Lours <guillaume.lours@docker.com>
2022-03-18 10:42:05 +01:00
Guillaume Lours
09e0cca9a7 update CI github actions comment to mention docs validation
Signed-off-by: Guillaume Lours <guillaume.lours@docker.com>
2022-03-18 10:42:05 +01:00
Guillaume Lours
bf26cbd498 contenairized documetation generation
add docs validation (using same process a BuildX project)

Signed-off-by: Guillaume Lours <guillaume.lours@docker.com>
2022-03-18 10:42:05 +01:00
Guillaume Lours
35ba6f68e5 generate reference api
Signed-off-by: Guillaume Lours <guillaume.lours@docker.com>
2022-03-18 10:42:05 +01:00
Nicolas De Loof
c843d373de restore TTY auto-detection using dockerCli.Out.IsTerminal
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-03-16 16:34:26 +01:00
Nicolas De Loof
1d4b4e3c8e use docker/cli RunExec and RunStart to handle all the interactive/tty/* terminal logic
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-03-16 16:34:26 +01:00
Guillaume Lours
d999c230a5 Merge pull request #9281 from ndeloof/down_volume_external
don't remove external volumes/networks
2022-03-14 17:21:04 +01:00
Nicolas De Loof
e7f545907b don't remove external volumes/networks
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-03-14 15:24:49 +01:00
Guillaume Lours
730609b359 Merge pull request #9280 from ndeloof/kill
kill only need project name
2022-03-14 10:33:21 +01:00
Nicolas De Loof
cd8074d501 kill only need project name
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-03-14 10:26:36 +01:00
Nicolas De Loof
283f7a1ec5 Bump buildx to v0.8.0
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-03-11 14:05:55 +01:00
Ulysses Souza
6ce57ea2f4 Remove DEPRECATED text, since it's just the default
Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
2022-03-11 13:01:55 +01:00
dependabot[bot]
2cf917a330 Bump github.com/spf13/cobra from 1.3.0 to 1.4.0
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.3.0 to 1.4.0.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Changelog](https://github.com/spf13/cobra/blob/master/CHANGELOG.md)
- [Commits](https://github.com/spf13/cobra/compare/v1.3.0...v1.4.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/cobra
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-11 13:01:21 +01:00
Nicolas De loof
fbcd7ee896 Update BUILDING.md 2022-03-11 12:47:50 +01:00
Sebastiaan van Stijn
2d32d7450c ps: un-deprecate --filter, and enhance docs
Compose currently only supports a single filter options on --filter,
for which reason the --status flag was added, which is more convenient
to use.

However, the `--filter` flag is common among various docker commands, and
it's possible that additional filters get added at some point (which may
be less "commonly" used, and not warrant a dedicated flag).

This PR removes the "deprecated" mention from the flag, to keep consistency
with other commands, but adds documentation to explain how they relate to
eachother.

Also added a short example for the `--format` flag.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-03-10 20:39:19 +01:00
Guillaume Lours
dc6097d1f0 Merge pull request #9261 from ndeloof/recreate_on_image_updated
recreate container after image has been rebuilt/pulled
2022-03-10 12:11:02 +01:00
Nicolas De Loof
85a4d04096 recreate container after image has been rebuilt/pulled
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-03-10 11:02:48 +01:00
Guillaume Lours
4d163f35f3 Merge pull request #9256 from ndeloof/rm_empty
don't fail trying to remove container with no candidate
2022-03-09 16:36:13 +01:00
Nicolas De Loof
5e8040ea18 don't fail trying to remove container with no candidate
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-03-09 16:24:16 +01:00
Sebastiaan van Stijn
42710b7c43 docs: also generate "usage" in MarkDown files
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-03-09 14:40:35 +01:00
Sebastiaan van Stijn
b9b3a3d91f docs: update cli-docs-tool to v0.4.0
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-03-09 14:40:35 +01:00
Sebastiaan van Stijn
1d06741032 docs: fix trailing whitespace from markdown and regenerate
Trailing whitespace in Markdown can force line-breaks, which doesn't seem to
be the intent on these;

    find . -type f -print0 | xargs -0 perl -pi -e 's/ +$//'

The trailing whitespace also can cause the YAML to go wonky (although the
cli-docs-tool now takes that into account), and caused the "examples" section
to be missed in the `docker compose pull` page (something we should fix in
the tool).

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-03-09 14:40:35 +01:00
Sebastiaan van Stijn
35b790dcdf docs: fix "source" path for YAML generator, and regenerate
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-03-09 14:40:35 +01:00
Guillaume Lours
7036cda306 Merge pull request #9198 from ndeloof/dockerCli_stdout
composeService to use dockerCli's In/Out/Err streams
2022-03-09 14:36:07 +01:00
Guillaume Lours
540ad83a6d Merge pull request #9251 from ndeloof/device_cgroup_rules
add support for device_cgroup_rules
2022-03-09 13:27:53 +01:00
Guillaume Lours
92be1e7112 Merge pull request #9252 from glours/test-run-with-dependencies
add run with dependencies e2e test
2022-03-09 12:35:59 +01:00
Guillaume Lours
a97576e844 add run with dependencies e2e test
Signed-off-by: Guillaume Lours <guillaume.lours@docker.com>
2022-03-09 12:22:38 +01:00
Nicolas De Loof
d8775c7a28 add support for device_cgroup_rules
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-03-09 11:39:20 +01:00
Nicolas De Loof
158b5ff6a3 build full compose model from resources, then filter by services
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-03-09 11:29:30 +01:00
Guillaume Lours
ce944520ff Merge pull request #9247 from ndeloof/plain
use plain text progress when ansi=never is set
2022-03-08 16:55:55 +01:00
Nicolas De Loof
57e8e8ef29 use plain text progress when ansi=never is set
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-03-08 15:05:37 +01:00
Guillaume Lours
a2b9c81254 Merge pull request #9246 from ndeloof/interactive
map --interactive to StdinOpen
2022-03-08 10:23:28 +01:00
Guillaume Lours
d75f22cc7b publish a draft release with auto generate release notes
Signed-off-by: Guillaume Lours <guillaume.lours@docker.com>
2022-03-08 10:19:06 +01:00
Nicolas De Loof
2282159922 map --interactive to StdinOpen
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-03-08 10:17:17 +01:00
Nicolas De Loof
8c7951465e filter containers after project has been rebuilt from resources
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-03-08 09:47:22 +01:00
Victor Timofei
9054f6a18b Fix json format for version command
Signed-off-by: Victor Timofei <victor@vtimothy.com>
2022-03-08 09:40:25 +01:00
Nicolas De Loof
22b8c731c7 prevent getCanonicalContainerName to crash
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-03-07 14:58:39 +01:00
Guillaume Lours
0ef4b90fae don't failed when trying to stop or rm services with no containers running
Signed-off-by: Guillaume Lours <guillaume.lours@docker.com>
2022-03-07 10:35:22 +01:00
Guillaume Lours
908c12120e Merge pull request #9237 from ndeloof/run_regression
don't try to start dependencies when there are none
2022-03-07 08:42:01 +01:00
Nicolas De Loof
dbe7de50a7 don't try to start dependencies when there are none
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-03-07 08:25:05 +01:00
Zixuan James Li
fcff39631a Add documentation for COMPOSE_IGNORE_ORPHANS
Signed-off-by: Zixuan James Li <359101898@qq.com>
2022-03-05 22:54:16 +01:00
PIG208
bcaa908f74 Support COMPOSE_IGNORE_ORPHANS for compose run
This revives #7020 and resolves the issue mentioned in #4992.

Signed-off-by: Zixuan James Li <359101898@qq.com>
2022-03-05 22:54:16 +01:00
Guillaume Lours
c64b044b7e Merge pull request #9229 from ulyssessouza/add-ti-run-exec
Add -i and -t to run and exec
2022-03-04 17:33:09 +01:00
Guillaume Lours
1a4e6c2465 Merge pull request #9230 from ulyssessouza/treat-env-bool-vars
Add function to convert strings to bool
2022-03-04 17:32:21 +01:00
Ulysses Souza
67b4669f9b Add function to convert strings to bool
Typically used on boolean environment variable values

Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
2022-03-04 16:42:37 +01:00
Ulysses Souza
61735c0012 Add -i and -t to run and exec
Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
2022-03-04 14:27:06 +01:00
Pavel Sirotkin
c12a948f97 CONTRIBUTING.md file. Update slack sign-up link
Signed-off-by: Pavel Sirotkin <pav.pnz@gmail.com>
2022-03-04 12:25:11 +01:00
Nicolas De Loof
f86f252a66 composeService to use dockerCli's In/Out/Err streams
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-03-03 16:34:57 +01:00
dependabot[bot]
bf5785307b Bump github.com/containerd/containerd from 1.6.0 to 1.6.1
Bumps [github.com/containerd/containerd](https://github.com/containerd/containerd) from 1.6.0 to 1.6.1.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v1.6.0...v1.6.1)

---
updated-dependencies:
- dependency-name: github.com/containerd/containerd
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-03 14:13:39 +01:00
Nicolas De Loof
52eeda9aa7 report external volume name not found
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-03-02 08:09:31 +01:00
Guillaume Lours
1f0cf0723c Merge pull request #9221 from ulyssessouza/bump-docker-distribution-2.8.0
Bump docker/distribution -> v2.8.0
2022-03-01 16:15:22 +01:00
Ulysses Souza
315f16e5fb Bump docker/distribution -> v2.8.0
Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
2022-03-01 16:08:48 +01:00
Ulysses Souza
327a1bb27b Merge pull request #9219 from ulyssessouza/bump-compose-go-1.1.0
Bump compose-go 1.1.0
2022-03-01 15:58:12 +01:00
Ulysses Souza
16914e372e Bump compose-go 1.1.0
Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
2022-03-01 15:52:14 +01:00
Guillaume Lours
ec080f184a Merge pull request #9214 from ndeloof/inconsistent_hash
exclude com.docker.compose.image label from service hash
2022-02-28 10:20:36 +01:00
Nicolas De Loof
b5b8e7a116 exclude com.docker.compose.image label from service hash
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-02-28 10:10:39 +01:00
Guillaume Lours
9d73cc88cc Merge pull request #9148 from arhemd/issue#9147
Using start, stop, restart from outside the working directory using --project-name (#9147)
2022-02-27 09:45:36 +01:00
Mehrad Dadar
9c68c76bea minor improvement and fix
Signed-off-by: Mehrad Dadar <mehrad.dadar@gmail.com>
2022-02-27 03:40:46 +03:30
Mehrad Dadar
aeb7448449 minor improvement and fix
Signed-off-by: Mehrad Dadar <mehrad.dadar@gmail.com>
2022-02-25 06:26:05 +03:30
Mehrad Dadar
42c3adb236 project existence check: added error message
Signed-off-by: Mehrad Dadar <mehrad.dadar@gmail.com>
2022-02-25 05:36:22 +03:30
Mehrad Dadar
35f37cd1f7 fix lint
Signed-off-by: Mehrad Dadar <mehrad.dadar@gmail.com>
2022-02-25 00:00:36 +03:30
Mehrad Dadar
c0465616bb check service existence in project
Signed-off-by: Mehrad Dadar <mehrad.dadar@gmail.com>
2022-02-24 23:46:07 +03:30
Mehrad Dadar
32d44dfc25 added scale to toProjectName
Signed-off-by: Mehrad Dadar <mehrad.dadar@gmail.com>
2022-02-24 22:39:38 +03:30
Mehrad Dadar
5885a250bc Merge branch 'docker:v2' into issue#9147 2022-02-24 18:03:05 +03:30
Mehrad Dadar
ce1c788237 minor code cleanup
Signed-off-by: Mehrad Dadar <mehrad.dadar@gmail.com>
2022-02-24 14:50:50 +03:30
Mehrad Dadar
67f7b84829 modified com.docker.compose.depends_on label to contain dependency type
Signed-off-by: Mehrad Dadar <mehrad.dadar@gmail.com>
2022-02-23 21:34:34 +03:30
Guillaume Lours
fd676adc5d Merge pull request #9200 from ndeloof/COMPOSE_REMOVE_ORPHANS
COMPOSE_REMOVE_ORPHANS can be set to always apply --remove-orphans
2022-02-23 14:10:32 +01:00
Nicolas De Loof
aa864fde20 COMPOSE_REMOVE_ORPHANS can be set to always apply --remove-orphans
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-02-23 14:04:12 +01:00
dependabot[bot]
f7a6c3bc54 Bump github.com/containerd/containerd from 1.5.8 to 1.6.0
Bumps [github.com/containerd/containerd](https://github.com/containerd/containerd) from 1.5.8 to 1.6.0.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v1.5.8...v1.6.0)

---
updated-dependencies:
- dependency-name: github.com/containerd/containerd
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-23 13:37:56 +01:00
Ulysses Souza
8a9498c571 Merge pull request #9192 from glours/wait-and-scale-0
Wait and scale 0
2022-02-23 12:47:34 +01:00
Mehrad Dadar
7e7262bc77 Merge branch 'docker:v2' into issue#9147 2022-02-22 05:08:29 +03:30
Nicolas De Loof
981aea674d bump buildx to 0.7.1
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-02-21 14:28:22 +01:00
Guillaume Lours
64a9e4bf01 fix cyclomatic complexity of composeService.waitDependencies function
Signed-off-by: Guillaume Lours <guillaume.lours@docker.com>
2022-02-20 11:59:26 +01:00
Guillaume Lours
4be38f84df do not stop the dependencies wait process when reaching a dependency with service_started condition
Signed-off-by: Guillaume Lours <guillaume.lours@docker.com>
2022-02-20 10:28:34 +01:00
Guillaume Lours
09e0fa94b8 do not wait for dependencies with scale 0
Signed-off-by: Guillaume Lours <guillaume.lours@docker.com>
2022-02-20 10:28:34 +01:00
Nicolas De Loof
416498441c declare --volume as an alias for --volumes
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-02-18 17:40:47 +01:00
Vedant Koditkar
cb45c6f2df Add unit tests for combinedConfigFiles logic
Signed-off-by: Vedant Koditkar <vedant.koditkar@outlook.com>
2022-02-18 16:37:35 +01:00
Vedant Koditkar
90ca37344f Update breaking test cases
Signed-off-by: Vedant Koditkar <vedant.koditkar@outlook.com>
2022-02-18 16:37:35 +01:00
Vedant Koditkar
fb3f9e270f Add compose file path to ls command
docker compose ls will not include config files section in result

Signed-off-by: Vedant Koditkar <vedant.koditkar@outlook.com>
2022-02-18 16:37:35 +01:00
Ulysses Souza
02f78d2893 Merge pull request #9183 from ndeloof/golangci-lint-action
use golangci-lint-action
2022-02-17 18:17:06 +01:00
Nicolas De Loof
d47dcef1a6 use golangci-lint-action
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-02-17 18:10:28 +01:00
Nicolas De Loof
fb9310caf2 use CustomLabels for composeV2 metadata and not impact service hash
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-02-17 09:05:54 +01:00
Mehrad Dadar
ced9eba940 refactor: removed redundant code
Signed-off-by: Mehrad Dadar <mehrad.dadar@gmail.com>
2022-02-17 07:55:11 +03:30
Ulysses Souza
2eeed8481d Fix pause/unpause by only applying to running containers
Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
2022-02-16 15:13:12 +01:00
Ulysses Souza
10ca0314bc Merge pull request #9164 from danBamikiya/compose-up-typo-fix
Fix typo in `reference/compose_up.md`
2022-02-11 15:18:59 +01:00
Dan Bámíkíyá
336b825fdd Fix the typo in the corresponding Yaml file also
Signed-off-by: Dan Bámíkíyá <dudeawesome732@gmail.com>
2022-02-11 15:10:08 +01:00
Dan Bámíkíyá
213d9166dc Fix typo in reference/compose_up
Signed-off-by: Dan Bámíkíyá <dudeawesome732@gmail.com>
2022-02-11 15:08:33 +01:00
Guillaume Lours
598b59f8bf Merge pull request #9142 from thaJeztah/update_goterm
go.mod: github.com/buger/goterm v1.0.4
2022-02-08 12:28:18 +01:00
Mehrad Dadar
65ed8cf4c2 Implemented #9147
Signed-off-by: Mehrad Dadar <mehrad.dadar@gmail.com>
2022-02-05 10:27:52 +03:30
Ulysses Souza
a23cbb580e Merge pull request #9146 from ulyssessouza/bump-compose-go-1.0.9
Bump compose-go 1.0.9
2022-02-05 02:59:23 +01:00
Ulysses Souza
a89e194558 Bump compose-go 1.0.9
Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
2022-02-04 14:47:16 +01:00
Sebastiaan van Stijn
b31695a66e go.mod: github.com/buger/goterm v1.0.4
Version v1.0.4 was released, containing a fix for a regression in
v1.0.3, so the previous downgrade can be reverted, and we can go
back to the latest version:
https://github.com/buger/goterm/compare/v1.0.3...v1.0.4

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-02-03 21:33:39 +01:00
Ulysses Souza
5262d3bbf5 Merge pull request #9130 from laurazard/fix_mac_address_network_priority
Set NetworkMode correctly according to network priorities
2022-02-01 14:53:25 +01:00
Laura Brehm
a4836391a5 Set NetworkMode correctly according to network priorities
Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2022-02-01 12:59:05 +00:00
Ulysses Souza
bfd7428619 Merge pull request #9074 from ndeloof/down_volumes
only remove volumes set by compose file
2022-01-21 13:09:10 +01:00
Nicolas De Loof
feba34e406 only remove volumes set by compose file
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-01-20 14:32:45 +01:00
Yuri Kanivetsky
37f763f009 Don't SetRawTerminal() when exec is run with -T
Signed-off-by: Yuri Kanivetsky <yuri.kanivetsky@gmail.com>
2022-01-19 09:11:35 +01:00
Guillaume Rose
99cd90a4b2 Return only numbers in short version
Python version of docker compose removes the v prefix in the version.

```
$ docker-compose version --short
1.25.5
```

Signed-off-by: Guillaume Rose <gurose@redhat.com>
2022-01-17 07:30:27 +01:00
Ulysses Souza
a279c3a934 Discard env_file section on convert/config
Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
2022-01-15 11:45:08 +01:00
Nikhil Benesch
ee586e7f1e Introduce ergonomic API for handling multiple container events
Signed-off-by: Nikhil Benesch <nikhil.benesch@gmail.com>
2022-01-07 21:22:57 +01:00
Nikhil Benesch
5eb314a4ca Add progress output while waiting for dependencies
This commit adds progress output while waiting for `depends_on`
conditions to resolve. The initial output looks like so:

     ⠿ Container chbench-zookeeper-1        Waiting      0s
     ⠿ Container chbench-kafka-1            Waiting      0s
     ⠿ Container chbench-one-off            Waiting      0s

Once all conditions have been resolved, the ouput looks like this:

     ⠿ Container chbench-zookeeper-1        Healthy      1.2s
     ⠿ Container chbench-kafka-1            Healthy      3.2s
     ⠿ Container chbench-schema-registry-1  Exited       4s

As shown above, `service_healthy` conditions result in a terminal status
of "Healthy" while `service_exited_successfully` conditions result in a
terminal status of "Exited".

Signed-off-by: Nikhil Benesch <nikhil.benesch@gmail.com>
2022-01-07 21:22:57 +01:00
Nikhil Benesch
c5cdce0b60 Don't wait forever for unhealthy dependencies
The previous code would wait for dependencies to become healthy forever,
even if they'd become unhealthy in the meantime. I can't find an issue
report for this bug, but it was described in a comment on the PR that
introduced the `--wait` flag [0].

[0]: https://github.com/docker/compose/pull/8777#issuecomment-965643839

Signed-off-by: Nikhil Benesch <nikhil.benesch@gmail.com>
2022-01-07 19:13:43 +01:00
Ulysses Souza
6dc6bedb60 Downgrade goterm to 1.0.1
This is to avoid a release error:
https://github.com/docker/compose/runs/4701103662?check_suite_focus=true

Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
2022-01-04 14:54:16 +01:00
Tymoteusz Blazejczyk
9f06a02eb5 Moved bind mode creation to getBindMode function
Added unit tests for all uses cases.

Signed-off-by: Tymoteusz Blazejczyk <tymoteusz.blazejczyk@tymonx.com>
2022-01-03 08:04:34 +01:00
Tymoteusz Blazejczyk
9d0421a929 Removed the replace directive in the go.mod file
Signed-off-by: Tymoteusz Blazejczyk <tymoteusz.blazejczyk@tymonx.com>
2022-01-03 08:04:34 +01:00
Tymoteusz Blazejczyk
340b5482b0 Changed to the official compose-spec/compose-go
Changes were merged. Updated the `replace` directive in the `go.mod`
file to the latest official package.

Signed-off-by: Tymoteusz Blazejczyk <tymoteusz.blazejczyk@tymonx.com>
2022-01-03 08:04:34 +01:00
Tymoteusz Blazejczyk
faaa93bf12 Added volume bind option SELinux label :z :Z
Added unsupported volume bind option SELinux label `:z` and `:Z` in v2.

It is a regression compared to v1 written in Python.

The v2 uses the compose-spec/compose-go to parse Compose YAML
specification files but there was missing support for volume bind option
SELinux label `:z` and `:Z` in parser. It is fixed in:

- https://github.com/compose-spec/compose-go/pull/213

It fixes #9072

References:

- https://docs.docker.com/storage/bind-mounts/#configure-the-selinux-label

Signed-off-by: Tymoteusz Blazejczyk <tymoteusz.blazejczyk@tymonx.com>
2022-01-03 08:04:34 +01:00
dependabot[bot]
381df20010 Bump github.com/AlecAivazis/survey/v2 from 2.2.3 to 2.3.2
Bumps [github.com/AlecAivazis/survey/v2](https://github.com/AlecAivazis/survey) from 2.2.3 to 2.3.2.
- [Release notes](https://github.com/AlecAivazis/survey/releases)
- [Commits](https://github.com/AlecAivazis/survey/compare/v2.2.3...v2.3.2)

---
updated-dependencies:
- dependency-name: github.com/AlecAivazis/survey/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-17 15:19:03 +01:00
dependabot[bot]
a9e8164a8d Bump github.com/containerd/console from 1.0.2 to 1.0.3
Bumps [github.com/containerd/console](https://github.com/containerd/console) from 1.0.2 to 1.0.3.
- [Release notes](https://github.com/containerd/console/releases)
- [Commits](https://github.com/containerd/console/compare/v1.0.2...v1.0.3)

---
updated-dependencies:
- dependency-name: github.com/containerd/console
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-17 15:09:03 +01:00
Lance Chen
1191023fb6 Propagate GroupAdd from ServiceConfig to HostConfig
The `group_add` key is parsed correctly from a compose file, but it is not
passed into the `ContainerCreate` API call, thus the configuration does
not take effect. This commit fixes the issue by propagating the
configuration from Docker compose's ServiceConfig to Docker container's
HostConfig.

Signed-off-by: Lance Chen <hello@lancechen.tw>
2021-12-17 15:08:14 +01:00
dependabot[bot]
a108690ac2 Bump github.com/buger/goterm from 1.0.0 to 1.0.3
Bumps [github.com/buger/goterm](https://github.com/buger/goterm) from 1.0.0 to 1.0.3.
- [Release notes](https://github.com/buger/goterm/releases)
- [Commits](https://github.com/buger/goterm/compare/v1.0.0...v1.0.3)

---
updated-dependencies:
- dependency-name: github.com/buger/goterm
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-17 14:59:02 +01:00
dependabot[bot]
0e81d1c88e Bump github.com/hashicorp/go-multierror from 1.1.0 to 1.1.1
Bumps [github.com/hashicorp/go-multierror](https://github.com/hashicorp/go-multierror) from 1.1.0 to 1.1.1.
- [Release notes](https://github.com/hashicorp/go-multierror/releases)
- [Commits](https://github.com/hashicorp/go-multierror/compare/v1.1.0...v1.1.1)

---
updated-dependencies:
- dependency-name: github.com/hashicorp/go-multierror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-17 07:39:21 +01:00
dependabot[bot]
22002531d8 Bump github.com/spf13/cobra from 1.2.1 to 1.3.0
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.2.1 to 1.3.0.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Changelog](https://github.com/spf13/cobra/blob/master/CHANGELOG.md)
- [Commits](https://github.com/spf13/cobra/compare/v1.2.1...v1.3.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/cobra
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-17 06:24:57 +01:00
dependabot[bot]
b66ff0c3d8 Bump github.com/golang/mock from 1.5.0 to 1.6.0
Bumps [github.com/golang/mock](https://github.com/golang/mock) from 1.5.0 to 1.6.0.
- [Release notes](https://github.com/golang/mock/releases)
- [Changelog](https://github.com/golang/mock/blob/master/.goreleaser.yml)
- [Commits](https://github.com/golang/mock/compare/v1.5.0...v1.6.0)

---
updated-dependencies:
- dependency-name: github.com/golang/mock
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-17 06:24:35 +01:00
Ulysses Souza
7b6439997d Fix output redirection on command run
Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
2021-12-15 15:10:22 +01:00
Ulysses Souza
d37b3fe413 Merge pull request #9036 from ulyssessouza/add-dependabot
Add dependabot
2021-12-15 10:38:28 +01:00
Ulysses Souza
ce7a2412b1 Add dependabot
Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
2021-12-14 16:19:41 +01:00
Ulysses Souza
9e52e9fbe3 Merge pull request #9004 from ndeloof/cp_stopped
compose cp doesn't need a full project and can copy from stopped containers
2021-12-14 14:27:51 +01:00
Nicolas De Loof
c5b7624d10 compose cp doesn't need a full project and can copy from stopped container
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2021-12-12 16:15:56 +01:00
Ulysses Souza
cf7319fc6e Only kill running containers
Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
2021-12-10 08:48:38 +01:00
Nicolas De Loof
740276f550 handle "stop" event
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2021-12-09 13:57:52 +01:00
Ulysses Souza
40bca10250 Merge pull request #9007 from ulyssessouza/add-test-modes
Add 2 modes test mechanism
2021-12-09 11:16:37 +01:00
Ulysses Souza
8ae8d99528 Use build tags for selecting e2e test mode
Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
2021-12-09 10:52:18 +01:00
Jon Cram
f03b7085c3 compose ps: fix typo "unknow" -> "unknown"
Signed-off-by: Jon Cram <webignition@gmail.com>
2021-12-09 08:56:35 +01:00
Ulysses Souza
36c2947e4d Merge pull request #9012 from notok/rm_containers_when_build_succeed
Remove intermediate containers when build succeeded in classic build
2021-12-08 12:39:10 +01:00
notok
3bbcc3d4d0 Fix lint error
Signed-off-by: notok <noto.kazufumi@gmail.com>
2021-12-08 18:42:00 +09:00
notok
b47d8ea868 Remove intermediate containers when build succeeded
Intermediate containers remain even when build succeeded
when building with classic way (i.e. not with buildkit).
Remove them when build succeeded like default behavior of docker build.

Signed-off-by: notok <noto.kazufumi@gmail.com>
2021-12-08 04:18:05 +09:00
Ulysses Souza
a97a73600e Add 2 modes test mechanism
This should test on plugin and standalone mode

Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
2021-12-07 10:25:50 +01:00
Nicolas De Loof
6735220557 ignore missing (swarm) overlay networks
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2021-12-06 14:06:34 +01:00
Kevin Roy
d7d29b25bc compose images should list created containers images
Signed-off-by: Kevin Roy <kiniou@gmail.com>
2021-12-05 10:59:25 +01:00
191 changed files with 6338 additions and 2130 deletions

View File

@@ -1,3 +1,2 @@
.git/
bin/
dist/

6
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: gomod
directory: /
schedule:
interval: daily

View File

@@ -7,10 +7,10 @@ jobs:
if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '/generate-artifacts')
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.17
- name: Set up Go 1.18
uses: actions/setup-go@v2
with:
go-version: 1.17
go-version: 1.18.3
id: go
- name: Checkout code into the Go module directory
@@ -42,6 +42,12 @@ jobs:
name: docker-compose-linux-amd64
path: ${{ github.workspace }}/bin/docker-compose-linux-amd64
- name: Upload linux-ppc64le binary
uses: actions/upload-artifact@v2
with:
name: docker-compose-linux-ppc64le
path: ${{ github.workspace }}/bin/docker-compose-linux-ppc64le
- name: Upload windows-amd64 binary
uses: actions/upload-artifact@v2
with:

View File

@@ -5,6 +5,12 @@ on:
branches:
- v2
pull_request:
workflow_dispatch:
inputs:
debug_enabled:
description: 'To run with tmate enter "debug_enabled"'
required: false
default: "false"
jobs:
lint:
@@ -13,24 +19,24 @@ jobs:
env:
GO111MODULE: "on"
steps:
- name: Set up Go 1.17
- name: Set up Go 1.18
uses: actions/setup-go@v2
with:
go-version: 1.17
go-version: 1.18.3
id: go
- name: Checkout code into the Go module directory
uses: actions/checkout@v2
- name: Validate go-mod is up-to-date and license headers
- name: Validate go-mod, license headers and docs are up-to-date
run: make validate
- name: Run golangci-lint
env:
BUILD_TAGS: e2e
run: |
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sudo sh -s -- -b /usr/bin/ v1.39.0
make -f builder.Makefile lint
uses: golangci/golangci-lint-action@v2
with:
args: --timeout=180s
# only on main branch, costs too much for the gain on every PR
validate-cross-build:
@@ -40,10 +46,10 @@ jobs:
env:
GO111MODULE: "on"
steps:
- name: Set up Go 1.17
- name: Set up Go 1.18
uses: actions/setup-go@v2
with:
go-version: 1.17
go-version: 1.18.3
id: go
- name: Checkout code into the Go module directory
@@ -59,22 +65,18 @@ jobs:
- name: Build packages
run: make -f builder.Makefile cross
build:
name: Build
build-plugin:
name: Build and tests in plugin mode
runs-on: ubuntu-latest
env:
GO111MODULE: "on"
steps:
- name: Set up Go 1.17
- name: Set up Go 1.18
uses: actions/setup-go@v2
with:
go-version: 1.17
go-version: 1.18.3
id: go
- name: Set up gosum
run: |
go get -u gotest.tools/gotestsum
- name: Setup docker CLI
run: |
curl https://download.docker.com/linux/static/stable/x86_64/docker-20.10.3.tgz | tar xz
@@ -89,14 +91,55 @@ jobs:
key: go-${{ hashFiles('**/go.sum') }}
- name: Test
env:
BUILD_TAGS: kube
run: make -f builder.Makefile test
- name: Build for local E2E
env:
BUILD_TAGS: e2e
run: make -f builder.Makefile compose-plugin
run: make GIT_TAG=e2e-PR-${{ github.event.pull_request.number }}-${{ github.event.pull_request.head.sha }} -f builder.Makefile compose-plugin
- name: E2E Test
- name: E2E Test in plugin mode
run: make e2e-compose
build-standalone:
name: Build and tests in standalone mode
runs-on: ubuntu-latest
env:
GO111MODULE: "on"
steps:
- name: Set up Go 1.18
uses: actions/setup-go@v2
with:
go-version: 1.18.3
id: go
- name: Setup docker CLI
run: |
curl https://download.docker.com/linux/static/stable/x86_64/docker-20.10.3.tgz | tar xz
sudo cp ./docker/docker /usr/bin/ && rm -rf docker && docker version
- name: Checkout code into the Go module directory
uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: ~/go/pkg/mod
key: go-${{ hashFiles('**/go.sum') }}
- name: Build for local E2E
env:
BUILD_TAGS: e2e
run: make GIT_TAG=e2e-PR-${{ github.event.pull_request.number }}-${{ github.event.pull_request.head.sha }} -f builder.Makefile compose-plugin
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
with:
limit-access-to-actor: true
github-token: ${{ secrets.GITHUB_TOKEN }}
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled }}
- name: E2E Test in standalone mode
run: |
rm -f /usr/local/bin/docker-compose
cp bin/docker-compose /usr/local/bin
make e2e-compose-standalone

View File

@@ -11,10 +11,10 @@ jobs:
upload-release:
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.17
- name: Set up Go 1.18
uses: actions/setup-go@v2
with:
go-version: 1.17
go-version: 1.18.3
id: go
- name: Setup docker CLI
@@ -36,7 +36,7 @@ jobs:
run: make GIT_TAG=${{ github.event.inputs.tag }} -f builder.Makefile cross
- name: Compute checksums
run: cd bin; for f in *; do shasum --algorithm 256 $f > $f.sha256; done
run: cd bin; for f in *; do shasum --binary --algorithm 256 $f | tee -a checksums.txt > $f.sha256; done
- name: License
run: cp packaging/* bin/
@@ -44,7 +44,8 @@ jobs:
- uses: ncipollo/release-action@v1
with:
artifacts: "bin/*"
prerelease: true
generateReleaseNotes: true
draft: true
commit: "v2"
token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ github.event.inputs.tag }}

View File

@@ -8,7 +8,7 @@
* [Docker Desktop](https://hub.docker.com/editions/community/docker-ce-desktop-mac)
* make
* Linux:
* [Docker 19.03 or later](https://docs.docker.com/engine/install/)
* [Docker 20.10 or later](https://docs.docker.com/engine/install/)
* make
### Building the CLI

View File

@@ -83,7 +83,7 @@ don't get discouraged! Our contributor's guide explains
<tr>
<td>Community Slack</td>
<td>
The Docker Community has a dedicated Slack chat to discuss features and issues. You can sign-up <a href="https://dockercommunity.slack.com/ssb/redirect" target="_blank">with this link</a>.
The Docker Community has a dedicated Slack chat to discuss features and issues. You can sign-up <a href="https://www.docker.com/docker-community" target="_blank">with this link</a>.
</td>
</tr>
<tr>

View File

@@ -15,7 +15,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
ARG GO_VERSION=1.17-alpine
ARG GO_VERSION=1.18.3-alpine
ARG GOLANGCI_LINT_VERSION=v1.40.1-alpine
ARG PROTOC_GEN_GO_VERSION=v1.4.3
@@ -88,7 +88,7 @@ RUN --mount=target=. \
make -f builder.Makefile test
FROM base AS check-license-headers
RUN go get -u github.com/kunalkushwaha/ltag
RUN go install github.com/kunalkushwaha/ltag@latest
RUN --mount=target=. \
make -f builder.Makefile check-license-headers

View File

@@ -42,8 +42,23 @@ compose-plugin: ## Compile the compose cli-plugin
--output ./bin
.PHONY: e2e-compose
e2e-compose: ## Run End to end local tests. Set E2E_TEST=TestName to run a single test
gotestsum $(TEST_FLAGS) ./pkg/e2e -- -count=1
e2e-compose: ## Run end to end local tests in plugin mode. Set E2E_TEST=TestName to run a single test
docker compose version
go test $(TEST_FLAGS) -count=1 ./pkg/e2e
.PHONY: e2e-compose-standalone
e2e-compose-standalone: ## Run End to end local tests in standalone mode. Set E2E_TEST=TestName to run a single test
docker-compose version
go test $(TEST_FLAGS) -v -count=1 -parallel=1 --tags=standalone ./pkg/e2e
.PHONY: mocks
mocks:
mockgen -destination pkg/mocks/mock_docker_cli.go -package mocks github.com/docker/cli/cli/command Cli
mockgen -destination pkg/mocks/mock_docker_api.go -package mocks github.com/docker/docker/client APIClient
mockgen -destination pkg/mocks/mock_docker_compose_api.go -package mocks -source=./pkg/api/api.go Service
.PHONY: e2e
e2e: e2e-compose e2e-compose-standalone ## Run end to end local tests in both modes. Set E2E_TEST=TestName to run a single test
.PHONY: cross
cross: ## Compile the CLI for linux, darwin and windows
@@ -70,6 +85,23 @@ lint: ## run linter(s)
--build-arg GIT_TAG=$(GIT_TAG) \
--target lint
.PHONY: docs
docs: ## generate documentation
$(eval $@_TMP_OUT := $(shell mktemp -d -t dockercli-output.XXXXXXXXXX))
docker build . \
--output type=local,dest=$($@_TMP_OUT) \
-f ./docs/docs.Dockerfile \
--target update
rm -rf ./docs/internal
cp -R "$($@_TMP_OUT)"/out/* ./docs/
rm -rf "$($@_TMP_OUT)"/*
.PHONY: validate-docs
validate-docs: ## validate the doc does not change
@docker build . \
-f ./docs/docs.Dockerfile \
--target validate
.PHONY: check-dependencies
check-dependencies: ## check dependency updates
go list -u -m -f '{{if not .Indirect}}{{if .Update}}{{.}}{{end}}{{end}}' all
@@ -86,7 +118,7 @@ go-mod-tidy: ## Run go mod tidy in a container and output resulting go.mod and g
validate-go-mod: ## Validate go.mod and go.sum are up-to-date
@docker build . --target check-go-mod
validate: validate-go-mod validate-headers ## Validate sources
validate: validate-go-mod validate-headers validate-docs ## Validate sources
pre-commit: validate check-dependencies lint compose-plugin test e2e-compose

View File

@@ -47,6 +47,7 @@ compose-plugin:
.PHONY: cross
cross:
GOOS=linux GOARCH=amd64 $(GO_BUILD) $(TAGS) -o $(COMPOSE_BINARY)-linux-x86_64 ./cmd
GOOS=linux GOARCH=ppc64le $(GO_BUILD) $(TAGS) -o $(COMPOSE_BINARY)-linux-ppc64le ./cmd
GOOS=linux GOARCH=arm64 $(GO_BUILD) $(TAGS) -o $(COMPOSE_BINARY)-linux-aarch64 ./cmd
GOOS=linux GOARM=6 GOARCH=arm $(GO_BUILD) $(TAGS) -o $(COMPOSE_BINARY)-linux-armv6 ./cmd
GOOS=linux GOARM=7 GOARCH=arm $(GO_BUILD) $(TAGS) -o $(COMPOSE_BINARY)-linux-armv7 ./cmd
@@ -70,7 +71,3 @@ check-license-headers:
.PHONY: check-go-mod
check-go-mod:
./scripts/validate/check-go-mod
.PHONY: yamldocs
yamldocs:
go run docs/yaml/main/generate.go

View File

@@ -14,7 +14,7 @@
limitations under the License.
*/
package main
package compatibility
import (
"fmt"
@@ -43,7 +43,8 @@ func getStringFlags() []string {
}
}
func convert(args []string) []string {
// Convert transforms standalone docker-compose args into CLI plugin compliant ones
func Convert(args []string) []string {
var rootFlags []string
command := []string{compose.PluginName}
l := len(args)

View File

@@ -14,7 +14,7 @@
limitations under the License.
*/
package main
package compatibility
import (
"testing"
@@ -71,7 +71,7 @@ func Test_convert(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := convert(tt.args)
got := Convert(tt.args)
assert.DeepEqual(t, tt.want, got)
})
}

View File

@@ -23,6 +23,7 @@ import (
"strings"
"github.com/compose-spec/compose-go/cli"
"github.com/compose-spec/compose-go/loader"
"github.com/compose-spec/compose-go/types"
buildx "github.com/docker/buildx/util/progress"
"github.com/docker/compose/v2/pkg/utils"
@@ -40,6 +41,28 @@ type buildOptions struct {
args []string
noCache bool
memory string
ssh string
}
func (opts buildOptions) toAPIBuildOptions(services []string) (api.BuildOptions, error) {
var SSHKeys []types.SSHKey
var err error
if opts.ssh != "" {
SSHKeys, err = loader.ParseShortSSHSyntax(opts.ssh)
if err != nil {
return api.BuildOptions{}, err
}
}
return api.BuildOptions{
Pull: opts.pull,
Progress: opts.progress,
Args: types.NewMappingWithEquals(opts.args),
NoCache: opts.noCache,
Quiet: opts.quiet,
Services: services,
SSHs: SSHKeys,
}, nil
}
var printerModes = []string{
@@ -73,7 +96,10 @@ func buildCommand(p *projectOptions, backend api.Service) *cobra.Command {
}
return nil
}),
RunE: Adapt(func(ctx context.Context, args []string) error {
RunE: AdaptCmd(func(ctx context.Context, cmd *cobra.Command, args []string) error {
if cmd.Flags().Changed("ssh") && opts.ssh == "" {
opts.ssh = "default"
}
return runBuild(ctx, backend, opts, args)
}),
ValidArgsFunction: serviceCompletion(p),
@@ -82,6 +108,7 @@ func buildCommand(p *projectOptions, backend api.Service) *cobra.Command {
cmd.Flags().BoolVar(&opts.pull, "pull", false, "Always attempt to pull a newer version of the image.")
cmd.Flags().StringVar(&opts.progress, "progress", buildx.PrinterModeAuto, fmt.Sprintf(`Set type of progress output (%s)`, strings.Join(printerModes, ", ")))
cmd.Flags().StringArrayVar(&opts.args, "build-arg", []string{}, "Set build-time variables for services.")
cmd.Flags().StringVar(&opts.ssh, "ssh", "", "Set SSH authentications used when building service images. (use 'default' for using your default SSH Agent)")
cmd.Flags().Bool("parallel", true, "Build images in parallel. DEPRECATED")
cmd.Flags().MarkHidden("parallel") //nolint:errcheck
cmd.Flags().Bool("compress", true, "Compress the build context using gzip. DEPRECATED")
@@ -103,12 +130,9 @@ func runBuild(ctx context.Context, backend api.Service, opts buildOptions, servi
return err
}
return backend.Build(ctx, project, api.BuildOptions{
Pull: opts.pull,
Progress: opts.progress,
Args: types.NewMappingWithEquals(opts.args),
NoCache: opts.noCache,
Quiet: opts.quiet,
Services: services,
})
apiBuildOptions, err := opts.toAPIBuildOptions(services)
if err != nil {
return err
}
return backend.Build(ctx, project, apiBuildOptions)
}

View File

@@ -27,17 +27,21 @@ import (
"github.com/compose-spec/compose-go/cli"
"github.com/compose-spec/compose-go/types"
composegoutils "github.com/compose-spec/compose-go/utils"
dockercli "github.com/docker/cli/cli"
"github.com/docker/cli/cli-plugins/manager"
"github.com/docker/compose/v2/cmd/formatter"
"github.com/docker/cli/cli/command"
"github.com/morikuni/aec"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/docker/compose/v2/cmd/formatter"
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/compose"
"github.com/docker/compose/v2/pkg/progress"
"github.com/docker/compose/v2/pkg/utils"
)
// Command defines a compose CLI command as a func with args
@@ -86,9 +90,6 @@ func Adapt(fn Command) func(cmd *cobra.Command, args []string) error {
})
}
// Warning is a global warning to be displayed to user on command failure
var Warning string
type projectOptions struct {
ProjectName string
Profiles []string
@@ -120,24 +121,6 @@ func (o *projectOptions) WithServices(fn ProjectServicesFunc) func(cmd *cobra.Co
return err
}
if o.EnvFile != "" {
var services types.Services
for _, s := range project.Services {
ef := o.EnvFile
if ef != "" {
if !filepath.IsAbs(ef) {
ef = filepath.Join(project.WorkingDir, o.EnvFile)
}
if s.Labels == nil {
s.Labels = make(map[string]string)
}
s.Labels[api.EnvironmentFileLabel] = ef
services = append(services, s)
}
}
project.Services = services
}
return fn(ctx, project, args)
})
}
@@ -147,8 +130,8 @@ func (o *projectOptions) addProjectFlags(f *pflag.FlagSet) {
f.StringVarP(&o.ProjectName, "project-name", "p", "", "Project name")
f.StringArrayVarP(&o.ConfigPaths, "file", "f", []string{}, "Compose configuration files")
f.StringVar(&o.EnvFile, "env-file", "", "Specify an alternate environment file.")
f.StringVar(&o.ProjectDir, "project-directory", "", "Specify an alternate working directory\n(default: the path of the Compose file)")
f.StringVar(&o.WorkDir, "workdir", "", "DEPRECATED! USE --project-directory INSTEAD.\nSpecify an alternate working directory\n(default: the path of the Compose file)")
f.StringVar(&o.ProjectDir, "project-directory", "", "Specify an alternate working directory\n(default: the path of the, first specified, Compose file)")
f.StringVar(&o.WorkDir, "workdir", "", "DEPRECATED! USE --project-directory INSTEAD.\nSpecify an alternate working directory\n(default: the path of the, first specified, Compose file)")
f.BoolVar(&o.Compatibility, "compatibility", false, "Run compose in backward compatibility mode")
_ = f.MarkHidden("workdir")
}
@@ -158,6 +141,11 @@ func (o *projectOptions) toProjectName() (string, error) {
return o.ProjectName, nil
}
envProjectName := os.Getenv("COMPOSE_PROJECT_NAME")
if envProjectName != "" {
return envProjectName, nil
}
project, err := o.toProject(nil)
if err != nil {
return "", err
@@ -176,10 +164,32 @@ func (o *projectOptions) toProject(services []string, po ...cli.ProjectOptionsFn
return nil, compose.WrapComposeError(err)
}
if o.Compatibility || project.Environment["COMPOSE_COMPATIBILITY"] == "true" {
if o.Compatibility || utils.StringToBool(project.Environment["COMPOSE_COMPATIBILITY"]) {
compose.Separator = "_"
}
ef := o.EnvFile
if ef != "" && !filepath.IsAbs(ef) {
ef, err = filepath.Abs(ef)
if err != nil {
return nil, err
}
}
for i, s := range project.Services {
s.CustomLabels = map[string]string{
api.ProjectLabel: project.Name,
api.ServiceLabel: s.Name,
api.VersionLabel: api.ComposeVersion,
api.WorkingDirLabel: project.WorkingDir,
api.ConfigFilesLabel: strings.Join(project.ComposeFiles, ","),
api.OneoffLabel: "False", // default, will be overridden by `run` command
}
if ef != "" {
s.CustomLabels[api.EnvironmentFileLabel] = ef
}
project.Services[i] = s
}
if len(services) > 0 {
s, err := project.GetServices(services...)
if err != nil {
@@ -204,14 +214,15 @@ func (o *projectOptions) toProjectOptions(po ...cli.ProjectOptionsFn) (*cli.Proj
return cli.NewProjectOptions(o.ConfigPaths,
append(po,
cli.WithWorkingDirectory(o.ProjectDir),
cli.WithOsEnv,
cli.WithEnvFile(o.EnvFile),
cli.WithDotEnv,
cli.WithOsEnv,
cli.WithConfigFileEnv,
cli.WithDefaultConfigPath,
cli.WithName(o.ProjectName))...)
}
// PluginName is the name of the plugin
const PluginName = "compose"
// RunningAsStandalone detects when running as a standalone program
@@ -220,7 +231,7 @@ func RunningAsStandalone() bool {
}
// RootCommand returns the compose command with its child commands
func RootCommand(backend api.Service) *cobra.Command {
func RootCommand(dockerCli command.Cli, backend api.Service) *cobra.Command {
opts := projectOptions{}
var (
ansi string
@@ -247,6 +258,10 @@ func RootCommand(backend api.Service) *cobra.Command {
}
},
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
err := setEnvWithDotEnv(&opts)
if err != nil {
return err
}
parent := cmd.Root()
if parent != nil {
parentPrerun := parent.PersistentPreRunE
@@ -262,12 +277,18 @@ func RootCommand(backend api.Service) *cobra.Command {
return errors.New(`cannot specify DEPRECATED "--no-ansi" and "--ansi". Please use only "--ansi"`)
}
ansi = "never"
fmt.Fprint(os.Stderr, aec.Apply("option '--no-ansi' is DEPRECATED ! Please use '--ansi' instead.\n", aec.RedF))
fmt.Fprint(os.Stderr, "option '--no-ansi' is DEPRECATED ! Please use '--ansi' instead.\n")
}
if verbose {
logrus.SetLevel(logrus.TraceLevel)
}
formatter.SetANSIMode(ansi)
switch ansi {
case "never":
progress.Mode = progress.ModePlain
case "tty":
progress.Mode = progress.ModeTTY
}
if opts.WorkDir != "" {
if opts.ProjectDir != "" {
return errors.New(`cannot specify DEPRECATED "--workdir" and "--project-directory". Please use only "--project-directory" instead`)
@@ -290,9 +311,9 @@ func RootCommand(backend api.Service) *cobra.Command {
logsCommand(&opts, backend),
convertCommand(&opts, backend),
killCommand(&opts, backend),
runCommand(&opts, backend),
runCommand(&opts, dockerCli, backend),
removeCommand(&opts, backend),
execCommand(&opts, backend),
execCommand(&opts, dockerCli, backend),
pauseCommand(&opts, backend),
unpauseCommand(&opts, backend),
topCommand(&opts, backend),
@@ -317,3 +338,27 @@ func RootCommand(backend api.Service) *cobra.Command {
command.Flags().MarkHidden("verbose") //nolint:errcheck
return command
}
func setEnvWithDotEnv(prjOpts *projectOptions) error {
options, err := prjOpts.toProjectOptions()
if err != nil {
return compose.WrapComposeError(err)
}
workingDir, err := options.GetWorkingDir()
if err != nil {
return err
}
envFromFile, err := cli.GetEnvFromFile(composegoutils.GetAsEqualsMap(os.Environ()), workingDir, options.EnvFile)
if err != nil {
return err
}
for k, v := range envFromFile {
if _, ok := os.LookupEnv(k); !ok {
if err = os.Setenv(k, v); err != nil {
return err
}
}
}
return nil
}

View File

@@ -116,7 +116,9 @@ func runConvert(ctx context.Context, backend api.Service, opts convertOptions, s
project, err := opts.toProject(services,
cli.WithInterpolation(!opts.noInterpolate),
cli.WithResolvedPaths(true),
cli.WithNormalization(!opts.noNormalize))
cli.WithNormalization(!opts.noNormalize),
cli.WithDiscardEnvFile)
if err != nil {
return err
}

View File

@@ -55,7 +55,7 @@ func copyCommand(p *projectOptions, backend api.Service) *cobra.Command {
}
return nil
}),
RunE: Adapt(func(ctx context.Context, args []string) error {
RunE: AdaptCmd(func(ctx context.Context, cmd *cobra.Command, args []string) error {
opts.source = args[0]
opts.destination = args[1]
return runCopy(ctx, backend, opts)
@@ -64,8 +64,10 @@ func copyCommand(p *projectOptions, backend api.Service) *cobra.Command {
}
flags := copyCmd.Flags()
flags.IntVar(&opts.index, "index", 1, "Index of the container if there are multiple instances of a service [default: 1].")
flags.IntVar(&opts.index, "index", 0, "Index of the container if there are multiple instances of a service .")
flags.BoolVar(&opts.all, "all", false, "Copy to all the containers of the service.")
flags.MarkHidden("all") //nolint:errcheck
flags.MarkDeprecated("all", "By default all the containers of the service will get the source file/directory to be copied.") //nolint:errcheck
flags.BoolVarP(&opts.followLink, "follow-link", "L", false, "Always follow symbol link in SRC_PATH")
flags.BoolVarP(&opts.copyUIDGID, "archive", "a", false, "Archive mode (copy all uid/gid information)")
@@ -73,12 +75,12 @@ func copyCommand(p *projectOptions, backend api.Service) *cobra.Command {
}
func runCopy(ctx context.Context, backend api.Service, opts copyOptions) error {
projects, err := opts.toProject(nil)
name, err := opts.toProjectName()
if err != nil {
return err
}
return backend.Copy(ctx, projects, api.CopyOptions{
return backend.Copy(ctx, name, api.CopyOptions{
Source: opts.source,
Destination: opts.destination,
All: opts.all,

View File

@@ -19,10 +19,14 @@ package compose
import (
"context"
"fmt"
"os"
"time"
"github.com/compose-spec/compose-go/types"
"github.com/docker/compose/v2/pkg/utils"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/docker/compose/v2/pkg/api"
)
@@ -55,13 +59,23 @@ func downCommand(p *projectOptions, backend api.Service) *cobra.Command {
RunE: Adapt(func(ctx context.Context, args []string) error {
return runDown(ctx, backend, opts)
}),
Args: cobra.NoArgs,
ValidArgsFunction: noCompletion(),
}
flags := downCmd.Flags()
flags.BoolVar(&opts.removeOrphans, "remove-orphans", false, "Remove containers for services not defined in the Compose file.")
removeOrphans := utils.StringToBool(os.Getenv("COMPOSE_REMOVE_ORPHANS"))
flags.BoolVar(&opts.removeOrphans, "remove-orphans", removeOrphans, "Remove containers for services not defined in the Compose file.")
flags.IntVarP(&opts.timeout, "timeout", "t", 10, "Specify a shutdown timeout in seconds")
flags.BoolVarP(&opts.volumes, "volumes", "v", false, " Remove named volumes declared in the `volumes` section of the Compose file and anonymous volumes attached to containers.")
flags.StringVar(&opts.images, "rmi", "", `Remove images used by services. "local" remove only images that don't have a custom tag ("local"|"all")`)
flags.SetNormalizeFunc(func(f *pflag.FlagSet, name string) pflag.NormalizedName {
switch name {
case "volume":
name = "volumes"
logrus.Warn("--volume is deprecated, please use --volumes")
}
return pflag.NormalizedName(name)
})
return downCmd
}

View File

@@ -18,12 +18,10 @@ package compose
import (
"context"
"fmt"
"os"
"github.com/compose-spec/compose-go/types"
"github.com/containerd/console"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/compose"
"github.com/spf13/cobra"
@@ -37,14 +35,15 @@ type execOpts struct {
environment []string
workingDir string
noTty bool
user string
detach bool
index int
privileged bool
noTty bool
user string
detach bool
index int
privileged bool
interactive bool
}
func execCommand(p *projectOptions, backend api.Service) *cobra.Command {
func execCommand(p *projectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
opts := execOpts{
composeOptions: &composeOptions{
projectOptions: p,
@@ -70,9 +69,14 @@ func execCommand(p *projectOptions, backend api.Service) *cobra.Command {
runCmd.Flags().IntVar(&opts.index, "index", 1, "index of the container if there are multiple instances of a service [default: 1].")
runCmd.Flags().BoolVarP(&opts.privileged, "privileged", "", false, "Give extended privileges to the process.")
runCmd.Flags().StringVarP(&opts.user, "user", "u", "", "Run the command as this user.")
runCmd.Flags().BoolVarP(&opts.noTty, "no-TTY", "T", notAtTTY(), "Disable pseudo-TTY allocation. By default `docker compose exec` allocates a TTY.")
runCmd.Flags().BoolVarP(&opts.noTty, "no-TTY", "T", !dockerCli.Out().IsTerminal(), "Disable pseudo-TTY allocation. By default `docker compose exec` allocates a TTY.")
runCmd.Flags().StringVarP(&opts.workingDir, "workdir", "w", "", "Path to workdir directory for this command.")
runCmd.Flags().BoolVarP(&opts.interactive, "interactive", "i", true, "Keep STDIN open even if not attached.")
runCmd.Flags().MarkHidden("interactive") //nolint:errcheck
runCmd.Flags().BoolP("tty", "t", true, "Allocate a pseudo-TTY.")
runCmd.Flags().MarkHidden("tty") //nolint:errcheck
runCmd.Flags().SetInterspersed(false)
return runCmd
}
@@ -100,27 +104,9 @@ func runExec(ctx context.Context, backend api.Service, opts execOpts) error {
Index: opts.index,
Detach: opts.detach,
WorkingDir: opts.workingDir,
Stdin: os.Stdin,
Stdout: os.Stdout,
Stderr: os.Stderr,
Interactive: opts.interactive,
}
if execOpts.Tty {
con := console.Current()
if err := con.SetRaw(); err != nil {
return err
}
defer func() {
if err := con.Reset(); err != nil {
fmt.Println("Unable to close the console")
}
}()
execOpts.Stdin = con
execOpts.Stdout = con
execOpts.Stderr = con
}
exitCode, err := backend.Exec(ctx, projectName, execOpts)
if exitCode != 0 {
errMsg := ""

View File

@@ -19,25 +19,44 @@ package compose
import (
"context"
"github.com/compose-spec/compose-go/types"
"github.com/spf13/cobra"
"github.com/docker/compose/v2/pkg/api"
)
type killOptions struct {
*projectOptions
signal string
}
func killCommand(p *projectOptions, backend api.Service) *cobra.Command {
var opts api.KillOptions
opts := killOptions{
projectOptions: p,
}
cmd := &cobra.Command{
Use: "kill [options] [SERVICE...]",
Short: "Force stop service containers.",
RunE: p.WithProject(func(ctx context.Context, project *types.Project) error {
return backend.Kill(ctx, project, opts)
RunE: Adapt(func(ctx context.Context, args []string) error {
return runKill(ctx, backend, opts, args)
}),
ValidArgsFunction: serviceCompletion(p),
}
flags := cmd.Flags()
flags.StringVarP(&opts.Signal, "signal", "s", "SIGKILL", "SIGNAL to send to the container.")
flags.StringVarP(&opts.signal, "signal", "s", "SIGKILL", "SIGNAL to send to the container.")
return cmd
}
func runKill(ctx context.Context, backend api.Service, opts killOptions, services []string) error {
projectName, err := opts.toProjectName()
if err != nil {
return err
}
return backend.Kill(ctx, projectName, api.KillOptions{
Services: services,
Signal: opts.signal,
})
}

View File

@@ -92,22 +92,24 @@ func runList(ctx context.Context, backend api.Service, opts lsOptions) error {
view := viewFromStackList(stackList)
return formatter.Print(view, opts.Format, os.Stdout, func(w io.Writer) {
for _, stack := range view {
_, _ = fmt.Fprintf(w, "%s\t%s\n", stack.Name, stack.Status)
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\n", stack.Name, stack.Status, stack.ConfigFiles)
}
}, "NAME", "STATUS")
}, "NAME", "STATUS", "CONFIG FILES")
}
type stackView struct {
Name string
Status string
Name string
Status string
ConfigFiles string
}
func viewFromStackList(stackList []api.Stack) []stackView {
retList := make([]stackView, len(stackList))
for i, s := range stackList {
retList[i] = stackView{
Name: s.Name,
Status: strings.TrimSpace(fmt.Sprintf("%s %s", s.Status, s.Reason)),
Name: s.Name,
Status: strings.TrimSpace(fmt.Sprintf("%s %s", s.Status, s.Reason)),
ConfigFiles: s.ConfigFiles,
}
}
return retList

View File

@@ -27,6 +27,7 @@ import (
"github.com/docker/compose/v2/cmd/formatter"
"github.com/docker/compose/v2/pkg/utils"
"github.com/docker/docker/api/types"
formatter2 "github.com/docker/cli/cli/command/formatter"
"github.com/pkg/errors"
@@ -59,7 +60,7 @@ func (p *psOptions) parseFilter() error {
case "source":
return api.ErrNotImplemented
default:
return fmt.Errorf("unknow filter %s", parts[0])
return fmt.Errorf("unknown filter %s", parts[0])
}
return nil
}
@@ -81,12 +82,11 @@ func psCommand(p *projectOptions, backend api.Service) *cobra.Command {
}
flags := psCmd.Flags()
flags.StringVar(&opts.Format, "format", "pretty", "Format the output. Values: [pretty | json]")
flags.StringVar(&opts.Filter, "filter", "", "Filter services by a property. Deprecated, use --status instead")
flags.StringVar(&opts.Filter, "filter", "", "Filter services by a property (supported filters: status).")
flags.StringArrayVar(&opts.Status, "status", []string{}, "Filter services by status. Values: [paused | restarting | removing | running | dead | created | exited]")
flags.BoolVarP(&opts.Quiet, "quiet", "q", false, "Only display IDs")
flags.BoolVar(&opts.Services, "services", false, "Display services")
flags.BoolVarP(&opts.All, "all", "a", false, "Show all stopped containers (including those created by the run command)")
flags.Lookup("filter").Hidden = true
return psCmd
}
@@ -140,14 +140,14 @@ SERVICES:
}
return formatter.Print(containers, opts.Format, os.Stdout,
writter(containers),
writer(containers),
"NAME", "COMMAND", "SERVICE", "STATUS", "PORTS")
}
func writter(containers []api.ContainerSummary) func(w io.Writer) {
func writer(containers []api.ContainerSummary) func(w io.Writer) {
return func(w io.Writer) {
for _, container := range containers {
ports := DisplayablePorts(container)
ports := displayablePorts(container)
status := container.State
if status == "running" && container.Health != "" {
status = fmt.Sprintf("%s (%s)", container.State, container.Health)
@@ -179,72 +179,20 @@ func hasStatus(c api.ContainerSummary, statuses []string) bool {
return false
}
type portRange struct {
pStart int
pEnd int
tStart int
tEnd int
IP string
protocol string
}
func (pr portRange) String() string {
var (
pub string
tgt string
)
if pr.pEnd > pr.pStart {
pub = fmt.Sprintf("%s:%d-%d->", pr.IP, pr.pStart, pr.pEnd)
} else if pr.pStart > 0 {
pub = fmt.Sprintf("%s:%d->", pr.IP, pr.pStart)
}
if pr.tEnd > pr.tStart {
tgt = fmt.Sprintf("%d-%d", pr.tStart, pr.tEnd)
} else {
tgt = fmt.Sprintf("%d", pr.tStart)
}
return fmt.Sprintf("%s%s/%s", pub, tgt, pr.protocol)
}
// DisplayablePorts is copy pasted from https://github.com/docker/cli/pull/581/files
func DisplayablePorts(c api.ContainerSummary) string {
func displayablePorts(c api.ContainerSummary) string {
if c.Publishers == nil {
return ""
}
sort.Sort(c.Publishers)
pr := portRange{}
ports := []string{}
for _, p := range c.Publishers {
prIsRange := pr.tEnd != pr.tStart
tOverlaps := p.TargetPort <= pr.tEnd
// Start a new port-range if:
// - the protocol is different from the current port-range
// - published or target port are not consecutive to the current port-range
// - the current port-range is a _range_, and the target port overlaps with the current range's target-ports
if p.Protocol != pr.protocol || p.URL != pr.IP || p.PublishedPort-pr.pEnd > 1 || p.TargetPort-pr.tEnd > 1 || prIsRange && tOverlaps {
// start a new port-range, and print the previous port-range (if any)
if pr.pStart > 0 {
ports = append(ports, pr.String())
}
pr = portRange{
pStart: p.PublishedPort,
pEnd: p.PublishedPort,
tStart: p.TargetPort,
tEnd: p.TargetPort,
protocol: p.Protocol,
IP: p.URL,
}
continue
ports := make([]types.Port, len(c.Publishers))
for i, pub := range c.Publishers {
ports[i] = types.Port{
IP: pub.URL,
PrivatePort: uint16(pub.TargetPort),
PublicPort: uint16(pub.PublishedPort),
Type: pub.Protocol,
}
pr.pEnd = p.PublishedPort
pr.tEnd = p.TargetPort
}
if pr.tStart > 0 {
ports = append(ports, pr.String())
}
return strings.Join(ports, ", ")
return formatter2.DisplayablePorts(ports)
}

84
cmd/compose/ps_test.go Normal file
View File

@@ -0,0 +1,84 @@
/*
Copyright 2020 Docker Compose CLI authors
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.
*/
package compose
import (
"context"
"os"
"path/filepath"
"testing"
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/mocks"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
)
func TestPsPretty(t *testing.T) {
ctx := context.Background()
origStdout := os.Stdout
t.Cleanup(func() {
os.Stdout = origStdout
})
dir := t.TempDir()
f, err := os.Create(filepath.Join(dir, "output.txt"))
if err != nil {
t.Fatal("could not create output file")
}
defer func() { _ = f.Close() }()
os.Stdout = f
ctrl := gomock.NewController(t)
defer ctrl.Finish()
backend := mocks.NewMockService(ctrl)
backend.EXPECT().
Ps(gomock.Eq(ctx), gomock.Any(), gomock.Any()).
DoAndReturn(func(ctx context.Context, projectName string, options api.PsOptions) ([]api.ContainerSummary, error) {
return []api.ContainerSummary{
{
ID: "abc123",
Name: "ABC",
Publishers: api.PortPublishers{
{
TargetPort: 8080,
PublishedPort: 8080,
Protocol: "tcp",
},
{
TargetPort: 8443,
PublishedPort: 8443,
Protocol: "tcp",
},
},
},
}, nil
}).AnyTimes()
opts := psOptions{projectOptions: &projectOptions{ProjectName: "test"}}
err = runPs(ctx, backend, nil, opts)
assert.NoError(t, err)
_, err = f.Seek(0, 0)
assert.NoError(t, err)
output := make([]byte, 256)
_, err = f.Read(output)
assert.NoError(t, err)
assert.Contains(t, string(output), "8080/tcp, 8443/tcp")
}

View File

@@ -59,7 +59,7 @@ Any data which is not in a volume will be lost.`,
}
func runRemove(ctx context.Context, backend api.Service, opts removeOptions, services []string) error {
project, err := opts.toProject(services)
project, err := opts.toProjectName()
if err != nil {
return err
}

View File

@@ -49,13 +49,13 @@ func restartCommand(p *projectOptions, backend api.Service) *cobra.Command {
}
func runRestart(ctx context.Context, backend api.Service, opts restartOptions, services []string) error {
project, err := opts.toProject(services)
projectName, err := opts.toProjectName()
if err != nil {
return err
}
timeout := time.Duration(opts.timeout) * time.Second
return backend.Restart(ctx, project, api.RestartOptions{
return backend.Restart(ctx, projectName, api.RestartOptions{
Timeout: &timeout,
Services: services,
})

View File

@@ -19,13 +19,12 @@ package compose
import (
"context"
"fmt"
"os"
"strings"
cgo "github.com/compose-spec/compose-go/cli"
"github.com/compose-spec/compose-go/loader"
"github.com/compose-spec/compose-go/types"
"github.com/mattn/go-isatty"
"github.com/docker/cli/cli/command"
"github.com/mattn/go-shellwords"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
@@ -43,6 +42,7 @@ type runOptions struct {
Detach bool
Remove bool
noTty bool
interactive bool
user string
workdir string
entrypoint string
@@ -54,6 +54,7 @@ type runOptions struct {
servicePorts bool
name string
noDeps bool
ignoreOrphans bool
quietPull bool
}
@@ -62,6 +63,9 @@ func (opts runOptions) apply(project *types.Project) error {
if err != nil {
return err
}
target.Tty = !opts.noTty
target.StdinOpen = opts.interactive
if !opts.servicePorts {
target.Ports = []types.ServicePortConfig{}
}
@@ -103,7 +107,7 @@ func (opts runOptions) apply(project *types.Project) error {
return nil
}
func runCommand(p *projectOptions, backend api.Service) *cobra.Command {
func runCommand(p *projectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
opts := runOptions{
composeOptions: &composeOptions{
projectOptions: p,
@@ -135,6 +139,8 @@ func runCommand(p *projectOptions, backend api.Service) *cobra.Command {
if err != nil {
return err
}
ignore := project.Environment["COMPOSE_IGNORE_ORPHANS"]
opts.ignoreOrphans = strings.ToLower(ignore) == "true"
return runRun(ctx, backend, project, opts)
}),
ValidArgsFunction: serviceCompletion(p),
@@ -144,7 +150,7 @@ func runCommand(p *projectOptions, backend api.Service) *cobra.Command {
flags.StringArrayVarP(&opts.environment, "env", "e", []string{}, "Set environment variables")
flags.StringArrayVarP(&opts.labels, "label", "l", []string{}, "Add or override a label")
flags.BoolVar(&opts.Remove, "rm", false, "Automatically remove the container when it exits")
flags.BoolVarP(&opts.noTty, "no-TTY", "T", notAtTTY(), "Disable pseudo-noTty allocation. By default docker compose run allocates a TTY")
flags.BoolVarP(&opts.noTty, "no-TTY", "T", !dockerCli.Out().IsTerminal(), "Disable pseudo-TTY allocation (default: auto-detected).")
flags.StringVar(&opts.name, "name", "", " Assign a name to the container")
flags.StringVarP(&opts.user, "user", "u", "", "Run as specified username or uid")
flags.StringVarP(&opts.workdir, "workdir", "w", "", "Working directory inside the container")
@@ -156,6 +162,10 @@ func runCommand(p *projectOptions, backend api.Service) *cobra.Command {
flags.BoolVar(&opts.servicePorts, "service-ports", false, "Run command with the service's ports enabled and mapped to the host.")
flags.BoolVar(&opts.quietPull, "quiet-pull", false, "Pull without printing progress information.")
cmd.Flags().BoolVarP(&opts.interactive, "interactive", "i", true, "Keep STDIN open even if not attached.")
cmd.Flags().BoolP("tty", "t", true, "Allocate a pseudo-TTY.")
cmd.Flags().MarkHidden("tty") //nolint:errcheck
flags.SetNormalizeFunc(normalizeRunFlags)
flags.SetInterspersed(false)
return cmd
@@ -171,11 +181,6 @@ func normalizeRunFlags(f *pflag.FlagSet, name string) pflag.NormalizedName {
return pflag.NormalizedName(name)
}
func notAtTTY() bool {
b := isatty.IsTerminal(os.Stdout.Fd()) && isatty.IsTerminal(os.Stdin.Fd())
return !b
}
func runRun(ctx context.Context, backend api.Service, project *types.Project, opts runOptions) error {
err := opts.apply(project)
if err != nil {
@@ -183,7 +188,7 @@ func runRun(ctx context.Context, backend api.Service, project *types.Project, op
}
err = progress.Run(ctx, func(ctx context.Context) error {
return startDependencies(ctx, backend, *project, opts.Service)
return startDependencies(ctx, backend, *project, opts.Service, opts.ignoreOrphans)
})
if err != nil {
return err
@@ -205,10 +210,8 @@ func runRun(ctx context.Context, backend api.Service, project *types.Project, op
Command: opts.Command,
Detach: opts.Detach,
AutoRemove: opts.Remove,
Stdin: os.Stdin,
Stdout: os.Stdout,
Stderr: os.Stderr,
Tty: !opts.noTty,
Interactive: opts.interactive,
WorkingDir: opts.workdir,
User: opts.user,
Environment: opts.environment,
@@ -219,6 +222,14 @@ func runRun(ctx context.Context, backend api.Service, project *types.Project, op
Index: 0,
QuietPull: opts.quietPull,
}
for i, service := range project.Services {
if service.Name == opts.Service {
service.StdinOpen = opts.interactive
project.Services[i] = service
}
}
exitCode, err := backend.RunOneOffContainer(ctx, project, runOpts)
if exitCode != 0 {
errMsg := ""
@@ -230,7 +241,7 @@ func runRun(ctx context.Context, backend api.Service, project *types.Project, op
return err
}
func startDependencies(ctx context.Context, backend api.Service, project types.Project, requestedServiceName string) error {
func startDependencies(ctx context.Context, backend api.Service, project types.Project, requestedServiceName string, ignoreOrphans bool) error {
dependencies := types.Services{}
var requestedService types.ServiceConfig
for _, service := range project.Services {
@@ -243,8 +254,17 @@ func startDependencies(ctx context.Context, backend api.Service, project types.P
project.Services = dependencies
project.DisabledServices = append(project.DisabledServices, requestedService)
if err := backend.Create(ctx, &project, api.CreateOptions{}); err != nil {
err := backend.Create(ctx, &project, api.CreateOptions{
IgnoreOrphans: ignoreOrphans,
})
if err != nil {
return err
}
return backend.Start(ctx, &project, api.StartOptions{})
if len(dependencies) > 0 {
return backend.Start(ctx, project.Name, api.StartOptions{
Project: &project,
})
}
return nil
}

View File

@@ -43,10 +43,12 @@ func startCommand(p *projectOptions, backend api.Service) *cobra.Command {
}
func runStart(ctx context.Context, backend api.Service, opts startOptions, services []string) error {
project, err := opts.toProject(services)
projectName, err := opts.toProjectName()
if err != nil {
return err
}
return backend.Start(ctx, project, api.StartOptions{})
return backend.Start(ctx, projectName, api.StartOptions{
AttachTo: services,
})
}

View File

@@ -53,7 +53,7 @@ func stopCommand(p *projectOptions, backend api.Service) *cobra.Command {
}
func runStop(ctx context.Context, backend api.Service, opts stopOptions, services []string) error {
project, err := opts.toProject(services)
projectName, err := opts.toProjectName()
if err != nil {
return err
}
@@ -63,7 +63,7 @@ func runStop(ctx context.Context, backend api.Service, opts stopOptions, service
timeoutValue := time.Duration(opts.timeout) * time.Second
timeout = &timeoutValue
}
return backend.Stop(ctx, project, api.StopOptions{
return backend.Stop(ctx, projectName, api.StopOptions{
Timeout: timeout,
Services: services,
})

View File

@@ -103,8 +103,7 @@ func upCommand(p *projectOptions, backend api.Service) *cobra.Command {
return validateFlags(&up, &create)
}),
RunE: p.WithServices(func(ctx context.Context, project *types.Project, services []string) error {
ignore := project.Environment["COMPOSE_IGNORE_ORPHANS"]
create.ignoreOrphans = strings.ToLower(ignore) == "true"
create.ignoreOrphans = utils.StringToBool(project.Environment["COMPOSE_IGNORE_ORPHANS"])
if create.ignoreOrphans && create.removeOrphans {
return fmt.Errorf("COMPOSE_IGNORE_ORPHANS and --remove-orphans cannot be combined")
}
@@ -186,6 +185,9 @@ func runUp(ctx context.Context, backend api.Service, createOptions createOptions
if upOptions.attachDependencies {
attachTo = project.ServiceNames()
}
if len(attachTo) == 0 {
attachTo = project.ServiceNames()
}
create := api.CreateOptions{
Services: services,
@@ -205,6 +207,7 @@ func runUp(ctx context.Context, backend api.Service, createOptions createOptions
return backend.Up(ctx, project, api.UpOptions{
Create: create,
Start: api.StartOptions{
Project: project,
Attach: consumer,
AttachTo: attachTo,
ExitCodeFrom: upOptions.exitCodeFrom,

View File

@@ -18,6 +18,7 @@ package compose
import (
"fmt"
"strings"
"github.com/docker/compose/v2/cmd/formatter"
@@ -52,11 +53,11 @@ func versionCommand() *cobra.Command {
func runVersion(opts versionOptions) {
if opts.short {
fmt.Println(internal.Version)
fmt.Println(strings.TrimPrefix(internal.Version, "v"))
return
}
if opts.format == formatter.JSON {
fmt.Printf(`{"version":%q}\n`, internal.Version)
fmt.Printf("{\"version\":%q}\n", internal.Version)
return
}
fmt.Println("Docker Compose version", internal.Version)

View File

@@ -79,7 +79,7 @@ func (l *logConsumer) Log(container, service, message string) {
}
p := l.getPresenter(container)
for _, line := range strings.Split(message, "\n") {
fmt.Fprintf(l.writer, "%s %s\n", p.prefix, line) // nolint:errcheck
fmt.Fprintf(l.writer, "%s%s\n", p.prefix, line) // nolint:errcheck
}
}
@@ -118,5 +118,5 @@ type presenter struct {
}
func (p *presenter) setPrefix(width int) {
p.prefix = p.colors(fmt.Sprintf("%-"+strconv.Itoa(width)+"s |", p.name))
p.prefix = p.colors(fmt.Sprintf("%-"+strconv.Itoa(width)+"s | ", p.name))
}

View File

@@ -25,27 +25,23 @@ import (
"github.com/docker/cli/cli/command"
"github.com/spf13/cobra"
"github.com/docker/compose/v2/cmd/compatibility"
commands "github.com/docker/compose/v2/cmd/compose"
"github.com/docker/compose/v2/internal"
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/compose"
)
func init() {
commands.Warning = "The new 'docker compose' command is currently experimental. " +
"To provide feedback or request new features please open issues at https://github.com/docker/compose"
}
func pluginMain() {
plugin.Run(func(dockerCli command.Cli) *cobra.Command {
lazyInit := api.NewServiceProxy()
cmd := commands.RootCommand(lazyInit)
cmd := commands.RootCommand(dockerCli, lazyInit)
originalPreRun := cmd.PersistentPreRunE
cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
if err := plugin.PersistentPreRunE(cmd, args); err != nil {
return err
}
lazyInit.WithService(compose.NewComposeService(dockerCli.Client(), dockerCli.ConfigFile()))
lazyInit.WithService(compose.NewComposeService(dockerCli))
if originalPreRun != nil {
return originalPreRun(cmd, args)
}
@@ -67,8 +63,8 @@ func pluginMain() {
}
func main() {
if commands.RunningAsStandalone() {
os.Args = append([]string{"docker"}, convert(os.Args[1:])...)
if plugin.RunningStandalone() {
os.Args = append([]string{"docker"}, compatibility.Convert(os.Args[1:])...)
}
pluginMain()
}

57
docs/docs.Dockerfile Normal file
View File

@@ -0,0 +1,57 @@
# syntax=docker/dockerfile:1.3-labs
# Copyright 2020 Docker Compose CLI authors
# 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.
ARG GO_VERSION=1.18.3
ARG FORMATS=md,yaml
FROM --platform=${BUILDPLATFORM} golang:${GO_VERSION}-alpine AS docsgen
WORKDIR /src
RUN --mount=target=. \
--mount=target=/root/.cache,type=cache \
go build -o /out/docsgen ./docs/yaml/main/generate.go
FROM --platform=${BUILDPLATFORM} alpine AS gen
RUN apk add --no-cache rsync git
WORKDIR /src
COPY --from=docsgen /out/docsgen /usr/bin
ARG FORMATS
RUN --mount=target=/context \
--mount=target=.,type=tmpfs <<EOT
set -e
rsync -a /context/. .
docsgen --formats "$FORMATS" --source "docs/reference"
mkdir /out
cp -r docs/reference /out
EOT
FROM scratch AS update
COPY --from=gen /out /out
FROM gen AS validate
RUN --mount=target=/context \
--mount=target=.,type=tmpfs <<EOT
set -e
rsync -a /context/. .
git add -A
rm -rf docs/reference/*
cp -rf /out/* ./docs/
if [ -n "$(git status --porcelain -- docs/reference)" ]; then
echo >&2 'ERROR: Docs result differs. Please update with "make docs"'
git status --porcelain -- docs/reference
exit 1
fi
EOT

View File

@@ -1,4 +1,54 @@
# docker compose
<!---MARKER_GEN_START-->
Docker Compose
### Subcommands
| Name | Description |
| --- | --- |
| [`build`](compose_build.md) | Build or rebuild services |
| [`convert`](compose_convert.md) | Converts the compose file to platform's canonical format |
| [`cp`](compose_cp.md) | Copy files/folders between a service container and the local filesystem |
| [`create`](compose_create.md) | Creates containers for a service. |
| [`down`](compose_down.md) | Stop and remove containers, networks |
| [`events`](compose_events.md) | Receive real time events from containers. |
| [`exec`](compose_exec.md) | Execute a command in a running container. |
| [`images`](compose_images.md) | List images used by the created containers |
| [`kill`](compose_kill.md) | Force stop service containers. |
| [`logs`](compose_logs.md) | View output from containers |
| [`ls`](compose_ls.md) | List running compose projects |
| [`pause`](compose_pause.md) | Pause services |
| [`port`](compose_port.md) | Print the public port for a port binding. |
| [`ps`](compose_ps.md) | List containers |
| [`pull`](compose_pull.md) | Pull service images |
| [`push`](compose_push.md) | Push service images |
| [`restart`](compose_restart.md) | Restart containers |
| [`rm`](compose_rm.md) | Removes stopped service containers |
| [`run`](compose_run.md) | Run a one-off command on a service. |
| [`start`](compose_start.md) | Start services |
| [`stop`](compose_stop.md) | Stop services |
| [`top`](compose_top.md) | Display the running processes |
| [`unpause`](compose_unpause.md) | Unpause services |
| [`up`](compose_up.md) | Create and start containers |
| [`version`](compose_version.md) | Show the Docker Compose version information |
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `--ansi` | `string` | `auto` | Control when to print ANSI control characters ("never"\|"always"\|"auto") |
| `--compatibility` | | | Run compose in backward compatibility mode |
| `--env-file` | `string` | | Specify an alternate environment file. |
| `-f`, `--file` | `stringArray` | | Compose configuration files |
| `--profile` | `stringArray` | | Specify a profile to enable |
| `--project-directory` | `string` | | Specify an alternate working directory
(default: the path of the, first specified, Compose file) |
| `-p`, `--project-name` | `string` | | Project name |
<!---MARKER_GEN_END-->
## Description
@@ -9,8 +59,8 @@ multiple services in Docker containers.
Use the `-f` flag to specify the location of a Compose configuration file.
#### Specifying multiple Compose files
You can supply multiple `-f` configuration files. When you supply multiple files, Compose combines them into a single
configuration. Compose builds the configuration in the order you supply the files. Subsequent files override and add
You can supply multiple `-f` configuration files. When you supply multiple files, Compose combines them into a single
configuration. Compose builds the configuration in the order you supply the files. Subsequent files override and add
to their predecessors.
For example, consider this command line:
@@ -30,7 +80,7 @@ services:
volumes:
- "/data"
```
If the `docker-compose.admin.yml` also specifies this same service, any matching fields override the previous file.
If the `docker-compose.admin.yml` also specifies this same service, any matching fields override the previous file.
New values, add to the `webapp` service configuration.
```yaml
@@ -41,22 +91,22 @@ services:
- DEBUG=1
```
When you use multiple Compose files, all paths in the files are relative to the first configuration file specified
When you use multiple Compose files, all paths in the files are relative to the first configuration file specified
with `-f`. You can use the `--project-directory` option to override this base path.
Use a `-f` with `-` (dash) as the filename to read the configuration from stdin. When stdin is used all paths in the
Use a `-f` with `-` (dash) as the filename to read the configuration from stdin. When stdin is used all paths in the
configuration are relative to the current working directory.
The `-f` flag is optional. If you dont provide this flag on the command line, Compose traverses the working directory
The `-f` flag is optional. If you dont provide this flag on the command line, Compose traverses the working directory
and its parent directories looking for a `compose.yaml` or `docker-compose.yaml` file.
#### Specifying a path to a single Compose file
You can use the `-f` flag to specify a path to a Compose file that is not located in the current directory, either
You can use the `-f` flag to specify a path to a Compose file that is not located in the current directory, either
from the command line or by setting up a `COMPOSE_FILE` environment variable in your shell or in an environment file.
For an example of using the `-f` option at the command line, suppose you are running the Compose Rails sample, and
have a `compose.yaml` file in a directory called `sandbox/rails`. You can use a command like `docker compose pull` to
get the postgres image for the db service from anywhere by using the `-f` flag as follows:
For an example of using the `-f` option at the command line, suppose you are running the Compose Rails sample, and
have a `compose.yaml` file in a directory called `sandbox/rails`. You can use a command like `docker compose pull` to
get the postgres image for the db service from anywhere by using the `-f` flag as follows:
```console
$ docker compose -f ~/sandbox/rails/compose.yaml pull db
@@ -64,17 +114,17 @@ $ docker compose -f ~/sandbox/rails/compose.yaml pull db
### Use `-p` to specify a project name
Each configuration has a project name. If you supply a `-p` flag, you can specify a project name. If you dont
specify the flag, Compose uses the current directory name.
Each configuration has a project name. If you supply a `-p` flag, you can specify a project name. If you dont
specify the flag, Compose uses the current directory name.
Project name can also be set by `COMPOSE_PROJECT_NAME` environment variable.
Most compose subcommand can be ran without a compose file, just passing
Most compose subcommand can be ran without a compose file, just passing
project name to retrieve the relevant resources.
```console
$ docker compose -p my_project ps -a
NAME SERVICE STATUS PORTS
my_project_demo_1 demo running
my_project_demo_1 demo running
$ docker compose -p my_project logs
demo_1 | PING localhost (127.0.0.1): 56 data bytes
@@ -84,8 +134,8 @@ demo_1 | 64 bytes from 127.0.0.1: seq=0 ttl=64 time=0.095 ms
### Use profiles to enable optional services
Use `--profile` to specify one or more active profiles
Calling `docker compose --profile frontend up` will start the services with the profile `frontend` and services
without any specified profiles.
Calling `docker compose --profile frontend up` will start the services with the profile `frontend` and services
without any specified profiles.
You can also enable multiple profiles, e.g. with `docker compose --profile frontend --profile debug up` the profiles `frontend` and `debug` will be enabled.
Profiles can also be set by `COMPOSE_PROFILES` environment variable.
@@ -99,3 +149,6 @@ Setting the `COMPOSE_FILE` environment variable is equivalent to passing the `-f
and so does `COMPOSE_PROFILES` environment variable for to the `--profiles` flag.
If flags are explicitly set on command line, associated environment variable is ignored
Setting the `COMPOSE_IGNORE_ORPHANS` environment variable to `true` will stop docker compose from detecting orphaned
containers for the project.

View File

@@ -1,12 +1,30 @@
# docker compose build
<!---MARKER_GEN_START-->
Build or rebuild services
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `--build-arg` | `stringArray` | | Set build-time variables for services. |
| `--no-cache` | | | Do not use cache when building the image |
| `--progress` | `string` | `auto` | Set type of progress output (auto, tty, plain, quiet) |
| `--pull` | | | Always attempt to pull a newer version of the image. |
| `-q`, `--quiet` | | | Don't print anything to STDOUT |
| `--ssh` | `string` | | Set SSH authentications used when building service images. (use 'default' for using your default SSH Agent) |
<!---MARKER_GEN_END-->
## Description
Services are built once and then tagged, by default as `project_service`.
Services are built once and then tagged, by default as `project_service`.
If the Compose file specifies an
[image](https://github.com/compose-spec/compose-spec/blob/master/spec.md#image) name,
[image](https://github.com/compose-spec/compose-spec/blob/master/spec.md#image) name,
the image is tagged with that name, substituting any variables beforehand. See
[variable interpolation](https://github.com/compose-spec/compose-spec/blob/master/spec.md#interpolation).
If you change a service's `Dockerfile` or the contents of its build directory,
If you change a service's `Dockerfile` or the contents of its build directory,
run `docker compose build` to rebuild it.

View File

@@ -1,9 +1,35 @@
# docker compose convert
<!---MARKER_GEN_START-->
Converts the compose file to platform's canonical format
### Aliases
`convert`, `config`
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `--format` | `string` | `yaml` | Format the output. Values: [yaml \| json] |
| `--hash` | `string` | | Print the service config hash, one per line. |
| `--images` | | | Print the image names, one per line. |
| `--no-interpolate` | | | Don't interpolate environment variables. |
| `--no-normalize` | | | Don't normalize compose model. |
| `-o`, `--output` | `string` | | Save to file (default to stdout) |
| `--profiles` | | | Print the profile names, one per line. |
| `-q`, `--quiet` | | | Only validate the configuration, don't print anything. |
| `--resolve-image-digests` | | | Pin image tags to digests. |
| `--services` | | | Print the service names, one per line. |
| `--volumes` | | | Print the volume names, one per line. |
<!---MARKER_GEN_END-->
## Description
`docker compose convert` render the actual data model to be applied on target platform. When used with Docker engine,
it merges the Compose files set by `-f` flags, resolves variables in Compose file, and expands short-notation into
fully defined Compose model.
it merges the Compose files set by `-f` flags, resolves variables in Compose file, and expands short-notation into
fully defined Compose model.
To allow smooth migration from docker-compose, this subcommand declares alias `docker compose config`

View File

@@ -0,0 +1,16 @@
# docker compose cp
<!---MARKER_GEN_START-->
Copy files/folders between a service container and the local filesystem
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `-a`, `--archive` | | | Archive mode (copy all uid/gid information) |
| `-L`, `--follow-link` | | | Always follow symbol link in SRC_PATH |
| `--index` | `int` | `0` | Index of the container if there are multiple instances of a service . |
<!---MARKER_GEN_END-->

View File

@@ -0,0 +1,17 @@
# docker compose create
<!---MARKER_GEN_START-->
Creates containers for a service.
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `--build` | | | Build images before starting containers. |
| `--force-recreate` | | | Recreate containers even if their configuration and image haven't changed. |
| `--no-build` | | | Don't build an image, even if it's missing. |
| `--no-recreate` | | | If containers already exist, don't recreate them. Incompatible with --force-recreate. |
<!---MARKER_GEN_END-->

View File

@@ -1,4 +1,19 @@
# docker compose down
<!---MARKER_GEN_START-->
Stop and remove containers, networks
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `--remove-orphans` | | | Remove containers for services not defined in the Compose file. |
| `--rmi` | `string` | | Remove images used by services. "local" remove only images that don't have a custom tag ("local"\|"all") |
| `-t`, `--timeout` | `int` | `10` | Specify a shutdown timeout in seconds |
| `-v`, `--volumes` | | | Remove named volumes declared in the `volumes` section of the Compose file and anonymous volumes attached to containers. |
<!---MARKER_GEN_END-->
## Description

View File

@@ -1,3 +1,16 @@
# docker compose events
<!---MARKER_GEN_START-->
Receive real time events from containers.
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `--json` | | | Output events as a stream of json objects |
<!---MARKER_GEN_END-->
## Description

View File

@@ -1,7 +1,26 @@
# docker compose exec
<!---MARKER_GEN_START-->
Execute a command in a running container.
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `-d`, `--detach` | | | Detached mode: Run command in the background. |
| `-e`, `--env` | `stringArray` | | Set environment variables |
| `--index` | `int` | `1` | index of the container if there are multiple instances of a service [default: 1]. |
| `-T`, `--no-TTY` | | | Disable pseudo-TTY allocation. By default `docker compose exec` allocates a TTY. |
| `--privileged` | | | Give extended privileges to the process. |
| `-u`, `--user` | `string` | | Run the command as this user. |
| `-w`, `--workdir` | `string` | | Path to workdir directory for this command. |
<!---MARKER_GEN_END-->
## Description
This is the equivalent of `docker exec` targeting a Compose service.
This is the equivalent of `docker exec` targeting a Compose service.
With this subcommand you can run arbitrary commands in your services. Commands are by default allocating a TTY, so
With this subcommand you can run arbitrary commands in your services. Commands are by default allocating a TTY, so
you can use a command such as `docker compose exec web sh` to get an interactive prompt.

View File

@@ -0,0 +1,14 @@
# docker compose images
<!---MARKER_GEN_START-->
List images used by the created containers
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `-q`, `--quiet` | | | Only display IDs |
<!---MARKER_GEN_END-->

View File

@@ -1,3 +1,16 @@
# docker compose kill
<!---MARKER_GEN_START-->
Force stop service containers.
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `-s`, `--signal` | `string` | `SIGKILL` | SIGNAL to send to the container. |
<!---MARKER_GEN_END-->
## Description

View File

@@ -1,3 +1,22 @@
# docker compose logs
<!---MARKER_GEN_START-->
View output from containers
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `-f`, `--follow` | | | Follow log output. |
| `--no-color` | | | Produce monochrome output. |
| `--no-log-prefix` | | | Don't print prefix in logs. |
| `--since` | `string` | | Show logs since timestamp (e.g. 2013-01-02T13:23:37Z) or relative (e.g. 42m for 42 minutes) |
| `--tail` | `string` | `all` | Number of lines to show from the end of the logs for each container. |
| `-t`, `--timestamps` | | | Show timestamps. |
| `--until` | `string` | | Show logs before a timestamp (e.g. 2013-01-02T13:23:37Z) or relative (e.g. 42m for 42 minutes) |
<!---MARKER_GEN_END-->
## Description

View File

@@ -1,3 +1,19 @@
# docker compose ls
<!---MARKER_GEN_START-->
List running compose projects
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `-a`, `--all` | | | Show all stopped Compose projects |
| `--filter` | `filter` | | Filter output based on conditions provided. |
| `--format` | `string` | `pretty` | Format the output. Values: [pretty \| json]. |
| `-q`, `--quiet` | | | Only display IDs. |
<!---MARKER_GEN_END-->
## Description

View File

@@ -1,3 +1,10 @@
# docker compose pause
<!---MARKER_GEN_START-->
Pause services
<!---MARKER_GEN_END-->
## Description

View File

@@ -1,3 +1,17 @@
# docker compose port
<!---MARKER_GEN_START-->
Print the public port for a port binding.
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `--index` | `int` | `1` | index of the container if service has multiple replicas |
| `--protocol` | `string` | `tcp` | tcp or udp |
<!---MARKER_GEN_END-->
## Description

View File

@@ -1,11 +1,117 @@
# docker compose ps
<!---MARKER_GEN_START-->
List containers
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `-a`, `--all` | | | Show all stopped containers (including those created by the run command) |
| [`--filter`](#filter) | `string` | | Filter services by a property (supported filters: status). |
| [`--format`](#format) | `string` | `pretty` | Format the output. Values: [pretty \| json] |
| `-q`, `--quiet` | | | Only display IDs |
| `--services` | | | Display services |
| [`--status`](#status) | `stringArray` | | Filter services by status. Values: [paused \| restarting \| removing \| running \| dead \| created \| exited] |
<!---MARKER_GEN_END-->
## Description
Lists containers for a Compose project, with current status and exposed ports.
By default, both running and stopped containers are shown:
```console
$ docker compose ps
NAME SERVICE STATUS PORTS
example_foo_1 foo running (healthy) 0.0.0.0:8000->80/tcp
example_bar_1 bar exited (1)
NAME COMMAND SERVICE STATUS PORTS
example-bar-1 "/docker-entrypoint.…" bar exited (0)
example-foo-1 "/docker-entrypoint.…" foo running 0.0.0.0:8080->80/tcp
```
## Examples
### <a name="format"></a> Format the output (--format)
By default, the `docker compose ps` command uses a table ("pretty") format to
show the containers. The `--format` flag allows you to specify alternative
presentations for the output. Currently supported options are `pretty` (default),
and `json`, which outputs information about the containers as a JSON array:
```console
$ docker compose ps --format json
[{"ID":"1553b0236cf4d2715845f053a4ee97042c4f9a2ef655731ee34f1f7940eaa41a","Name":"example-bar-1","Command":"/docker-entrypoint.sh nginx -g 'daemon off;'","Project":"example","Service":"bar","State":"exited","Health":"","ExitCode":0,"Publishers":null},{"ID":"f02a4efaabb67416e1ff127d51c4b5578634a0ad5743bd65225ff7d1909a3fa0","Name":"example-foo-1","Command":"/docker-entrypoint.sh nginx -g 'daemon off;'","Project":"example","Service":"foo","State":"running","Health":"","ExitCode":0,"Publishers":[{"URL":"0.0.0.0","TargetPort":80,"PublishedPort":8080,"Protocol":"tcp"}]}]
```
The JSON output allows you to use the information in other tools for further
processing, for example, using the [`jq` utility](https://stedolan.github.io/jq/){:target="_blank" rel="noopener" class="_"}
to pretty-print the JSON:
```console
$ docker compose ps --format json | jq .
[
{
"ID": "1553b0236cf4d2715845f053a4ee97042c4f9a2ef655731ee34f1f7940eaa41a",
"Name": "example-bar-1",
"Command": "/docker-entrypoint.sh nginx -g 'daemon off;'",
"Project": "example",
"Service": "bar",
"State": "exited",
"Health": "",
"ExitCode": 0,
"Publishers": null
},
{
"ID": "f02a4efaabb67416e1ff127d51c4b5578634a0ad5743bd65225ff7d1909a3fa0",
"Name": "example-foo-1",
"Command": "/docker-entrypoint.sh nginx -g 'daemon off;'",
"Project": "example",
"Service": "foo",
"State": "running",
"Health": "",
"ExitCode": 0,
"Publishers": [
{
"URL": "0.0.0.0",
"TargetPort": 80,
"PublishedPort": 8080,
"Protocol": "tcp"
}
]
}
]
```
### <a name="status"></a> Filter containers by status (--status)
Use the `--status` flag to filter the list of containers by status. For example,
to show only containers that are running, or only containers that have exited:
```console
$ docker compose ps --status=running
NAME COMMAND SERVICE STATUS PORTS
example-foo-1 "/docker-entrypoint.…" foo running 0.0.0.0:8080->80/tcp
$ docker compose ps --status=exited
NAME COMMAND SERVICE STATUS PORTS
example-bar-1 "/docker-entrypoint.…" bar exited (0)
```
### <a name="filter"></a> Filter containers by status (--filter)
The [`--status` flag](#status) is a convenience shorthand for the `--filter status=<status>`
flag. The example below is the equivalent to the example from the previous section,
this time using the `--filter` flag:
```console
$ docker compose ps --filter status=running
NAME COMMAND SERVICE STATUS PORTS
example-foo-1 "/docker-entrypoint.…" foo running 0.0.0.0:8080->80/tcp
$ docker compose ps --filter status=running
NAME COMMAND SERVICE STATUS PORTS
example-bar-1 "/docker-entrypoint.…" bar exited (0)
```
The `docker compose ps` command currently only supports the `--filter status=<status>`
option, but additional filter options may be added in future.

View File

@@ -1,11 +1,26 @@
# docker compose pull
<!---MARKER_GEN_START-->
Pull service images
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `--ignore-pull-failures` | | | Pull what it can and ignores images with pull failures |
| `--include-deps` | | | Also pull services declared as dependencies |
| `-q`, `--quiet` | | | Pull without printing progress information |
<!---MARKER_GEN_END-->
## Description
Pulls an image associated with a service defined in a `compose.yaml` file, but does not start containers based on
Pulls an image associated with a service defined in a `compose.yaml` file, but does not start containers based on
those images.
## Examples
## Examples
suppose you have this `compose.yaml`:
@@ -24,8 +39,8 @@ services:
- db
```
If you run `docker compose pull ServiceName` in the same directory as the `compose.yaml` file that defines the service,
Docker pulls the associated image. For example, to call the postgres image configured as the db service in our example,
If you run `docker compose pull ServiceName` in the same directory as the `compose.yaml` file that defines the service,
Docker pulls the associated image. For example, to call the postgres image configured as the db service in our example,
you would run `docker compose pull db`.
```console
@@ -46,4 +61,4 @@ $ docker compose pull db
⠹ f63c47038e66 Waiting 9.3s
⠹ 77a0c198cde5 Waiting 9.3s
⠹ c8752d5b785c Waiting 9.3s
`
``

View File

@@ -1,3 +1,16 @@
# docker compose push
<!---MARKER_GEN_START-->
Push service images
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `--ignore-push-failures` | | | Push what it can and ignores images with push failures |
<!---MARKER_GEN_END-->
## Description

View File

@@ -1,8 +1,24 @@
# docker compose restart
<!---MARKER_GEN_START-->
Restart containers
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `-t`, `--timeout` | `int` | `10` | Specify a shutdown timeout in seconds |
<!---MARKER_GEN_END-->
## Description
Restarts all stopped and running services.
If you make changes to your `compose.yml` configuration, these changes are not reflected
after running this command. For example, changes to environment variables (which are added
after a container is built, but before the container's command is executed) are not updated
If you make changes to your `compose.yml` configuration, these changes are not reflected
after running this command. For example, changes to environment variables (which are added
after a container is built, but before the container's command is executed) are not updated
after restarting.
If you are looking to configure a service's restart policy, please refer to

View File

@@ -1,3 +1,23 @@
# docker compose rm
<!---MARKER_GEN_START-->
Removes stopped service containers
By default, anonymous volumes attached to containers will not be removed. You
can override this with -v. To list all volumes, use "docker volume ls".
Any data which is not in a volume will be lost.
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `-f`, `--force` | | | Don't ask to confirm removal |
| `-s`, `--stop` | | | Stop the containers, if required, before removing |
| `-v`, `--volumes` | | | Remove any anonymous volumes attached to containers |
<!---MARKER_GEN_END-->
## Description

View File

@@ -1,7 +1,35 @@
# docker compose run
<!---MARKER_GEN_START-->
Run a one-off command on a service.
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `-d`, `--detach` | | | Run container in background and print container ID |
| `--entrypoint` | `string` | | Override the entrypoint of the image |
| `-e`, `--env` | `stringArray` | | Set environment variables |
| `-i`, `--interactive` | | | Keep STDIN open even if not attached. |
| `-l`, `--label` | `stringArray` | | Add or override a label |
| `--name` | `string` | | Assign a name to the container |
| `-T`, `--no-TTY` | | | Disable pseudo-TTY allocation (default: auto-detected). |
| `--no-deps` | | | Don't start linked services. |
| `-p`, `--publish` | `stringArray` | | Publish a container's port(s) to the host. |
| `--quiet-pull` | | | Pull without printing progress information. |
| `--rm` | | | Automatically remove the container when it exits |
| `--service-ports` | | | Run command with the service's ports enabled and mapped to the host. |
| `--use-aliases` | | | Use the service's network useAliases in the network(s) the container connects to. |
| `-u`, `--user` | `string` | | Run as specified username or uid |
| `-v`, `--volume` | `stringArray` | | Bind mount a volume. |
| `-w`, `--workdir` | `string` | | Working directory inside the container |
<!---MARKER_GEN_END-->
## Description
Runs a one-time command against a service.
Runs a one-time command against a service.
the following command starts the `web` service and runs `bash` as its command:
@@ -12,12 +40,12 @@ $ docker compose run web bash
Commands you use with run start in new containers with configuration defined by that of the service,
including volumes, links, and other details. However, there are two important differences:
First, the command passed by `run` overrides the command defined in the service configuration. For example, if the
`web` service configuration is started with `bash`, then `docker compose run web python app.py` overrides it with
First, the command passed by `run` overrides the command defined in the service configuration. For example, if the
`web` service configuration is started with `bash`, then `docker compose run web python app.py` overrides it with
`python app.py`.
The second difference is that the `docker compose run` command does not create any of the ports specified in the
service configuration. This prevents port collisions with already-open ports. If you do want the services ports
The second difference is that the `docker compose run` command does not create any of the ports specified in the
service configuration. This prevents port collisions with already-open ports. If you do want the services ports
to be created and mapped to the host, specify the `--service-ports`
```console
@@ -30,8 +58,8 @@ Alternatively, manual port mapping can be specified with the `--publish` or `-p`
$ docker compose run --publish 8080:80 -p 2022:22 -p 127.0.0.1:2021:21 web python manage.py shell
```
If you start a service configured with links, the run command first checks to see if the linked service is running
and starts the service if it is stopped. Once all the linked services are running, the run executes the command you
If you start a service configured with links, the run command first checks to see if the linked service is running
and starts the service if it is stopped. Once all the linked services are running, the run executes the command you
passed it. For example, you could run:
```console
@@ -52,5 +80,5 @@ If you want to remove the container after running while overriding the container
$ docker compose run --rm web python manage.py db upgrade
```
This runs a database upgrade script, and removes the container when finished running, even if a restart policy is
This runs a database upgrade script, and removes the container when finished running, even if a restart policy is
specified in the service configuration.

View File

@@ -1,3 +1,10 @@
# docker compose start
<!---MARKER_GEN_START-->
Start services
<!---MARKER_GEN_END-->
## Description

View File

@@ -1,3 +1,16 @@
# docker compose stop
<!---MARKER_GEN_START-->
Stop services
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `-t`, `--timeout` | `int` | `10` | Specify a shutdown timeout in seconds |
<!---MARKER_GEN_END-->
## Description

View File

@@ -1,3 +1,10 @@
# docker compose top
<!---MARKER_GEN_START-->
Display the running processes
<!---MARKER_GEN_END-->
## Description
@@ -9,5 +16,5 @@ Displays the running processes.
$ docker compose top
example_foo_1
UID PID PPID C STIME TTY TIME CMD
root 142353 142331 2 15:33 ? 00:00:00 ping localhost -c 5
root 142353 142331 2 15:33 ? 00:00:00 ping localhost -c 5
```

View File

@@ -1,3 +1,10 @@
# docker compose unpause
<!---MARKER_GEN_START-->
Unpause services
<!---MARKER_GEN_END-->
## Description

View File

@@ -1,3 +1,35 @@
# docker compose up
<!---MARKER_GEN_START-->
Create and start containers
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `--abort-on-container-exit` | | | Stops all containers if any container was stopped. Incompatible with -d |
| `--always-recreate-deps` | | | Recreate dependent containers. Incompatible with --no-recreate. |
| `--attach` | `stringArray` | | Attach to service output. |
| `--attach-dependencies` | | | Attach to dependent containers. |
| `--build` | | | Build images before starting containers. |
| `-d`, `--detach` | | | Detached mode: Run containers in the background |
| `--exit-code-from` | `string` | | Return the exit code of the selected service container. Implies --abort-on-container-exit |
| `--force-recreate` | | | Recreate containers even if their configuration and image haven't changed. |
| `--no-build` | | | Don't build an image, even if it's missing. |
| `--no-color` | | | Produce monochrome output. |
| `--no-deps` | | | Don't start linked services. |
| `--no-log-prefix` | | | Don't print prefix in logs. |
| `--no-recreate` | | | If containers already exist, don't recreate them. Incompatible with --force-recreate. |
| `--no-start` | | | Don't start the services after creating them. |
| `--quiet-pull` | | | Pull without printing progress information. |
| `--remove-orphans` | | | Remove containers for services not defined in the Compose file. |
| `-V`, `--renew-anon-volumes` | | | Recreate anonymous volumes instead of retrieving data from the previous containers. |
| `--scale` | `stringArray` | | Scale SERVICE to NUM instances. Overrides the `scale` setting in the Compose file if present. |
| `-t`, `--timeout` | `int` | `10` | Use this timeout in seconds for container shutdown when attached or when containers are already running. |
| `--wait` | | | Wait for services to be running\|healthy. Implies detached mode. |
<!---MARKER_GEN_END-->
## Description
@@ -5,12 +37,12 @@ Builds, (re)creates, starts, and attaches to containers for a service.
Unless they are already running, this command also starts any linked services.
The `docker compose up` command aggregates the output of each container (liked `docker compose logs --follow` does).
When the command exits, all containers are stopped. Running `docker compose up --detach` starts the containers in the
The `docker compose up` command aggregates the output of each container (like `docker compose logs --follow` does).
When the command exits, all containers are stopped. Running `docker compose up --detach` starts the containers in the
background and leaves them running.
If there are existing containers for a service, and the services configuration or image was changed after the
containers creation, `docker compose up` picks up the changes by stopping and recreating the containers
If there are existing containers for a service, and the services configuration or image was changed after the
containers creation, `docker compose up` picks up the changes by stopping and recreating the containers
(preserving mounted volumes). To prevent Compose from picking up changes, use the `--no-recreate` flag.
If you want to force Compose to stop and recreate all containers, use the `--force-recreate` flag.

View File

@@ -0,0 +1,14 @@
# docker compose version
<!---MARKER_GEN_START-->
Show the Docker Compose version information
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `-f`, `--format` | `string` | | Format the output. Values: [pretty \| json]. (Default: pretty) |
| `--short` | | | Shows only Compose's version number. |
<!---MARKER_GEN_END-->

View File

@@ -98,6 +98,9 @@ long: |-
and so does `COMPOSE_PROFILES` environment variable for to the `--profiles` flag.
If flags are explicitly set on command line, associated environment variable is ignored
Setting the `COMPOSE_IGNORE_ORPHANS` environment variable to `true` will stop docker compose from detecting orphaned
containers for the project.
usage: docker compose
pname: docker
plink: docker.yaml
@@ -126,6 +129,7 @@ cname:
- docker compose top
- docker compose unpause
- docker compose up
- docker compose version
clink:
- docker_compose_build.yaml
- docker_compose_convert.yaml
@@ -151,6 +155,7 @@ clink:
- docker_compose_top.yaml
- docker_compose_unpause.yaml
- docker_compose_up.yaml
- docker_compose_version.yaml
options:
- option: ansi
value_type: string
@@ -158,6 +163,17 @@ options:
description: |
Control when to print ANSI control characters ("never"|"always"|"auto")
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: compatibility
value_type: bool
default_value: "false"
description: Run compose in backward compatibility mode
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -166,6 +182,7 @@ options:
value_type: string
description: Specify an alternate environment file.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -176,6 +193,7 @@ options:
default_value: '[]'
description: Compose configuration files
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -185,6 +203,7 @@ options:
default_value: "false"
description: Do not print ANSI control characters (DEPRECATED)
deprecated: false
hidden: true
experimental: false
experimentalcli: false
kubernetes: false
@@ -194,6 +213,7 @@ options:
default_value: '[]'
description: Specify a profile to enable
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -202,8 +222,9 @@ options:
value_type: string
description: |-
Specify an alternate working directory
(default: the path of the Compose file)
(default: the path of the, first specified, Compose file)
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -213,6 +234,7 @@ options:
value_type: string
description: Project name
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -222,6 +244,18 @@ options:
default_value: "false"
description: Show more output
deprecated: false
hidden: true
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: version
shorthand: v
value_type: bool
default_value: "false"
description: Show the Docker Compose version information
deprecated: false
hidden: true
experimental: false
experimentalcli: false
kubernetes: false
@@ -231,8 +265,9 @@ options:
description: |-
DEPRECATED! USE --project-directory INSTEAD.
Specify an alternate working directory
(default: the path of the Compose file)
(default: the path of the, first specified, Compose file)
deprecated: false
hidden: true
experimental: false
experimentalcli: false
kubernetes: false

View File

@@ -19,6 +19,7 @@ options:
default_value: '[]'
description: Set build-time variables for services.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -28,6 +29,7 @@ options:
default_value: "true"
description: Compress the build context using gzip. DEPRECATED
deprecated: false
hidden: true
experimental: false
experimentalcli: false
kubernetes: false
@@ -37,6 +39,7 @@ options:
default_value: "true"
description: Always remove intermediate containers. DEPRECATED
deprecated: false
hidden: true
experimental: false
experimentalcli: false
kubernetes: false
@@ -47,6 +50,7 @@ options:
description: |
Set memory limit for the build container. Not supported on buildkit yet.
deprecated: false
hidden: true
experimental: false
experimentalcli: false
kubernetes: false
@@ -56,6 +60,7 @@ options:
default_value: "false"
description: Do not use cache when building the image
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -66,6 +71,7 @@ options:
description: |
Do not remove intermediate containers after a successful build. DEPRECATED
deprecated: false
hidden: true
experimental: false
experimentalcli: false
kubernetes: false
@@ -75,6 +81,7 @@ options:
default_value: "true"
description: Build images in parallel. DEPRECATED
deprecated: false
hidden: true
experimental: false
experimentalcli: false
kubernetes: false
@@ -82,8 +89,9 @@ options:
- option: progress
value_type: string
default_value: auto
description: Set type of progress output ("auto", "plain", "noTty")
description: Set type of progress output (auto, tty, plain, quiet)
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -93,6 +101,7 @@ options:
default_value: "false"
description: Always attempt to pull a newer version of the image.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -103,6 +112,17 @@ options:
default_value: "false"
description: Don't print anything to STDOUT
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: ssh
value_type: string
description: |
Set SSH authentications used when building service images. (use 'default' for using your default SSH Agent)
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false

View File

@@ -16,6 +16,7 @@ options:
default_value: yaml
description: 'Format the output. Values: [yaml | json]'
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -24,6 +25,17 @@ options:
value_type: string
description: Print the service config hash, one per line.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: images
value_type: bool
default_value: "false"
description: Print the image names, one per line.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -33,6 +45,27 @@ options:
default_value: "false"
description: Don't interpolate environment variables.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: no-normalize
value_type: bool
default_value: "false"
description: Don't normalize compose model.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: output
shorthand: o
value_type: string
description: Save to file (default to stdout)
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -42,6 +75,7 @@ options:
default_value: "false"
description: Print the profile names, one per line.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -52,6 +86,7 @@ options:
default_value: "false"
description: Only validate the configuration, don't print anything.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -61,6 +96,7 @@ options:
default_value: "false"
description: Pin image tags to digests.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -70,6 +106,7 @@ options:
default_value: "false"
description: Print the service names, one per line.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -79,6 +116,7 @@ options:
default_value: "false"
description: Print the volume names, one per line.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false

View File

@@ -10,7 +10,8 @@ options:
value_type: bool
default_value: "false"
description: Copy to all the containers of the service.
deprecated: false
deprecated: true
hidden: true
experimental: false
experimentalcli: false
kubernetes: false
@@ -21,6 +22,7 @@ options:
default_value: "false"
description: Archive mode (copy all uid/gid information)
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -31,16 +33,18 @@ options:
default_value: "false"
description: Always follow symbol link in SRC_PATH
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: index
value_type: int
default_value: "1"
default_value: "0"
description: |
Index of the container if there are multiple instances of a service [default: 1].
Index of the container if there are multiple instances of a service .
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false

View File

@@ -10,6 +10,7 @@ options:
default_value: "false"
description: Build images before starting containers.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -20,6 +21,7 @@ options:
description: |
Recreate containers even if their configuration and image haven't changed.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -29,6 +31,7 @@ options:
default_value: "false"
description: Don't build an image, even if it's missing.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -39,6 +42,7 @@ options:
description: |
If containers already exist, don't recreate them. Incompatible with --force-recreate.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false

View File

@@ -23,6 +23,7 @@ options:
default_value: "false"
description: Remove containers for services not defined in the Compose file.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -32,6 +33,7 @@ options:
description: |
Remove images used by services. "local" remove only images that don't have a custom tag ("local"|"all")
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -42,6 +44,7 @@ options:
default_value: "10"
description: Specify a shutdown timeout in seconds
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -53,6 +56,7 @@ options:
description: |
Remove named volumes declared in the `volumes` section of the Compose file and anonymous volumes attached to containers.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false

View File

@@ -29,6 +29,7 @@ options:
default_value: "false"
description: Output events as a stream of json objects
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false

View File

@@ -15,6 +15,7 @@ options:
default_value: "false"
description: 'Detached mode: Run command in the background.'
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -25,6 +26,7 @@ options:
default_value: '[]'
description: Set environment variables
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -35,6 +37,18 @@ options:
description: |
index of the container if there are multiple instances of a service [default: 1].
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: interactive
shorthand: i
value_type: bool
default_value: "true"
description: Keep STDIN open even if not attached.
deprecated: false
hidden: true
experimental: false
experimentalcli: false
kubernetes: false
@@ -42,10 +56,11 @@ options:
- option: no-TTY
shorthand: T
value_type: bool
default_value: "false"
default_value: "true"
description: |
Disable pseudo-TTY allocation. By default `docker compose exec` allocates a TTY.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -55,6 +70,18 @@ options:
default_value: "false"
description: Give extended privileges to the process.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: tty
shorthand: t
value_type: bool
default_value: "true"
description: Allocate a pseudo-TTY.
deprecated: false
hidden: true
experimental: false
experimentalcli: false
kubernetes: false
@@ -64,6 +91,7 @@ options:
value_type: string
description: Run the command as this user.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -73,6 +101,7 @@ options:
value_type: string
description: Path to workdir directory for this command.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false

View File

@@ -11,6 +11,7 @@ options:
default_value: "false"
description: Only display IDs
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false

View File

@@ -16,6 +16,7 @@ options:
default_value: SIGKILL
description: SIGNAL to send to the container.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false

View File

@@ -11,6 +11,7 @@ options:
default_value: "false"
description: Follow log output.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -20,6 +21,7 @@ options:
default_value: "false"
description: Produce monochrome output.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -29,6 +31,7 @@ options:
default_value: "false"
description: Don't print prefix in logs.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -38,6 +41,7 @@ options:
description: |
Show logs since timestamp (e.g. 2013-01-02T13:23:37Z) or relative (e.g. 42m for 42 minutes)
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -48,6 +52,7 @@ options:
description: |
Number of lines to show from the end of the logs for each container.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -58,6 +63,7 @@ options:
default_value: "false"
description: Show timestamps.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -67,6 +73,7 @@ options:
description: |
Show logs before a timestamp (e.g. 2013-01-02T13:23:37Z) or relative (e.g. 42m for 42 minutes)
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false

View File

@@ -11,6 +11,7 @@ options:
default_value: "false"
description: Show all stopped Compose projects
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -19,6 +20,7 @@ options:
value_type: filter
description: Filter output based on conditions provided.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -28,6 +30,7 @@ options:
default_value: pretty
description: 'Format the output. Values: [pretty | json].'
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -38,6 +41,7 @@ options:
default_value: "false"
description: Only display IDs.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false

View File

@@ -1,5 +1,5 @@
command: docker compose pause
short: pause services
short: Pause services
long: |
Pauses running containers of a service. They can be unpaused with `docker compose unpause`.
usage: docker compose pause [SERVICE...]

View File

@@ -10,6 +10,7 @@ options:
default_value: "1"
description: index of the container if service has multiple replicas
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -19,6 +20,7 @@ options:
default_value: tcp
description: tcp or udp
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false

View File

@@ -2,12 +2,13 @@ command: docker compose ps
short: List containers
long: |-
Lists containers for a Compose project, with current status and exposed ports.
By default, both running and stopped containers are shown:
```console
$ docker compose ps
NAME SERVICE STATUS PORTS
example_foo_1 foo running (healthy) 0.0.0.0:8000->80/tcp
example_bar_1 bar exited (1)
NAME COMMAND SERVICE STATUS PORTS
example-bar-1 "/docker-entrypoint.…" bar exited (0)
example-foo-1 "/docker-entrypoint.…" foo running 0.0.0.0:8080->80/tcp
```
usage: docker compose ps [SERVICE...]
pname: docker compose
@@ -20,14 +21,17 @@ options:
description: |
Show all stopped containers (including those created by the run command)
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: filter
value_type: string
description: Filter services by a property
description: 'Filter services by a property (supported filters: status).'
details_url: '#filter'
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -36,7 +40,9 @@ options:
value_type: string
default_value: pretty
description: 'Format the output. Values: [pretty | json]'
details_url: '#format'
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -47,6 +53,7 @@ options:
default_value: "false"
description: Only display IDs
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -56,18 +63,108 @@ options:
default_value: "false"
description: Display services
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: status
value_type: string
description: Filter services by status
value_type: stringArray
default_value: '[]'
description: |
Filter services by status. Values: [paused | restarting | removing | running | dead | created | exited]
details_url: '#status'
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
examples: |-
### Format the output (--format) {#format}
By default, the `docker compose ps` command uses a table ("pretty") format to
show the containers. The `--format` flag allows you to specify alternative
presentations for the output. Currently supported options are `pretty` (default),
and `json`, which outputs information about the containers as a JSON array:
```console
$ docker compose ps --format json
[{"ID":"1553b0236cf4d2715845f053a4ee97042c4f9a2ef655731ee34f1f7940eaa41a","Name":"example-bar-1","Command":"/docker-entrypoint.sh nginx -g 'daemon off;'","Project":"example","Service":"bar","State":"exited","Health":"","ExitCode":0,"Publishers":null},{"ID":"f02a4efaabb67416e1ff127d51c4b5578634a0ad5743bd65225ff7d1909a3fa0","Name":"example-foo-1","Command":"/docker-entrypoint.sh nginx -g 'daemon off;'","Project":"example","Service":"foo","State":"running","Health":"","ExitCode":0,"Publishers":[{"URL":"0.0.0.0","TargetPort":80,"PublishedPort":8080,"Protocol":"tcp"}]}]
```
The JSON output allows you to use the information in other tools for further
processing, for example, using the [`jq` utility](https://stedolan.github.io/jq/){:target="_blank" rel="noopener" class="_"}
to pretty-print the JSON:
```console
$ docker compose ps --format json | jq .
[
{
"ID": "1553b0236cf4d2715845f053a4ee97042c4f9a2ef655731ee34f1f7940eaa41a",
"Name": "example-bar-1",
"Command": "/docker-entrypoint.sh nginx -g 'daemon off;'",
"Project": "example",
"Service": "bar",
"State": "exited",
"Health": "",
"ExitCode": 0,
"Publishers": null
},
{
"ID": "f02a4efaabb67416e1ff127d51c4b5578634a0ad5743bd65225ff7d1909a3fa0",
"Name": "example-foo-1",
"Command": "/docker-entrypoint.sh nginx -g 'daemon off;'",
"Project": "example",
"Service": "foo",
"State": "running",
"Health": "",
"ExitCode": 0,
"Publishers": [
{
"URL": "0.0.0.0",
"TargetPort": 80,
"PublishedPort": 8080,
"Protocol": "tcp"
}
]
}
]
```
### Filter containers by status (--status) {#status}
Use the `--status` flag to filter the list of containers by status. For example,
to show only containers that are running, or only containers that have exited:
```console
$ docker compose ps --status=running
NAME COMMAND SERVICE STATUS PORTS
example-foo-1 "/docker-entrypoint.…" foo running 0.0.0.0:8080->80/tcp
$ docker compose ps --status=exited
NAME COMMAND SERVICE STATUS PORTS
example-bar-1 "/docker-entrypoint.…" bar exited (0)
```
### Filter containers by status (--filter) {#filter}
The [`--status` flag](#status) is a convenience shorthand for the `--filter status=<status>`
flag. The example below is the equivalent to the example from the previous section,
this time using the `--filter` flag:
```console
$ docker compose ps --filter status=running
NAME COMMAND SERVICE STATUS PORTS
example-foo-1 "/docker-entrypoint.…" foo running 0.0.0.0:8080->80/tcp
$ docker compose ps --filter status=running
NAME COMMAND SERVICE STATUS PORTS
example-bar-1 "/docker-entrypoint.…" bar exited (0)
```
The `docker compose ps` command currently only supports the `--filter status=<status>`
option, but additional filter options may be added in future.
deprecated: false
experimental: false
experimentalcli: false

View File

@@ -12,6 +12,7 @@ options:
default_value: "false"
description: Pull what it can and ignores images with pull failures
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -21,6 +22,7 @@ options:
default_value: "false"
description: Also pull services declared as dependencies
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -30,6 +32,7 @@ options:
default_value: "true"
description: DEPRECATED disable parallel pulling.
deprecated: false
hidden: true
experimental: false
experimentalcli: false
kubernetes: false
@@ -39,6 +42,7 @@ options:
default_value: "true"
description: DEPRECATED pull multiple images in parallel.
deprecated: false
hidden: true
experimental: false
experimentalcli: false
kubernetes: false
@@ -49,10 +53,52 @@ options:
default_value: "false"
description: Pull without printing progress information
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
examples: |-
suppose you have this `compose.yaml`:
```yaml
services:
db:
image: postgres
web:
build: .
command: bundle exec rails s -p 3000 -b '0.0.0.0'
volumes:
- .:/myapp
ports:
- "3000:3000"
depends_on:
- db
```
If you run `docker compose pull ServiceName` in the same directory as the `compose.yaml` file that defines the service,
Docker pulls the associated image. For example, to call the postgres image configured as the db service in our example,
you would run `docker compose pull db`.
```console
$ docker compose pull db
[+] Running 1/15
⠸ db Pulling 12.4s
⠿ 45b42c59be33 Already exists 0.0s
⠹ 40adec129f1a Downloading 3.374MB/4.178MB 9.3s
⠹ b4c431d00c78 Download complete 9.3s
⠹ 2696974e2815 Download complete 9.3s
⠹ 564b77596399 Downloading 5.622MB/7.965MB 9.3s
⠹ 5044045cf6f2 Downloading 216.7kB/391.1kB 9.3s
⠹ d736e67e6ac3 Waiting 9.3s
⠹ 390c1c9a5ae4 Waiting 9.3s
⠹ c0e62f172284 Waiting 9.3s
⠹ ebcdc659c5bf Waiting 9.3s
⠹ 29be22cb3acc Waiting 9.3s
⠹ f63c47038e66 Waiting 9.3s
⠹ 77a0c198cde5 Waiting 9.3s
⠹ c8752d5b785c Waiting 9.3s
``̀`
deprecated: false
experimental: false
experimentalcli: false

View File

@@ -28,6 +28,7 @@ options:
default_value: "false"
description: Push what it can and ignores images with push failures
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false

View File

@@ -1,6 +1,16 @@
command: docker compose restart
short: Restart containers
long: Restart containers
long: |-
Restarts all stopped and running services.
If you make changes to your `compose.yml` configuration, these changes are not reflected
after running this command. For example, changes to environment variables (which are added
after a container is built, but before the container's command is executed) are not updated
after restarting.
If you are looking to configure a service's restart policy, please refer to
[restart](https://github.com/compose-spec/compose-spec/blob/master/spec.md#restart)
or [restart_policy](https://github.com/compose-spec/compose-spec/blob/master/deploy.md#restart_policy).
usage: docker compose restart
pname: docker compose
plink: docker_compose.yaml
@@ -11,6 +21,7 @@ options:
default_value: "10"
description: Specify a shutdown timeout in seconds
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false

View File

@@ -26,6 +26,7 @@ options:
default_value: "false"
description: Deprecated - no effect
deprecated: false
hidden: true
experimental: false
experimentalcli: false
kubernetes: false
@@ -36,6 +37,7 @@ options:
default_value: "false"
description: Don't ask to confirm removal
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -46,6 +48,7 @@ options:
default_value: "false"
description: Stop the containers, if required, before removing
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -56,6 +59,7 @@ options:
default_value: "false"
description: Remove any anonymous volumes attached to containers
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false

View File

@@ -65,6 +65,7 @@ options:
default_value: "false"
description: Run container in background and print container ID
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -73,6 +74,7 @@ options:
value_type: string
description: Override the entrypoint of the image
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -83,16 +85,29 @@ options:
default_value: '[]'
description: Set environment variables
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: labels
- option: interactive
shorthand: i
value_type: bool
default_value: "true"
description: Keep STDIN open even if not attached.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: label
shorthand: l
value_type: stringArray
default_value: '[]'
description: Add or override a label
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -101,6 +116,7 @@ options:
value_type: string
description: Assign a name to the container
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -108,10 +124,10 @@ options:
- option: no-TTY
shorthand: T
value_type: bool
default_value: "false"
description: |
Disable pseudo-noTty allocation. By default docker compose run allocates a TTY
default_value: "true"
description: 'Disable pseudo-TTY allocation (default: auto-detected).'
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -121,6 +137,7 @@ options:
default_value: "false"
description: Don't start linked services.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -131,6 +148,17 @@ options:
default_value: '[]'
description: Publish a container's port(s) to the host.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: quiet-pull
value_type: bool
default_value: "false"
description: Pull without printing progress information.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -140,6 +168,7 @@ options:
default_value: "false"
description: Automatically remove the container when it exits
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -150,6 +179,18 @@ options:
description: |
Run command with the service's ports enabled and mapped to the host.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: tty
shorthand: t
value_type: bool
default_value: "true"
description: Allocate a pseudo-TTY.
deprecated: false
hidden: true
experimental: false
experimentalcli: false
kubernetes: false
@@ -160,6 +201,7 @@ options:
description: |
Use the service's network useAliases in the network(s) the container connects to.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -169,6 +211,7 @@ options:
value_type: string
description: Run as specified username or uid
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -179,6 +222,7 @@ options:
default_value: '[]'
description: Bind mount a volume.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -188,6 +232,7 @@ options:
value_type: string
description: Working directory inside the container
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false

View File

@@ -12,6 +12,7 @@ options:
default_value: "10"
description: Specify a shutdown timeout in seconds
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false

View File

@@ -1,5 +1,5 @@
command: docker compose unpause
short: unpause services
short: Unpause services
long: Unpauses paused containers of a service.
usage: docker compose unpause [SERVICE...]
pname: docker compose

View File

@@ -5,7 +5,7 @@ long: |-
Unless they are already running, this command also starts any linked services.
The `docker compose up` command aggregates the output of each container (liked `docker compose logs --follow` does).
The `docker compose up` command aggregates the output of each container (like `docker compose logs --follow` does).
When the command exits, all containers are stopped. Running `docker compose up --detach` starts the containers in the
background and leaves them running.
@@ -27,6 +27,7 @@ options:
description: |
Stops all containers if any container was stopped. Incompatible with -d
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -36,6 +37,7 @@ options:
default_value: "false"
description: Recreate dependent containers. Incompatible with --no-recreate.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -45,6 +47,7 @@ options:
default_value: '[]'
description: Attach to service output.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -54,6 +57,7 @@ options:
default_value: "false"
description: Attach to dependent containers.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -63,6 +67,7 @@ options:
default_value: "false"
description: Build images before starting containers.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -73,16 +78,7 @@ options:
default_value: "false"
description: 'Detached mode: Run containers in the background'
deprecated: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: environment
shorthand: e
value_type: stringArray
default_value: '[]'
description: Environment variables
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -92,6 +88,7 @@ options:
description: |
Return the exit code of the selected service container. Implies --abort-on-container-exit
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -102,6 +99,7 @@ options:
description: |
Recreate containers even if their configuration and image haven't changed.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -111,6 +109,7 @@ options:
default_value: "false"
description: Don't build an image, even if it's missing.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -120,6 +119,7 @@ options:
default_value: "false"
description: Produce monochrome output.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -129,6 +129,7 @@ options:
default_value: "false"
description: Don't start linked services.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -138,6 +139,7 @@ options:
default_value: "false"
description: Don't print prefix in logs.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -148,6 +150,7 @@ options:
description: |
If containers already exist, don't recreate them. Incompatible with --force-recreate.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -157,6 +160,7 @@ options:
default_value: "false"
description: Don't start the services after creating them.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -166,6 +170,7 @@ options:
default_value: "false"
description: Pull without printing progress information.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -175,6 +180,7 @@ options:
default_value: "false"
description: Remove containers for services not defined in the Compose file.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -186,6 +192,7 @@ options:
description: |
Recreate anonymous volumes instead of retrieving data from the previous containers.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -196,6 +203,7 @@ options:
description: |
Scale SERVICE to NUM instances. Overrides the `scale` setting in the Compose file if present.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -207,6 +215,17 @@ options:
description: |
Use this timeout in seconds for container shutdown when attached or when containers are already running.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: wait
value_type: bool
default_value: "false"
description: Wait for services to be running|healthy. Implies detached mode.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false

View File

@@ -10,6 +10,7 @@ options:
value_type: string
description: 'Format the output. Values: [pretty | json]. (Default: pretty)'
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
@@ -19,6 +20,7 @@ options:
default_value: "false"
description: Shows only Compose's version number.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false

View File

@@ -22,17 +22,33 @@ import (
"path/filepath"
clidocstool "github.com/docker/cli-docs-tool"
"github.com/docker/cli/cli/command"
"github.com/docker/compose/v2/cmd/compose"
"github.com/spf13/cobra"
)
func generateCliYaml(opts *options) error {
cmd := &cobra.Command{Use: "docker"}
cmd.AddCommand(compose.RootCommand(nil))
func generateDocs(opts *options) error {
dockerCLI, err := command.NewDockerCli()
if err != nil {
return err
}
cmd := &cobra.Command{
Use: "docker",
DisableAutoGenTag: true,
}
cmd.AddCommand(compose.RootCommand(dockerCLI, nil))
disableFlagsInUseLine(cmd)
cmd.DisableAutoGenTag = true
return clidocstool.GenYamlTree(cmd, opts.target)
tool, err := clidocstool.New(clidocstool.Options{
Root: cmd,
SourceDir: opts.source,
TargetDir: opts.target,
Plugin: true,
})
if err != nil {
return err
}
return tool.GenAllTree()
}
func disableFlagsInUseLine(cmd *cobra.Command) {
@@ -60,12 +76,12 @@ type options struct {
func main() {
cwd, _ := os.Getwd()
opts := &options{
source: cwd,
source: filepath.Join(cwd, "docs", "reference"),
target: filepath.Join(cwd, "docs", "reference"),
}
fmt.Printf("Project root: %s\n", opts.source)
fmt.Printf("Generating yaml files into %s\n", opts.target)
if err := generateCliYaml(opts); err != nil {
fmt.Fprintf(os.Stderr, "Failed to generate yaml files: %s\n", err.Error())
if err := generateDocs(opts); err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Failed to generate documentation: %s\n", err.Error())
}
}

181
go.mod
View File

@@ -1,130 +1,153 @@
module github.com/docker/compose/v2
go 1.17
go 1.18
require (
github.com/AlecAivazis/survey/v2 v2.2.3
github.com/buger/goterm v1.0.0
github.com/cnabio/cnab-to-oci v0.3.1-beta1
github.com/compose-spec/compose-go v1.0.8
github.com/containerd/console v1.0.2
github.com/containerd/containerd v1.5.8
github.com/AlecAivazis/survey/v2 v2.3.5
github.com/buger/goterm v1.0.4
github.com/cnabio/cnab-to-oci v0.3.3
github.com/compose-spec/compose-go v1.2.8
github.com/containerd/console v1.0.3
github.com/containerd/containerd v1.6.6
github.com/distribution/distribution/v3 v3.0.0-20210316161203-a01c71e2477e
github.com/docker/buildx v0.5.2-0.20210422185057-908a856079fc
github.com/docker/cli v20.10.7+incompatible
github.com/docker/cli-docs-tool v0.1.1
github.com/docker/docker v20.10.7+incompatible
github.com/docker/buildx v0.8.2 // when updating, also update the replace rules accordingly
github.com/docker/cli v20.10.13+incompatible
github.com/docker/cli-docs-tool v0.4.0
github.com/docker/docker v20.10.13+incompatible
github.com/docker/go-connections v0.4.0
github.com/docker/go-units v0.4.0
github.com/golang/mock v1.5.0
github.com/hashicorp/go-multierror v1.1.0
github.com/hashicorp/go-version v1.3.0
github.com/mattn/go-isatty v0.0.12
github.com/golang/mock v1.6.0
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/go-version v1.5.0
github.com/mattn/go-isatty v0.0.14
github.com/mattn/go-shellwords v1.0.12
github.com/moby/buildkit v0.8.2-0.20210401015549-df49b648c8bf
github.com/moby/buildkit v0.10.1-0.20220403220257-10e6f94bf90d
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6
github.com/morikuni/aec v1.0.0
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.0.2
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799
github.com/pkg/errors v0.9.1
github.com/sanathkr/go-yaml v0.0.0-20170819195128-ed9d249f429b
github.com/sirupsen/logrus v1.8.1
github.com/spf13/cobra v1.2.1
github.com/spf13/cobra v1.4.0
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.7.0
github.com/stretchr/testify v1.7.2
github.com/theupdateframework/notary v0.7.0
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
gotest.tools v2.2.0+incompatible
gotest.tools/v3 v3.0.3
gotest.tools/v3 v3.3.0
)
require (
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Masterminds/semver v1.5.0 // indirect
github.com/Microsoft/go-winio v0.4.17 // indirect
github.com/Microsoft/hcsshim v0.8.23 // indirect
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect
github.com/Microsoft/go-winio v0.5.2 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.1 // indirect
github.com/cnabio/cnab-go v0.10.0-beta1 // indirect
github.com/compose-spec/godotenv v1.1.1 // indirect
github.com/containerd/cgroups v1.0.1 // indirect
github.com/containerd/continuity v0.1.0 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cnabio/cnab-go v0.23.1 // indirect
github.com/containerd/continuity v0.2.2 // indirect
github.com/containerd/ttrpc v1.1.0 // indirect
github.com/containerd/typeurl v1.0.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/docker/distribution v2.7.1+incompatible // indirect
github.com/docker/docker-credential-helpers v0.6.4-0.20210125172408-38bea2ce277a // indirect
github.com/docker/distribution v2.8.1+incompatible // indirect
github.com/docker/docker-credential-helpers v0.6.4 // indirect
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect
github.com/docker/go-metrics v0.0.1 // indirect
github.com/fvbommel/sortorder v1.0.1 // indirect
github.com/go-logr/logr v0.4.0 // indirect
github.com/felixge/httpsnoop v1.0.2 // indirect
github.com/fvbommel/sortorder v1.0.2 // indirect
github.com/go-logr/logr v1.2.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/gofrs/flock v0.8.0 // indirect
github.com/gogo/googleapis v1.4.0 // indirect
github.com/gogo/googleapis v1.4.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-cmp v0.5.5 // indirect
github.com/google/gofuzz v1.1.0 // indirect
github.com/google/go-cmp v0.5.7 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 // indirect
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/imdario/mergo v0.3.13 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jaguilar/vt100 v0.0.0-20150826170717-2703a27b14ea // indirect
github.com/json-iterator/go v1.1.11 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/klauspost/compress v1.11.13 // indirect
github.com/kr/pty v1.1.8 // indirect
github.com/mattn/go-colorable v0.1.6 // indirect
github.com/klauspost/compress v1.15.1 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
github.com/miekg/pkcs11 v1.0.3 // indirect
github.com/mitchellh/mapstructure v1.4.2 // indirect
github.com/miekg/pkcs11 v1.1.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/sys/mount v0.2.0 // indirect
github.com/moby/sys/mountinfo v0.4.1 // indirect
github.com/moby/sys/symlink v0.1.0 // indirect
github.com/moby/sys/signal v0.6.0 // indirect
github.com/moby/sys/symlink v0.2.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/opencontainers/runc v1.0.2 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/opencontainers/runc v1.1.2 // indirect
github.com/pelletier/go-toml v1.9.4 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.7.1 // indirect
github.com/prometheus/client_golang v1.12.1 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.10.0 // indirect
github.com/prometheus/procfs v0.6.0 // indirect
github.com/qri-io/jsonpointer v0.1.0 // indirect
github.com/qri-io/jsonschema v0.1.1 // indirect
github.com/sergi/go-diff v1.1.0 // indirect
github.com/theupdateframework/notary v0.6.1 // indirect
github.com/tonistiigi/fsutil v0.0.0-20201103201449-0834f99b7b85 // indirect
github.com/prometheus/common v0.32.1 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/qri-io/jsonpointer v0.1.1 // indirect
github.com/qri-io/jsonschema v0.2.2-0.20210831022256-780655b2ba0e // indirect
github.com/tonistiigi/fsutil v0.0.0-20220315205639-9ed612626da3 // indirect
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
github.com/tonistiigi/vt100 v0.0.0-20210615222946-8066bb97264f // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
go.opencensus.io v0.23.0 // indirect
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602 // indirect
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 // indirect
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect
golang.org/x/text v0.3.5 // indirect
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.29.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.29.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.29.0 // indirect
go.opentelemetry.io/otel v1.4.1 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.4.1 // indirect
go.opentelemetry.io/otel/internal/metric v0.27.0 // indirect
go.opentelemetry.io/otel/metric v0.27.0 // indirect
go.opentelemetry.io/otel/sdk v1.4.1 // indirect
go.opentelemetry.io/otel/trace v1.4.1 // indirect
go.opentelemetry.io/proto/otlp v0.12.0 // indirect
golang.org/x/crypto v0.0.0-20211202192323-5770296d904e // indirect
golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect
google.golang.org/grpc v1.38.0 // indirect
google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106 // indirect
google.golang.org/grpc v1.45.0 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
k8s.io/apimachinery v0.21.0 // indirect
k8s.io/client-go v0.21.0 // indirect
k8s.io/klog/v2 v2.8.0 // indirect
k8s.io/utils v0.0.0-20201110183641-67b214c5f920 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.1.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apimachinery v0.23.4 // indirect; see replace for the actual version used
k8s.io/client-go v0.23.4 // indirect; see replace for the actual version used
k8s.io/klog/v2 v2.30.0 // indirect
k8s.io/utils v0.0.0-20211116205334-6203023598ed // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect
sigs.k8s.io/yaml v1.2.0 // indirect
)
// (for buildx)
replace github.com/jaguilar/vt100 => github.com/tonistiigi/vt100 v0.0.0-20190402012908-ad4c4a574305
require (
github.com/cyberphone/json-canonicalization v0.0.0-20210303052042-6bc126869bf4 // indirect
github.com/zmap/zcrypto v0.0.0-20220605182715-4dfcec6e9a8c // indirect
github.com/zmap/zlint v1.1.0 // indirect
)
replace (
github.com/docker/cli => github.com/docker/cli v20.10.3-0.20220309205733-2b52f62e9627+incompatible
github.com/docker/docker => github.com/docker/docker v20.10.3-0.20220309172631-83b51522df43+incompatible
github.com/opencontainers/runc => github.com/opencontainers/runc v1.1.2 // Can be removed on next bump of containerd to > 1.6.4
// For k8s dependencies, we use a replace directive, to prevent them being
// upgraded to the version specified in containerd, which is not relevant to the
// version needed.
// See https://github.com/docker/buildx/pull/948 for details.
// https://github.com/docker/buildx/blob/v0.8.1/go.mod#L62-L64
k8s.io/api => k8s.io/api v0.22.4
k8s.io/apimachinery => k8s.io/apimachinery v0.22.4
k8s.io/client-go => k8s.io/client-go v0.22.4
)

1078
go.sum

File diff suppressed because it is too large Load Diff

View File

@@ -19,7 +19,6 @@ package api
import (
"context"
"fmt"
"io"
"strings"
"time"
@@ -33,15 +32,15 @@ type Service interface {
// Push executes the equivalent ot a `compose push`
Push(ctx context.Context, project *types.Project, options PushOptions) error
// Pull executes the equivalent of a `compose pull`
Pull(ctx context.Context, project *types.Project, opts PullOptions) error
Pull(ctx context.Context, project *types.Project, options PullOptions) error
// Create executes the equivalent to a `compose create`
Create(ctx context.Context, project *types.Project, opts CreateOptions) error
Create(ctx context.Context, project *types.Project, options CreateOptions) error
// Start executes the equivalent to a `compose start`
Start(ctx context.Context, project *types.Project, options StartOptions) error
Start(ctx context.Context, projectName string, options StartOptions) error
// Restart restarts containers
Restart(ctx context.Context, project *types.Project, options RestartOptions) error
Restart(ctx context.Context, projectName string, options RestartOptions) error
// Stop executes the equivalent to a `compose stop`
Stop(ctx context.Context, project *types.Project, options StopOptions) error
Stop(ctx context.Context, projectName string, options StopOptions) error
// Up executes the equivalent to a `compose up`
Up(ctx context.Context, project *types.Project, options UpOptions) error
// Down executes the equivalent to a `compose down`
@@ -55,25 +54,25 @@ type Service interface {
// Convert translate compose model into backend's native format
Convert(ctx context.Context, project *types.Project, options ConvertOptions) ([]byte, error)
// Kill executes the equivalent to a `compose kill`
Kill(ctx context.Context, project *types.Project, options KillOptions) error
Kill(ctx context.Context, projectName string, options KillOptions) error
// RunOneOffContainer creates a service oneoff container and starts its dependencies
RunOneOffContainer(ctx context.Context, project *types.Project, opts RunOptions) (int, error)
// Remove executes the equivalent to a `compose rm`
Remove(ctx context.Context, project *types.Project, options RemoveOptions) error
Remove(ctx context.Context, projectName string, options RemoveOptions) error
// Exec executes a command in a running service container
Exec(ctx context.Context, project string, opts RunOptions) (int, error)
Exec(ctx context.Context, projectName string, options RunOptions) (int, error)
// Copy copies a file/folder between a service container and the local filesystem
Copy(ctx context.Context, project *types.Project, opts CopyOptions) error
Copy(ctx context.Context, projectName string, options CopyOptions) error
// Pause executes the equivalent to a `compose pause`
Pause(ctx context.Context, project string, options PauseOptions) error
Pause(ctx context.Context, projectName string, options PauseOptions) error
// UnPause executes the equivalent to a `compose unpause`
UnPause(ctx context.Context, project string, options PauseOptions) error
UnPause(ctx context.Context, projectName string, options PauseOptions) error
// Top executes the equivalent to a `compose top`
Top(ctx context.Context, projectName string, services []string) ([]ContainerProcSummary, error)
// Events executes the equivalent to a `compose events`
Events(ctx context.Context, project string, options EventsOptions) error
Events(ctx context.Context, projectName string, options EventsOptions) error
// Port executes the equivalent to a `compose port`
Port(ctx context.Context, project string, service string, port int, options PortOptions) (string, int, error)
Port(ctx context.Context, projectName string, service string, port int, options PortOptions) (string, int, error)
// Images executes the equivalent of a `compose images`
Images(ctx context.Context, projectName string, options ImagesOptions) ([]ImageSummary, error)
}
@@ -92,6 +91,8 @@ type BuildOptions struct {
Quiet bool
// Services passed in the command line to be built
Services []string
// Ssh authentications passed in the command line
SSHs []types.SSHKey
}
// CreateOptions group options of the Create API
@@ -116,6 +117,8 @@ type CreateOptions struct {
// StartOptions group options of the Start API
type StartOptions struct {
// Project is the compose project used to define this app. Might be nil if user ran `start` just with project name
Project *types.Project
// Attach to container and forward logs if not nil
Attach LogConsumer
// AttachTo set the services to attach to
@@ -216,10 +219,8 @@ type RunOptions struct {
Entrypoint []string
Detach bool
AutoRemove bool
Stdin io.ReadCloser
Stdout io.WriteCloser
Stderr io.WriteCloser
Tty bool
Interactive bool
WorkingDir string
User string
Environment []string
@@ -404,10 +405,11 @@ const (
// Stack holds the name and state of a compose application/stack
type Stack struct {
ID string
Name string
Status string
Reason string
ID string
Name string
Status string
ConfigFiles string
Reason string
}
// LogConsumer is a callback to process log messages from services
@@ -436,6 +438,8 @@ const (
ContainerEventLog = iota
// ContainerEventAttach is a ContainerEvent of type attach. First event sent about a container
ContainerEventAttach
// ContainerEventStopped is a ContainerEvent of type stopped.
ContainerEventStopped
// ContainerEventExit is a ContainerEvent of type exit. ExitCode is set
ContainerEventExit
// UserCancel user cancelled compose up, we are stopping containers

View File

@@ -28,20 +28,20 @@ type ServiceProxy struct {
PushFn func(ctx context.Context, project *types.Project, options PushOptions) error
PullFn func(ctx context.Context, project *types.Project, opts PullOptions) error
CreateFn func(ctx context.Context, project *types.Project, opts CreateOptions) error
StartFn func(ctx context.Context, project *types.Project, options StartOptions) error
RestartFn func(ctx context.Context, project *types.Project, options RestartOptions) error
StopFn func(ctx context.Context, project *types.Project, options StopOptions) error
StartFn func(ctx context.Context, projectName string, options StartOptions) error
RestartFn func(ctx context.Context, projectName string, options RestartOptions) error
StopFn func(ctx context.Context, projectName string, options StopOptions) error
UpFn func(ctx context.Context, project *types.Project, options UpOptions) error
DownFn func(ctx context.Context, projectName string, options DownOptions) error
LogsFn func(ctx context.Context, projectName string, consumer LogConsumer, options LogOptions) error
PsFn func(ctx context.Context, projectName string, options PsOptions) ([]ContainerSummary, error)
ListFn func(ctx context.Context, options ListOptions) ([]Stack, error)
ConvertFn func(ctx context.Context, project *types.Project, options ConvertOptions) ([]byte, error)
KillFn func(ctx context.Context, project *types.Project, options KillOptions) error
KillFn func(ctx context.Context, project string, options KillOptions) error
RunOneOffContainerFn func(ctx context.Context, project *types.Project, opts RunOptions) (int, error)
RemoveFn func(ctx context.Context, project *types.Project, options RemoveOptions) error
RemoveFn func(ctx context.Context, project string, options RemoveOptions) error
ExecFn func(ctx context.Context, project string, opts RunOptions) (int, error)
CopyFn func(ctx context.Context, project *types.Project, opts CopyOptions) error
CopyFn func(ctx context.Context, project string, options CopyOptions) error
PauseFn func(ctx context.Context, project string, options PauseOptions) error
UnPauseFn func(ctx context.Context, project string, options PauseOptions) error
TopFn func(ctx context.Context, projectName string, services []string) ([]ContainerProcSummary, error)
@@ -141,36 +141,27 @@ func (s *ServiceProxy) Create(ctx context.Context, project *types.Project, optio
}
// Start implements Service interface
func (s *ServiceProxy) Start(ctx context.Context, project *types.Project, options StartOptions) error {
func (s *ServiceProxy) Start(ctx context.Context, projectName string, options StartOptions) error {
if s.StartFn == nil {
return ErrNotImplemented
}
for _, i := range s.interceptors {
i(ctx, project)
}
return s.StartFn(ctx, project, options)
return s.StartFn(ctx, projectName, options)
}
// Restart implements Service interface
func (s *ServiceProxy) Restart(ctx context.Context, project *types.Project, options RestartOptions) error {
func (s *ServiceProxy) Restart(ctx context.Context, projectName string, options RestartOptions) error {
if s.RestartFn == nil {
return ErrNotImplemented
}
for _, i := range s.interceptors {
i(ctx, project)
}
return s.RestartFn(ctx, project, options)
return s.RestartFn(ctx, projectName, options)
}
// Stop implements Service interface
func (s *ServiceProxy) Stop(ctx context.Context, project *types.Project, options StopOptions) error {
func (s *ServiceProxy) Stop(ctx context.Context, projectName string, options StopOptions) error {
if s.StopFn == nil {
return ErrNotImplemented
}
for _, i := range s.interceptors {
i(ctx, project)
}
return s.StopFn(ctx, project, options)
return s.StopFn(ctx, projectName, options)
}
// Up implements Service interface
@@ -228,14 +219,11 @@ func (s *ServiceProxy) Convert(ctx context.Context, project *types.Project, opti
}
// Kill implements Service interface
func (s *ServiceProxy) Kill(ctx context.Context, project *types.Project, options KillOptions) error {
func (s *ServiceProxy) Kill(ctx context.Context, projectName string, options KillOptions) error {
if s.KillFn == nil {
return ErrNotImplemented
}
for _, i := range s.interceptors {
i(ctx, project)
}
return s.KillFn(ctx, project, options)
return s.KillFn(ctx, projectName, options)
}
// RunOneOffContainer implements Service interface
@@ -250,49 +238,43 @@ func (s *ServiceProxy) RunOneOffContainer(ctx context.Context, project *types.Pr
}
// Remove implements Service interface
func (s *ServiceProxy) Remove(ctx context.Context, project *types.Project, options RemoveOptions) error {
func (s *ServiceProxy) Remove(ctx context.Context, projectName string, options RemoveOptions) error {
if s.RemoveFn == nil {
return ErrNotImplemented
}
for _, i := range s.interceptors {
i(ctx, project)
}
return s.RemoveFn(ctx, project, options)
return s.RemoveFn(ctx, projectName, options)
}
// Exec implements Service interface
func (s *ServiceProxy) Exec(ctx context.Context, project string, options RunOptions) (int, error) {
func (s *ServiceProxy) Exec(ctx context.Context, projectName string, options RunOptions) (int, error) {
if s.ExecFn == nil {
return 0, ErrNotImplemented
}
return s.ExecFn(ctx, project, options)
return s.ExecFn(ctx, projectName, options)
}
// Copy implements Service interface
func (s *ServiceProxy) Copy(ctx context.Context, project *types.Project, options CopyOptions) error {
func (s *ServiceProxy) Copy(ctx context.Context, projectName string, options CopyOptions) error {
if s.CopyFn == nil {
return ErrNotImplemented
}
for _, i := range s.interceptors {
i(ctx, project)
}
return s.CopyFn(ctx, project, options)
return s.CopyFn(ctx, projectName, options)
}
// Pause implements Service interface
func (s *ServiceProxy) Pause(ctx context.Context, project string, options PauseOptions) error {
func (s *ServiceProxy) Pause(ctx context.Context, projectName string, options PauseOptions) error {
if s.PauseFn == nil {
return ErrNotImplemented
}
return s.PauseFn(ctx, project, options)
return s.PauseFn(ctx, projectName, options)
}
// UnPause implements Service interface
func (s *ServiceProxy) UnPause(ctx context.Context, project string, options PauseOptions) error {
func (s *ServiceProxy) UnPause(ctx context.Context, projectName string, options PauseOptions) error {
if s.UnPauseFn == nil {
return ErrNotImplemented
}
return s.UnPauseFn(ctx, project, options)
return s.UnPauseFn(ctx, projectName, options)
}
// Top implements Service interface
@@ -304,19 +286,19 @@ func (s *ServiceProxy) Top(ctx context.Context, project string, services []strin
}
// Events implements Service interface
func (s *ServiceProxy) Events(ctx context.Context, project string, options EventsOptions) error {
func (s *ServiceProxy) Events(ctx context.Context, projectName string, options EventsOptions) error {
if s.EventsFn == nil {
return ErrNotImplemented
}
return s.EventsFn(ctx, project, options)
return s.EventsFn(ctx, projectName, options)
}
// Port implements Service interface
func (s *ServiceProxy) Port(ctx context.Context, project string, service string, port int, options PortOptions) (string, int, error) {
func (s *ServiceProxy) Port(ctx context.Context, projectName string, service string, port int, options PortOptions) (string, int, error) {
if s.PortFn == nil {
return "", 0, ErrNotImplemented
}
return s.PortFn(ctx, project, service, port, options)
return s.PortFn(ctx, projectName, service, port, options)
}
// Images implements Service interface

View File

@@ -48,7 +48,7 @@ func (s *composeService) attach(ctx context.Context, project *types.Project, lis
fmt.Printf("Attaching to %s\n", strings.Join(names, ", "))
for _, container := range containers {
err := s.attachContainer(ctx, container, listener, project)
err := s.attachContainer(ctx, container, listener)
if err != nil {
return nil, err
}
@@ -56,13 +56,9 @@ func (s *composeService) attach(ctx context.Context, project *types.Project, lis
return containers, err
}
func (s *composeService) attachContainer(ctx context.Context, container moby.Container, listener api.ContainerEventListener, project *types.Project) error {
func (s *composeService) attachContainer(ctx context.Context, container moby.Container, listener api.ContainerEventListener) error {
serviceName := container.Labels[api.ServiceLabel]
containerName := getContainerNameWithoutProject(container)
service, err := project.GetService(serviceName)
if err != nil {
return err
}
listener(api.ContainerEvent{
Type: api.ContainerEventAttach,
@@ -78,7 +74,13 @@ func (s *composeService) attachContainer(ctx context.Context, container moby.Con
Line: line,
})
})
_, _, err = s.attachContainerStreams(ctx, container.ID, service.Tty, nil, w, w)
inspect, err := s.dockerCli.Client().ContainerInspect(ctx, container.ID)
if err != nil {
return err
}
_, _, err = s.attachContainerStreams(ctx, container.ID, inspect.Config.Tty, nil, w, w)
return err
}
@@ -137,7 +139,7 @@ func (s *composeService) attachContainerStreams(ctx context.Context, container s
func (s *composeService) getContainerStreams(ctx context.Context, container string) (io.WriteCloser, io.ReadCloser, error) {
var stdout io.ReadCloser
var stdin io.WriteCloser
cnx, err := s.apiClient.ContainerAttach(ctx, container, moby.ContainerAttachOptions{
cnx, err := s.apiClient().ContainerAttach(ctx, container, moby.ContainerAttachOptions{
Stream: true,
Stdin: true,
Stdout: true,
@@ -151,7 +153,7 @@ func (s *composeService) getContainerStreams(ctx context.Context, container stri
}
// Fallback to logs API
logs, err := s.apiClient.ContainerLogs(ctx, container, moby.ContainerLogsOptions{
logs, err := s.apiClient().ContainerLogs(ctx, container, moby.ContainerLogsOptions{
ShowStdout: true,
ShowStderr: true,
Follow: true,

View File

@@ -19,7 +19,6 @@ package compose
import (
"context"
"fmt"
"os"
"path/filepath"
"github.com/compose-spec/compose-go/types"
@@ -28,11 +27,12 @@ import (
_ "github.com/docker/buildx/driver/docker" // required to get default driver registered
"github.com/docker/buildx/util/buildflags"
xprogress "github.com/docker/buildx/util/progress"
"github.com/docker/cli/cli/command"
"github.com/docker/docker/pkg/urlutil"
bclient "github.com/moby/buildkit/client"
"github.com/moby/buildkit/session"
"github.com/moby/buildkit/session/auth/authprovider"
"github.com/moby/buildkit/session/secrets/secretsprovider"
"github.com/moby/buildkit/session/sshforward/sshprovider"
specs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/docker/compose/v2/pkg/api"
@@ -64,7 +64,7 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
if service.Build != nil {
imageName := getImageName(service, project.Name)
imagesToBuild = append(imagesToBuild, imageName)
buildOptions, err := s.toBuildOptions(project, service, imageName)
buildOptions, err := s.toBuildOptions(project, service, imageName, options.SSHs)
if err != nil {
return err
}
@@ -82,7 +82,6 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
Attrs: map[string]string{"ref": image},
})
}
opts[imageName] = buildOptions
}
}
@@ -141,7 +140,7 @@ func (s *composeService) ensureImagesExists(ctx context.Context, project *types.
if project.Services[i].Labels == nil {
project.Services[i].Labels = types.Labels{}
}
project.Services[i].Labels[api.ImageDigestLabel] = digest
project.Services[i].CustomLabels[api.ImageDigestLabel] = digest
project.Services[i].Image = image
}
}
@@ -161,7 +160,7 @@ func (s *composeService) getBuildOptions(project *types.Project, images map[stri
if localImagePresent && service.PullPolicy != types.PullPolicyBuild {
continue
}
opt, err := s.toBuildOptions(project, service, imageName)
opt, err := s.toBuildOptions(project, service, imageName, []types.SSHKey{})
if err != nil {
return nil, err
}
@@ -189,37 +188,29 @@ func (s *composeService) getLocalImagesDigests(ctx context.Context, project *typ
for name, info := range imgs {
images[name] = info.ID
}
return images, nil
}
func (s *composeService) serverInfo(ctx context.Context) (command.ServerInfo, error) {
ping, err := s.apiClient.Ping(ctx)
if err != nil {
return command.ServerInfo{}, err
for _, s := range project.Services {
imgName := getImageName(s, project.Name)
digest, ok := images[imgName]
if ok {
s.CustomLabels[api.ImageDigestLabel] = digest
}
}
serverInfo := command.ServerInfo{
HasExperimental: ping.Experimental,
OSType: ping.OSType,
BuildkitVersion: ping.BuilderVersion,
}
return serverInfo, err
return images, nil
}
func (s *composeService) doBuild(ctx context.Context, project *types.Project, opts map[string]build.Options, mode string) (map[string]string, error) {
if len(opts) == 0 {
return nil, nil
}
serverInfo, err := s.serverInfo(ctx)
if err != nil {
return nil, err
}
if buildkitEnabled, err := command.BuildKitEnabled(serverInfo); err != nil || !buildkitEnabled {
if buildkitEnabled, err := s.dockerCli.BuildKitEnabled(); err != nil || !buildkitEnabled {
return s.doBuildClassic(ctx, opts)
}
return s.doBuildBuildkit(ctx, project, opts, mode)
}
func (s *composeService) toBuildOptions(project *types.Project, service types.ServiceConfig, imageTag string) (build.Options, error) {
func (s *composeService) toBuildOptions(project *types.Project, service types.ServiceConfig, imageTag string, sshKeys []types.SSHKey) (build.Options, error) {
var tags []string
tags = append(tags, imageTag)
@@ -244,11 +235,59 @@ func (s *composeService) toBuildOptions(project *types.Project, service types.Se
plats = append(plats, p)
}
cacheFrom, err := buildflags.ParseCacheEntry(service.Build.CacheFrom)
if err != nil {
return build.Options{}, err
}
cacheTo, err := buildflags.ParseCacheEntry(service.Build.CacheTo)
if err != nil {
return build.Options{}, err
}
sessionConfig := []session.Attachable{
authprovider.NewDockerAuthProvider(s.stderr()),
}
if len(sshKeys) > 0 || len(service.Build.SSH) > 0 {
sshAgentProvider, err := sshAgentProvider(append(service.Build.SSH, sshKeys...))
if err != nil {
return build.Options{}, err
}
sessionConfig = append(sessionConfig, sshAgentProvider)
}
if len(service.Build.Secrets) > 0 {
var sources []secretsprovider.Source
for _, secret := range service.Build.Secrets {
config := project.Secrets[secret.Source]
if config.File == "" {
return build.Options{}, fmt.Errorf("build.secrets only supports file-based secrets: %q", secret.Source)
}
sources = append(sources, secretsprovider.Source{
ID: secret.Source,
FilePath: config.File,
})
}
store, err := secretsprovider.NewStore(sources)
if err != nil {
return build.Options{}, err
}
p := secretsprovider.NewSecretProvider(store)
sessionConfig = append(sessionConfig, p)
}
if len(service.Build.Tags) > 0 {
tags = append(tags, service.Build.Tags...)
}
return build.Options{
Inputs: build.Inputs{
ContextPath: service.Build.Context,
DockerfilePath: dockerFilePath(service.Build.Context, service.Build.Dockerfile),
},
CacheFrom: cacheFrom,
CacheTo: cacheTo,
NoCache: service.Build.NoCache,
Pull: service.Build.Pull,
BuildArgs: buildArgs,
Tags: tags,
Target: service.Build.Target,
@@ -256,10 +295,8 @@ func (s *composeService) toBuildOptions(project *types.Project, service types.Se
Platforms: plats,
Labels: service.Build.Labels,
NetworkMode: service.Build.Network,
ExtraHosts: service.Build.ExtraHosts,
Session: []session.Attachable{
authprovider.NewDockerAuthProvider(os.Stderr),
},
ExtraHosts: service.Build.ExtraHosts.AsList(),
Session: sessionConfig,
}, nil
}
@@ -293,3 +330,14 @@ func dockerFilePath(context string, dockerfile string) string {
}
return filepath.Join(context, dockerfile)
}
func sshAgentProvider(sshKeys types.SSHConfig) (session.Attachable, error) {
sshConfig := make([]sshprovider.AgentConfig, 0, len(sshKeys))
for _, sshKey := range sshKeys {
sshConfig = append(sshConfig, sshprovider.AgentConfig{
ID: sshKey.ID,
Paths: []string{sshKey.Path},
})
}
return sshprovider.NewSSHAgentProvider(sshConfig)
}

View File

@@ -19,6 +19,7 @@ package compose
import (
"context"
"os"
"path/filepath"
"github.com/compose-spec/compose-go/types"
"github.com/docker/buildx/build"
@@ -28,7 +29,7 @@ import (
func (s *composeService) doBuildBuildkit(ctx context.Context, project *types.Project, opts map[string]build.Options, mode string) (map[string]string, error) {
const drivername = "default"
d, err := driver.GetDriver(ctx, drivername, nil, s.apiClient, s.configFile, nil, nil, "", nil, nil, project.WorkingDir)
d, err := driver.GetDriver(ctx, drivername, nil, s.apiClient(), s.configFile(), nil, nil, nil, nil, nil, project.WorkingDir)
if err != nil {
return nil, err
}
@@ -44,10 +45,10 @@ func (s *composeService) doBuildBuildkit(ctx context.Context, project *types.Pro
// build and will lock
progressCtx, cancel := context.WithCancel(context.Background())
defer cancel()
w := xprogress.NewPrinter(progressCtx, os.Stdout, mode)
w := xprogress.NewPrinter(progressCtx, s.stdout(), os.Stdout, mode)
// We rely on buildx "docker" builder integrated in docker engine, so don't need a DockerAPI here
response, err := build.Build(ctx, driverInfo, opts, nil, nil, w)
response, err := build.Build(ctx, driverInfo, opts, nil, filepath.Dir(s.configFile().Filename), w)
errW := w.Wait()
if err == nil {
err = errW

View File

@@ -69,8 +69,8 @@ func (s *composeService) doBuildClassicSimpleImage(ctx context.Context, options
dockerfileName := options.Inputs.DockerfilePath
specifiedContext := options.Inputs.ContextPath
progBuff := os.Stdout
buildBuff := os.Stdout
progBuff := s.stdout()
buildBuff := s.stdout()
if options.ImageIDFile != "" {
// Avoid leaving a stale file if we eventually fail
if err := os.Remove(options.ImageIDFile); err != nil && !os.IsNotExist(err) {
@@ -143,19 +143,10 @@ func (s *composeService) doBuildClassicSimpleImage(ctx context.Context, options
return "", err
}
// if up to this point nothing has set the context then we must have another
// way for sending it(streaming) and set the context to the Dockerfile
if dockerfileCtx != nil && buildCtx == nil {
buildCtx = dockerfileCtx
}
progressOutput := streamformatter.NewProgressOutput(progBuff)
var body io.Reader
if buildCtx != nil {
body = progress.NewProgressReader(buildCtx, progressOutput, 0, "", "Sending build context to Docker daemon")
}
body := progress.NewProgressReader(buildCtx, progressOutput, 0, "", "Sending build context to Docker daemon")
configFile := s.configFile
configFile := s.configFile()
creds, err := configFile.GetAllCredentials()
if err != nil {
return "", err
@@ -171,7 +162,7 @@ func (s *composeService) doBuildClassicSimpleImage(ctx context.Context, options
ctx, cancel := context.WithCancel(ctx)
defer cancel()
response, err := s.apiClient.ImageBuild(ctx, body, buildOptions)
response, err := s.apiClient().ImageBuild(ctx, body, buildOptions)
if err != nil {
return "", err
}
@@ -181,13 +172,13 @@ func (s *composeService) doBuildClassicSimpleImage(ctx context.Context, options
aux := func(msg jsonmessage.JSONMessage) {
var result dockertypes.BuildResult
if err := json.Unmarshal(*msg.Aux, &result); err != nil {
fmt.Fprintf(os.Stderr, "Failed to parse aux message: %s", err)
fmt.Fprintf(s.stderr(), "Failed to parse aux message: %s", err)
} else {
imageID = result.ID
}
}
err = jsonmessage.DisplayJSONMessagesStream(response.Body, buildBuff, progBuff.Fd(), true, aux)
err = jsonmessage.DisplayJSONMessagesStream(response.Body, buildBuff, progBuff.FD(), true, aux)
if err != nil {
if jerr, ok := err.(*jsonmessage.JSONError); ok {
// If no error code is set, default to 1
@@ -203,7 +194,7 @@ func (s *composeService) doBuildClassicSimpleImage(ctx context.Context, options
// daemon isn't running Windows.
if response.OSType != "windows" && runtime.GOOS == "windows" {
// if response.OSType != "windows" && runtime.GOOS == "windows" && !options.quiet {
fmt.Fprintln(os.Stdout, "SECURITY WARNING: You are building a Docker "+
fmt.Fprintln(s.stdout(), "SECURITY WARNING: You are building a Docker "+
"image from Windows against a non-Windows Docker host. All files and "+
"directories added to build context will have '-rwxr-xr-x' permissions. "+
"It is recommended to double check and reset permissions for sensitive "+
@@ -231,6 +222,7 @@ func imageBuildOptions(options buildx.Options) dockertypes.ImageBuildOptions {
return dockertypes.ImageBuildOptions{
Tags: options.Tags,
NoCache: options.NoCache,
Remove: true,
PullParent: options.Pull,
BuildArgs: toMapStringStringPtr(options.BuildArgs),
Labels: options.Labels,

View File

@@ -21,14 +21,18 @@ import (
"context"
"encoding/json"
"fmt"
"io"
"strings"
"github.com/docker/compose/v2/pkg/api"
"github.com/compose-spec/compose-go/types"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/config/configfile"
"github.com/docker/cli/cli/streams"
"github.com/docker/compose/v2/pkg/api"
moby "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/client"
"github.com/pkg/errors"
"github.com/sanathkr/go-yaml"
)
@@ -36,19 +40,41 @@ import (
var Separator = "-"
// NewComposeService create a local implementation of the compose.Service API
func NewComposeService(apiClient client.APIClient, configFile *configfile.ConfigFile) api.Service {
func NewComposeService(dockerCli command.Cli) api.Service {
return &composeService{
apiClient: apiClient,
configFile: configFile,
dockerCli: dockerCli,
}
}
type composeService struct {
apiClient client.APIClient
configFile *configfile.ConfigFile
dockerCli command.Cli
}
func (s *composeService) apiClient() client.APIClient {
return s.dockerCli.Client()
}
func (s *composeService) configFile() *configfile.ConfigFile {
return s.dockerCli.ConfigFile()
}
func (s *composeService) stdout() *streams.Out {
return s.dockerCli.Out()
}
func (s *composeService) stdin() *streams.In {
return s.dockerCli.In()
}
func (s *composeService) stderr() io.Writer {
return s.dockerCli.Err()
}
func getCanonicalContainerName(c moby.Container) string {
if len(c.Names) == 0 {
// corner case, sometime happens on removal. return short ID as a safeguard value
return c.ID[:12]
}
// Names return container canonical name /foo + link aliases /linked_by/foo
for _, name := range c.Names {
if strings.LastIndex(name, "/") == 0 {
@@ -92,3 +118,115 @@ func escapeDollarSign(marshal []byte) []byte {
escDollar := []byte{'$', '$'}
return bytes.ReplaceAll(marshal, dollar, escDollar)
}
// projectFromName builds a types.Project based on actual resources with compose labels set
func (s *composeService) projectFromName(containers Containers, projectName string, services ...string) (*types.Project, error) {
project := &types.Project{
Name: projectName,
}
if len(containers) == 0 {
return project, errors.Wrap(api.ErrNotFound, fmt.Sprintf("no container found for project %q", projectName))
}
set := map[string]*types.ServiceConfig{}
for _, c := range containers {
serviceLabel := c.Labels[api.ServiceLabel]
_, ok := set[serviceLabel]
if !ok {
set[serviceLabel] = &types.ServiceConfig{
Name: serviceLabel,
Image: c.Image,
Labels: c.Labels,
}
}
set[serviceLabel].Scale++
}
for _, service := range set {
dependencies := service.Labels[api.DependenciesLabel]
if len(dependencies) > 0 {
service.DependsOn = types.DependsOnConfig{}
for _, dc := range strings.Split(dependencies, ",") {
dcArr := strings.Split(dc, ":")
condition := ServiceConditionRunningOrHealthy
dependency := dcArr[0]
// backward compatibility
if len(dcArr) > 1 {
condition = dcArr[1]
}
service.DependsOn[dependency] = types.ServiceDependency{Condition: condition}
}
}
project.Services = append(project.Services, *service)
}
SERVICES:
for _, qs := range services {
for _, es := range project.Services {
if es.Name == qs {
continue SERVICES
}
}
return project, errors.Wrapf(api.ErrNotFound, "no such service: %q", qs)
}
err := project.ForServices(services)
if err != nil {
return project, err
}
return project, nil
}
// actualState list resources labelled by projectName to rebuild compose project model
func (s *composeService) actualState(ctx context.Context, projectName string, services []string) (Containers, *types.Project, error) {
var containers Containers
// don't filter containers by options.Services so projectFromName can rebuild project with all existing resources
containers, err := s.getContainers(ctx, projectName, oneOffInclude, true)
if err != nil {
return nil, nil, err
}
project, err := s.projectFromName(containers, projectName, services...)
if err != nil && !api.IsNotFoundError(err) {
return nil, nil, err
}
if len(services) > 0 {
containers = containers.filter(isService(services...))
}
return containers, project, nil
}
func (s *composeService) actualVolumes(ctx context.Context, projectName string) (types.Volumes, error) {
volumes, err := s.apiClient().VolumeList(ctx, filters.NewArgs(projectFilter(projectName)))
if err != nil {
return nil, err
}
actual := types.Volumes{}
for _, vol := range volumes.Volumes {
actual[vol.Labels[api.VolumeLabel]] = types.VolumeConfig{
Name: vol.Name,
Driver: vol.Driver,
Labels: vol.Labels,
}
}
return actual, nil
}
func (s *composeService) actualNetworks(ctx context.Context, projectName string) (types.Networks, error) {
networks, err := s.apiClient().NetworkList(ctx, moby.NetworkListOptions{
Filters: filters.NewArgs(projectFilter(projectName)),
})
if err != nil {
return nil, err
}
actual := types.Networks{}
for _, net := range networks {
actual[net.Labels[api.NetworkLabel]] = types.NetworkConfig{
Name: net.Name,
Driver: net.Driver,
Labels: net.Labels,
}
}
return actual, nil
}

View File

@@ -18,13 +18,13 @@ package compose
import (
"context"
"fmt"
"sort"
moby "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/utils"
moby "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
)
// Containers is a set of moby Container
@@ -40,18 +40,8 @@ const (
func (s *composeService) getContainers(ctx context.Context, project string, oneOff oneOff, stopped bool, selectedServices ...string) (Containers, error) {
var containers Containers
f := []filters.KeyValuePair{projectFilter(project)}
if len(selectedServices) == 1 {
f = append(f, serviceFilter(selectedServices[0]))
}
switch oneOff {
case oneOffOnly:
f = append(f, oneOffFilter(true))
case oneOffExclude:
f = append(f, oneOffFilter(false))
case oneOffInclude:
}
containers, err := s.apiClient.ContainerList(ctx, moby.ContainerListOptions{
f := getDefaultFilters(project, oneOff, selectedServices...)
containers, err := s.apiClient().ContainerList(ctx, moby.ContainerListOptions{
Filters: filters.NewArgs(f...),
All: stopped,
})
@@ -64,6 +54,40 @@ func (s *composeService) getContainers(ctx context.Context, project string, oneO
return containers, nil
}
func getDefaultFilters(projectName string, oneOff oneOff, selectedServices ...string) []filters.KeyValuePair {
f := []filters.KeyValuePair{projectFilter(projectName)}
if len(selectedServices) == 1 {
f = append(f, serviceFilter(selectedServices[0]))
}
switch oneOff {
case oneOffOnly:
f = append(f, oneOffFilter(true))
case oneOffExclude:
f = append(f, oneOffFilter(false))
case oneOffInclude:
}
return f
}
func (s *composeService) getSpecifiedContainer(ctx context.Context, projectName string, oneOff oneOff, stopped bool, serviceName string, containerIndex int) (moby.Container, error) {
defaultFilters := getDefaultFilters(projectName, oneOff, serviceName)
defaultFilters = append(defaultFilters, containerNumberFilter(containerIndex))
containers, err := s.apiClient().ContainerList(ctx, moby.ContainerListOptions{
Filters: filters.NewArgs(
defaultFilters...,
),
All: stopped,
})
if err != nil {
return moby.Container{}, err
}
if len(containers) < 1 {
return moby.Container{}, fmt.Errorf("service %q is not running container #%d", serviceName, containerIndex)
}
container := containers[0]
return container, nil
}
// containerPredicate define a predicate we want container to satisfy for filtering operations
type containerPredicate func(c moby.Container) bool

View File

@@ -180,26 +180,20 @@ func (c *convergence) ensureService(ctx context.Context, project *types.Project,
// Scale Down
container := container
eg.Go(func() error {
err := c.service.apiClient.ContainerStop(ctx, container.ID, timeout)
err := c.service.apiClient().ContainerStop(ctx, container.ID, timeout)
if err != nil {
return err
}
return c.service.apiClient.ContainerRemove(ctx, container.ID, moby.ContainerRemoveOptions{})
return c.service.apiClient().ContainerRemove(ctx, container.ID, moby.ContainerRemoveOptions{})
})
continue
}
if recreate == api.RecreateNever {
continue
}
// Re-create diverged containers
configHash, err := ServiceHash(service)
mustRecreate, err := mustRecreate(service, container, recreate)
if err != nil {
return err
}
name := getContainerProgressName(container)
diverged := container.Labels[api.ConfigHashLabel] != configHash
if diverged || recreate == api.RecreateForce || service.Extensions[extLifecycle] == forceRecreate {
if mustRecreate {
i, container := i, container
eg.Go(func() error {
recreated, err := c.service.recreateContainer(ctx, project, service, container, inherit, timeout)
@@ -211,6 +205,7 @@ func (c *convergence) ensureService(ctx context.Context, project *types.Project,
// Enforce non-diverged containers are running
w := progress.ContextWriter(ctx)
name := getContainerProgressName(container)
switch container.State {
case ContainerRunning:
w.Event(progress.RunningEvent(name))
@@ -249,6 +244,22 @@ func (c *convergence) ensureService(ctx context.Context, project *types.Project,
return err
}
func mustRecreate(expected types.ServiceConfig, actual moby.Container, policy string) (bool, error) {
if policy == api.RecreateNever {
return false, nil
}
if policy == api.RecreateForce || expected.Extensions[extLifecycle] == forceRecreate {
return true, nil
}
configHash, err := ServiceHash(expected)
if err != nil {
return false, err
}
configChanged := actual.Labels[api.ConfigHashLabel] != configHash
imageUpdated := actual.Labels[api.ImageDigestLabel] != expected.CustomLabels[api.ImageDigestLabel]
return configChanged || imageUpdated, nil
}
func getContainerName(projectName string, service types.ServiceConfig, number int) string {
name := strings.Join([]string{projectName, service.Name, strconv.Itoa(number)}, Separator)
if service.ContainerName != "" {
@@ -261,12 +272,33 @@ func getContainerProgressName(container moby.Container) string {
return "Container " + getCanonicalContainerName(container)
}
func containerEvents(containers Containers, eventFunc func(string) progress.Event) []progress.Event {
events := []progress.Event{}
for _, container := range containers {
events = append(events, eventFunc(getContainerProgressName(container)))
}
return events
}
// ServiceConditionRunningOrHealthy is a service condition on statys running or healthy
const ServiceConditionRunningOrHealthy = "running_or_healthy"
func (s *composeService) waitDependencies(ctx context.Context, project *types.Project, dependencies types.DependsOnConfig) error {
eg, _ := errgroup.WithContext(ctx)
w := progress.ContextWriter(ctx)
for dep, config := range dependencies {
if shouldWait, err := shouldWaitForDependency(dep, config, project); err != nil {
return err
} else if !shouldWait {
continue
}
containers, err := s.getContainers(ctx, project.Name, oneOffExclude, false, dep)
if err != nil {
return err
}
w.Events(containerEvents(containers, progress.Waiting))
dep, config := dep, config
eg.Go(func() error {
ticker := time.NewTicker(500 * time.Millisecond)
@@ -280,6 +312,7 @@ func (s *composeService) waitDependencies(ctx context.Context, project *types.Pr
return err
}
if healthy {
w.Events(containerEvents(containers, progress.Healthy))
return nil
}
case types.ServiceConditionHealthy:
@@ -288,6 +321,7 @@ func (s *composeService) waitDependencies(ctx context.Context, project *types.Pr
return err
}
if healthy {
w.Events(containerEvents(containers, progress.Healthy))
return nil
}
case types.ServiceConditionCompletedSuccessfully:
@@ -296,14 +330,12 @@ func (s *composeService) waitDependencies(ctx context.Context, project *types.Pr
return err
}
if exited {
w.Events(containerEvents(containers, progress.Exited))
if code != 0 {
return fmt.Errorf("service %q didn't completed successfully: exit %d", dep, code)
}
return nil
}
case types.ServiceConditionStarted:
// already managed by InDependencyOrder
return nil
default:
logrus.Warnf("unsupported depends_on condition: %s", config.Condition)
return nil
@@ -314,6 +346,20 @@ func (s *composeService) waitDependencies(ctx context.Context, project *types.Pr
return eg.Wait()
}
func shouldWaitForDependency(serviceName string, dependencyConfig types.ServiceDependency, project *types.Project) (bool, error) {
if dependencyConfig.Condition == types.ServiceConditionStarted {
// already managed by InDependencyOrder
return false, nil
}
if service, err := project.GetService(serviceName); err != nil {
return false, err
} else if service.Scale == 0 {
// don't wait for the dependency which configured to have 0 containers running
return false, nil
}
return true, nil
}
func nextContainerNumber(containers []moby.Container) (int, error) {
max := 0
for _, c := range containers {
@@ -360,13 +406,13 @@ func (s *composeService) recreateContainer(ctx context.Context, project *types.P
var created moby.Container
w := progress.ContextWriter(ctx)
w.Event(progress.NewEvent(getContainerProgressName(replaced), progress.Working, "Recreate"))
err := s.apiClient.ContainerStop(ctx, replaced.ID, timeout)
err := s.apiClient().ContainerStop(ctx, replaced.ID, timeout)
if err != nil {
return created, err
}
name := getCanonicalContainerName(replaced)
tmpName := fmt.Sprintf("%s_%s", replaced.ID[:12], name)
err = s.apiClient.ContainerRename(ctx, replaced.ID, tmpName)
err = s.apiClient().ContainerRename(ctx, replaced.ID, tmpName)
if err != nil {
return created, err
}
@@ -384,7 +430,7 @@ func (s *composeService) recreateContainer(ctx context.Context, project *types.P
if err != nil {
return created, err
}
err = s.apiClient.ContainerRemove(ctx, replaced.ID, moby.ContainerRemoveOptions{})
err = s.apiClient().ContainerRemove(ctx, replaced.ID, moby.ContainerRemoveOptions{})
if err != nil {
return created, err
}
@@ -409,7 +455,7 @@ func setDependentLifecycle(project *types.Project, service string, strategy stri
func (s *composeService) startContainer(ctx context.Context, container moby.Container) error {
w := progress.ContextWriter(ctx)
w.Event(progress.NewEvent(getContainerProgressName(container), progress.Working, "Restart"))
err := s.apiClient.ContainerStart(ctx, container.ID, moby.ContainerStartOptions{})
err := s.apiClient().ContainerStart(ctx, container.ID, moby.ContainerStartOptions{})
if err != nil {
return err
}
@@ -433,11 +479,11 @@ func (s *composeService) createMobyContainer(ctx context.Context, project *types
}
plat = &p
}
response, err := s.apiClient.ContainerCreate(ctx, containerConfig, hostConfig, networkingConfig, plat, name)
response, err := s.apiClient().ContainerCreate(ctx, containerConfig, hostConfig, networkingConfig, plat, name)
if err != nil {
return created, err
}
inspectedContainer, err := s.apiClient.ContainerInspect(ctx, response.ID)
inspectedContainer, err := s.apiClient().ContainerInspect(ctx, response.ID)
if err != nil {
return created, err
}
@@ -467,7 +513,7 @@ func (s *composeService) createMobyContainer(ctx context.Context, project *types
if shortIDAliasExists(created.ID, val.Aliases...) {
continue
}
err = s.apiClient.NetworkDisconnect(ctx, netwrk.Name, created.ID, false)
err = s.apiClient().NetworkDisconnect(ctx, netwrk.Name, created.ID, false)
if err != nil {
return created, err
}
@@ -477,6 +523,8 @@ func (s *composeService) createMobyContainer(ctx context.Context, project *types
return created, err
}
}
err = s.injectSecrets(ctx, project, service, created.ID)
return created, err
}
@@ -561,7 +609,7 @@ func (s *composeService) connectContainerToNetwork(ctx context.Context, id strin
IPv6Address: ipv6Address,
}
}
err := s.apiClient.NetworkConnect(ctx, netwrk, id, &network.EndpointSettings{
err := s.apiClient().NetworkConnect(ctx, netwrk, id, &network.EndpointSettings{
Aliases: aliases,
IPAddress: ipv4Address,
GlobalIPv6Address: ipv6Address,
@@ -584,7 +632,7 @@ func (s *composeService) isServiceHealthy(ctx context.Context, project *types.Pr
return false, nil
}
for _, c := range containers {
container, err := s.apiClient.ContainerInspect(ctx, c.ID)
container, err := s.apiClient().ContainerInspect(ctx, c.ID)
if err != nil {
return false, err
}
@@ -596,8 +644,15 @@ func (s *composeService) isServiceHealthy(ctx context.Context, project *types.Pr
if container.State == nil || container.State.Health == nil {
return false, fmt.Errorf("container for service %q has no healthcheck configured", service)
}
if container.State.Health.Status != moby.Healthy {
switch container.State.Health.Status {
case moby.Healthy:
// Continue by checking the next container.
case moby.Unhealthy:
return false, fmt.Errorf("container for service %q is unhealthy", service)
case moby.Starting:
return false, nil
default:
return false, fmt.Errorf("container for service %q had unexpected health status %q", service, container.State.Health.Status)
}
}
return true, nil
@@ -609,7 +664,7 @@ func (s *composeService) isServiceCompleted(ctx context.Context, project *types.
return false, 0, err
}
for _, c := range containers {
container, err := s.apiClient.ContainerInspect(ctx, c.ID)
container, err := s.apiClient().ContainerInspect(ctx, c.ID)
if err != nil {
return false, 0, err
}
@@ -629,7 +684,7 @@ func (s *composeService) startService(ctx context.Context, project *types.Projec
if err != nil {
return err
}
containers, err := s.apiClient.ContainerList(ctx, moby.ContainerListOptions{
containers, err := s.apiClient().ContainerList(ctx, moby.ContainerListOptions{
Filters: filters.NewArgs(
projectFilter(project.Name),
serviceFilter(service.Name),
@@ -658,7 +713,7 @@ func (s *composeService) startService(ctx context.Context, project *types.Projec
eg.Go(func() error {
eventName := getContainerProgressName(container)
w.Event(progress.StartingEvent(eventName))
err := s.apiClient.ContainerStart(ctx, container.ID, moby.ContainerStartOptions{})
err := s.apiClient().ContainerStart(ctx, container.ID, moby.ContainerStartOptions{})
if err == nil {
w.Event(progress.StartedEvent(eventName))
}

View File

@@ -19,6 +19,7 @@ package compose
import (
"context"
"fmt"
"strings"
"testing"
"github.com/compose-spec/compose-go/types"
@@ -73,8 +74,11 @@ func TestServiceLinks(t *testing.T) {
t.Run("service links default", func(t *testing.T) {
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
apiClient := mocks.NewMockAPIClient(mockCtrl)
tested.apiClient = apiClient
cli := mocks.NewMockCli(mockCtrl)
tested.dockerCli = cli
cli.EXPECT().Client().Return(apiClient).AnyTimes()
s.Links = []string{"db"}
@@ -94,7 +98,9 @@ func TestServiceLinks(t *testing.T) {
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
apiClient := mocks.NewMockAPIClient(mockCtrl)
tested.apiClient = apiClient
cli := mocks.NewMockCli(mockCtrl)
tested.dockerCli = cli
cli.EXPECT().Client().Return(apiClient).AnyTimes()
s.Links = []string{"db:db"}
@@ -114,7 +120,9 @@ func TestServiceLinks(t *testing.T) {
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
apiClient := mocks.NewMockAPIClient(mockCtrl)
tested.apiClient = apiClient
cli := mocks.NewMockCli(mockCtrl)
tested.dockerCli = cli
cli.EXPECT().Client().Return(apiClient).AnyTimes()
s.Links = []string{"db:dbname"}
@@ -134,7 +142,9 @@ func TestServiceLinks(t *testing.T) {
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
apiClient := mocks.NewMockAPIClient(mockCtrl)
tested.apiClient = apiClient
cli := mocks.NewMockCli(mockCtrl)
tested.dockerCli = cli
cli.EXPECT().Client().Return(apiClient).AnyTimes()
s.Links = []string{"db:dbname"}
s.ExternalLinks = []string{"db1:db2"}
@@ -158,7 +168,9 @@ func TestServiceLinks(t *testing.T) {
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
apiClient := mocks.NewMockAPIClient(mockCtrl)
tested.apiClient = apiClient
cli := mocks.NewMockCli(mockCtrl)
tested.dockerCli = cli
cli.EXPECT().Client().Return(apiClient).AnyTimes()
s.Links = []string{}
s.ExternalLinks = []string{}
@@ -184,3 +196,34 @@ func TestServiceLinks(t *testing.T) {
assert.Equal(t, links[2], "testProject-web-1:testProject-web-1")
})
}
func TestWaitDependencies(t *testing.T) {
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
apiClient := mocks.NewMockAPIClient(mockCtrl)
cli := mocks.NewMockCli(mockCtrl)
tested.dockerCli = cli
cli.EXPECT().Client().Return(apiClient).AnyTimes()
t.Run("should skip dependencies with scale 0", func(t *testing.T) {
dbService := types.ServiceConfig{Name: "db", Scale: 0}
redisService := types.ServiceConfig{Name: "redis", Scale: 0}
project := types.Project{Name: strings.ToLower(testProject), Services: []types.ServiceConfig{dbService, redisService}}
dependencies := types.DependsOnConfig{
"db": {Condition: ServiceConditionRunningOrHealthy},
"redis": {Condition: ServiceConditionRunningOrHealthy},
}
assert.NilError(t, tested.waitDependencies(context.Background(), &project, dependencies))
})
t.Run("should skip dependencies with condition service_started", func(t *testing.T) {
dbService := types.ServiceConfig{Name: "db", Scale: 1}
redisService := types.ServiceConfig{Name: "redis", Scale: 1}
project := types.Project{Name: strings.ToLower(testProject), Services: []types.ServiceConfig{dbService, redisService}}
dependencies := types.DependsOnConfig{
"db": {Condition: types.ServiceConditionStarted},
"redis": {Condition: types.ServiceConditionStarted},
}
assert.NilError(t, tested.waitDependencies(context.Background(), &project, dependencies))
})
}

View File

@@ -26,11 +26,9 @@ import (
"golang.org/x/sync/errgroup"
"github.com/compose-spec/compose-go/types"
"github.com/docker/cli/cli/command"
"github.com/docker/compose/v2/pkg/api"
moby "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/system"
"github.com/pkg/errors"
@@ -44,62 +42,80 @@ const (
acrossServices = fromService | toService
)
func (s *composeService) Copy(ctx context.Context, project *types.Project, opts api.CopyOptions) error {
srcService, srcPath := splitCpArg(opts.Source)
destService, dstPath := splitCpArg(opts.Destination)
func (s *composeService) Copy(ctx context.Context, projectName string, options api.CopyOptions) error {
projectName = strings.ToLower(projectName)
srcService, srcPath := splitCpArg(options.Source)
destService, dstPath := splitCpArg(options.Destination)
var direction copyDirection
var serviceName string
var copyFunc func(ctx context.Context, containerID string, srcPath string, dstPath string, opts api.CopyOptions) error
if srcService != "" {
direction |= fromService
serviceName = srcService
copyFunc = s.copyFromContainer
// copying from multiple containers of a services doesn't make sense.
if opts.All {
if options.All {
return errors.New("cannot use the --all flag when copying from a service")
}
}
if destService != "" {
direction |= toService
serviceName = destService
copyFunc = s.copyToContainer
}
if direction == acrossServices {
return errors.New("copying between services is not supported")
}
f := filters.NewArgs(
projectFilter(project.Name),
serviceFilter(serviceName),
)
if !opts.All {
f.Add("label", fmt.Sprintf("%s=%d", api.ContainerNumberLabel, opts.Index))
if direction == 0 {
return errors.New("unknown copy direction")
}
containers, err := s.apiClient.ContainerList(ctx, moby.ContainerListOptions{Filters: f})
containers, err := s.listContainersTargetedForCopy(ctx, projectName, options.Index, direction, serviceName)
if err != nil {
return err
}
if len(containers) < 1 {
return fmt.Errorf("service %s not running", serviceName)
}
g := errgroup.Group{}
for _, container := range containers {
containerID := container.ID
g.Go(func() error {
switch direction {
case fromService:
return s.copyFromContainer(ctx, containerID, srcPath, dstPath, opts)
case toService:
return s.copyToContainer(ctx, containerID, srcPath, dstPath, opts)
case acrossServices:
return errors.New("copying between services is not supported")
default:
return errors.New("unknown copy direction")
}
return copyFunc(ctx, containerID, srcPath, dstPath, options)
})
}
return g.Wait()
}
func (s *composeService) listContainersTargetedForCopy(ctx context.Context, projectName string, index int, direction copyDirection, serviceName string) (Containers, error) {
var containers Containers
var err error
switch {
case index > 0:
container, err := s.getSpecifiedContainer(ctx, projectName, oneOffExclude, true, serviceName, index)
if err != nil {
return nil, err
}
return append(containers, container), nil
default:
containers, err = s.getContainers(ctx, projectName, oneOffExclude, true, serviceName)
if err != nil {
return nil, err
}
if len(containers) < 1 {
return nil, fmt.Errorf("no container found for service %q", serviceName)
}
if direction == fromService {
return containers[:1], err
}
return containers, err
}
}
func (s *composeService) copyToContainer(ctx context.Context, containerID string, srcPath string, dstPath string, opts api.CopyOptions) error {
var err error
if srcPath != "-" {
@@ -112,7 +128,7 @@ func (s *composeService) copyToContainer(ctx context.Context, containerID string
// Prepare destination copy info by stat-ing the container path.
dstInfo := archive.CopyInfo{Path: dstPath}
dstStat, err := s.apiClient.ContainerStatPath(ctx, containerID, dstPath)
dstStat, err := s.apiClient().ContainerStatPath(ctx, containerID, dstPath)
// If the destination is a symbolic link, we should evaluate it.
if err == nil && dstStat.Mode&os.ModeSymlink != 0 {
@@ -124,7 +140,7 @@ func (s *composeService) copyToContainer(ctx context.Context, containerID string
}
dstInfo.Path = linkTarget
dstStat, err = s.apiClient.ContainerStatPath(ctx, containerID, linkTarget)
dstStat, err = s.apiClient().ContainerStatPath(ctx, containerID, linkTarget)
}
// Validate the destination path
@@ -148,7 +164,7 @@ func (s *composeService) copyToContainer(ctx context.Context, containerID string
)
if srcPath == "-" {
content = os.Stdin
content = s.stdin()
resolvedDstPath = dstInfo.Path
if !dstInfo.IsDir {
return errors.Errorf("destination \"%s:%s\" must be a directory", containerID, dstPath)
@@ -192,7 +208,7 @@ func (s *composeService) copyToContainer(ctx context.Context, containerID string
AllowOverwriteDirWithFile: false,
CopyUIDGID: opts.CopyUIDGID,
}
return s.apiClient.CopyToContainer(ctx, containerID, resolvedDstPath, content, options)
return s.apiClient().CopyToContainer(ctx, containerID, resolvedDstPath, content, options)
}
func (s *composeService) copyFromContainer(ctx context.Context, containerID, srcPath, dstPath string, opts api.CopyOptions) error {
@@ -212,7 +228,7 @@ func (s *composeService) copyFromContainer(ctx context.Context, containerID, src
// if client requests to follow symbol link, then must decide target file to be copied
var rebaseName string
if opts.FollowLink {
srcStat, err := s.apiClient.ContainerStatPath(ctx, containerID, srcPath)
srcStat, err := s.apiClient().ContainerStatPath(ctx, containerID, srcPath)
// If the destination is a symbolic link, we should follow it.
if err == nil && srcStat.Mode&os.ModeSymlink != 0 {
@@ -228,14 +244,14 @@ func (s *composeService) copyFromContainer(ctx context.Context, containerID, src
}
}
content, stat, err := s.apiClient.CopyFromContainer(ctx, containerID, srcPath)
content, stat, err := s.apiClient().CopyFromContainer(ctx, containerID, srcPath)
if err != nil {
return err
}
defer content.Close() //nolint:errcheck
if dstPath == "-" {
_, err = io.Copy(os.Stdout, content)
_, err = io.Copy(s.stdout(), content)
return err
}

View File

@@ -31,6 +31,7 @@ import (
moby "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/blkiodev"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/mount"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/strslice"
@@ -173,13 +174,21 @@ func prepareServicesDependsOn(p *types.Project) error {
dependencies = append(dependencies, spec[0])
}
for _, link := range service.Links {
dependencies = append(dependencies, strings.Split(link, ":")[0])
}
if len(dependencies) == 0 {
continue
}
if service.DependsOn == nil {
service.DependsOn = make(types.DependsOnConfig)
}
deps, err := p.GetServices(dependencies...)
// Verify dependencies exist in the project, whether disabled or not
projAllServices := types.Project{}
projAllServices.Services = p.AllServices()
deps, err := projAllServices.GetServices(dependencies...)
if err != nil {
return err
}
@@ -229,7 +238,7 @@ func getImageName(service types.ServiceConfig, projectName string) string {
func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project, service types.ServiceConfig,
number int, inherit *moby.Container, autoRemove bool, attachStdin bool) (*container.Config, *container.HostConfig, *network.NetworkingConfig, error) {
labels, err := s.prepareLabels(p, service, number)
labels, err := s.prepareLabels(service, number)
if err != nil {
return nil, nil, nil, err
}
@@ -255,7 +264,7 @@ func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project,
return nil, nil, nil, err
}
proxyConfig := types.MappingWithEquals(s.configFile.ParseProxyConfig(s.apiClient.DaemonHost(), nil))
proxyConfig := types.MappingWithEquals(s.configFile().ParseProxyConfig(s.apiClient().DaemonHost(), nil))
env := proxyConfig.OverrideBy(service.Environment)
containerConfig := container.Config{
@@ -347,6 +356,11 @@ func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project,
volumesFrom = append(volumesFrom, v[len("container:"):])
}
links, err := s.getLinks(ctx, p.Name, service, number)
if err != nil {
return nil, nil, nil, err
}
securityOpts, err := parseSecurityOpts(p, service.SecurityOpt)
if err != nil {
return nil, nil, nil, err
@@ -371,7 +385,7 @@ func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project,
DNS: service.DNS,
DNSSearch: service.DNSSearch,
DNSOptions: service.DNSOpts,
ExtraHosts: service.ExtraHosts,
ExtraHosts: service.ExtraHosts.AsList(),
SecurityOpt: securityOpts,
UsernsMode: container.UsernsMode(service.UserNSMode),
Privileged: service.Privileged,
@@ -380,6 +394,8 @@ func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project,
Isolation: container.Isolation(service.Isolation),
Runtime: service.Runtime,
LogConfig: logConfig,
GroupAdd: service.GroupAdd,
Links: links,
}
return &containerConfig, &hostConfig, networkConfig, nil
@@ -413,45 +429,42 @@ func parseSecurityOpts(p *types.Project, securityOpts []string) ([]string, error
return securityOpts, nil
}
func (s *composeService) prepareLabels(p *types.Project, service types.ServiceConfig, number int) (map[string]string, error) {
func (s *composeService) prepareLabels(service types.ServiceConfig, number int) (map[string]string, error) {
labels := map[string]string{}
for k, v := range service.Labels {
labels[k] = v
}
labels[api.ProjectLabel] = p.Name
labels[api.ServiceLabel] = service.Name
labels[api.VersionLabel] = api.ComposeVersion
if _, ok := service.Labels[api.OneoffLabel]; !ok {
labels[api.OneoffLabel] = "False"
for k, v := range service.CustomLabels {
labels[k] = v
}
hash, err := ServiceHash(service)
if err != nil {
return nil, err
}
labels[api.ConfigHashLabel] = hash
labels[api.WorkingDirLabel] = p.WorkingDir
labels[api.ConfigFilesLabel] = strings.Join(p.ComposeFiles, ",")
labels[api.ContainerNumberLabel] = strconv.Itoa(number)
var dependencies []string
for s := range service.DependsOn {
dependencies = append(dependencies, s)
for s, d := range service.DependsOn {
dependencies = append(dependencies, s+":"+d.Condition)
}
labels[api.DependenciesLabel] = strings.Join(dependencies, ",")
return labels, nil
}
func getDefaultNetworkMode(project *types.Project, service types.ServiceConfig) string {
mode := "none"
if len(project.Networks) > 0 {
for name := range getNetworksForService(service) {
mode = project.Networks[name].Name
break
}
if len(project.Networks) == 0 {
return "none"
}
return mode
if len(service.Networks) > 0 {
name := service.NetworksByPriority()[0]
return project.Networks[name].Name
}
return project.Networks["default"].Name
}
func getRestartPolicy(service types.ServiceConfig) container.RestartPolicy {
@@ -502,6 +515,7 @@ func getDeployResources(s types.ServiceConfig) container.Resources {
CPUShares: s.CPUShares,
CPUPercent: int64(s.CPUS * 100),
CpusetCpus: s.CPUSet,
DeviceCgroupRules: s.DeviceCgroupRules,
}
if s.PidsLimit != 0 {
@@ -581,8 +595,12 @@ func setLimits(limits *types.Resource, resources *container.Resources) {
resources.Memory = int64(limits.MemoryBytes)
}
if limits.NanoCPUs != "" {
i, _ := strconv.ParseInt(limits.NanoCPUs, 10, 64)
resources.NanoCPUs = i
if f, err := strconv.ParseFloat(limits.NanoCPUs, 64); err == nil {
resources.NanoCPUs = int64(f * 1e9)
}
}
if limits.PIds > 0 {
resources.PidsLimit = &limits.PIds
}
}
@@ -640,15 +658,11 @@ func buildContainerPortBindingOptions(s types.ServiceConfig) nat.PortMap {
bindings := nat.PortMap{}
for _, port := range s.Ports {
p := nat.Port(fmt.Sprintf("%d/%s", port.Target, port.Protocol))
bind := bindings[p]
binding := nat.PortBinding{
HostIP: port.HostIP,
HostIP: port.HostIP,
HostPort: port.Published,
}
if port.Published > 0 {
binding.HostPort = fmt.Sprint(port.Published)
}
bind = append(bind, binding)
bindings[p] = bind
bindings[p] = append(bindings[p], binding)
}
return bindings
}
@@ -699,7 +713,7 @@ func (s *composeService) buildContainerVolumes(ctx context.Context, p types.Proj
var mounts = []mount.Mount{}
image := getImageName(service, p.Name)
imgInspect, _, err := s.apiClient.ImageInspectWithRaw(ctx, image)
imgInspect, _, err := s.apiClient().ImageInspectWithRaw(ctx, image)
if err != nil {
return nil, nil, nil, err
}
@@ -714,16 +728,20 @@ func (s *composeService) buildContainerVolumes(ctx context.Context, p types.Proj
MOUNTS:
for _, m := range mountOptions {
volumeMounts[m.Target] = struct{}{}
// `Bind` API is used when host path need to be created if missing, `Mount` is preferred otherwise
if m.Type == mount.TypeBind || m.Type == mount.TypeNamedPipe {
// `Mount` is preferred but does not offer option to created host path if missing
// so `Bind` API is used here with raw volume string
// see https://github.com/moby/moby/issues/43483
for _, v := range service.Volumes {
if v.Target == m.Target && v.Bind != nil && v.Bind.CreateHostPath {
mode := "rw"
if m.ReadOnly {
mode = "ro"
if v.Target == m.Target {
switch {
case string(m.Type) != v.Type:
v.Source = m.Source
fallthrough
case v.Bind != nil && v.Bind.CreateHostPath:
binds = append(binds, v.String())
continue MOUNTS
}
binds = append(binds, fmt.Sprintf("%s:%s:%s", m.Source, m.Target, mode))
continue MOUNTS
}
}
}
@@ -871,6 +889,10 @@ func buildContainerSecretMounts(p types.Project, s types.ServiceConfig) ([]mount
return nil, fmt.Errorf("unsupported external secret %s", definedSecret.Name)
}
if definedSecret.Environment != "" {
continue
}
mount, err := buildMount(p, types.ServiceVolumeConfig{
Type: types.VolumeTypeBind,
Source: definedSecret.File,
@@ -914,10 +936,14 @@ func buildMount(project types.Project, volume types.ServiceVolumeConfig) (mount.
}
}
bind, vol, tmpfs := buildMountOptions(volume)
bind, vol, tmpfs := buildMountOptions(project, volume)
volume.Target = path.Clean(volume.Target)
if bind != nil {
volume.Type = types.VolumeTypeBind
}
return mount.Mount{
Type: mount.Type(volume.Type),
Source: source,
@@ -930,7 +956,7 @@ func buildMount(project types.Project, volume types.ServiceVolumeConfig) (mount.
}, nil
}
func buildMountOptions(volume types.ServiceVolumeConfig) (*mount.BindOptions, *mount.VolumeOptions, *mount.TmpfsOptions) {
func buildMountOptions(project types.Project, volume types.ServiceVolumeConfig) (*mount.BindOptions, *mount.VolumeOptions, *mount.TmpfsOptions) {
switch volume.Type {
case "bind":
if volume.Volume != nil {
@@ -947,6 +973,11 @@ func buildMountOptions(volume types.ServiceVolumeConfig) (*mount.BindOptions, *m
if volume.Tmpfs != nil {
logrus.Warnf("mount of type `volume` should not define `tmpfs` option")
}
if v, ok := project.Volumes[volume.Source]; ok && v.DriverOpts["o"] == types.VolumeTypeBind {
return buildBindOption(&types.ServiceVolumeBind{
CreateHostPath: true,
}), nil, nil
}
return nil, buildVolumeOptions(volume.Volume), nil
case "tmpfs":
if volume.Bind != nil {
@@ -999,87 +1030,93 @@ func getAliases(s types.ServiceConfig, c *types.ServiceNetworkConfig) []string {
return aliases
}
func getNetworksForService(s types.ServiceConfig) map[string]*types.ServiceNetworkConfig {
if len(s.Networks) > 0 {
return s.Networks
}
if s.NetworkMode != "" {
return nil
}
return map[string]*types.ServiceNetworkConfig{"default": nil}
}
func (s *composeService) ensureNetwork(ctx context.Context, n types.NetworkConfig) error {
_, err := s.apiClient.NetworkInspect(ctx, n.Name, moby.NetworkInspectOptions{})
// NetworkInspect will match on ID prefix, so NetworkList with a name
// filter is used to look for an exact match to prevent e.g. a network
// named `db` from getting erroneously matched to a network with an ID
// like `db9086999caf`
networks, err := s.apiClient().NetworkList(ctx, moby.NetworkListOptions{
Filters: filters.NewArgs(filters.Arg("name", n.Name)),
})
if err != nil {
if errdefs.IsNotFound(err) {
if n.External.External {
return fmt.Errorf("network %s declared as external, but could not be found", n.Name)
}
var ipam *network.IPAM
if n.Ipam.Config != nil {
var config []network.IPAMConfig
for _, pool := range n.Ipam.Config {
config = append(config, network.IPAMConfig{
Subnet: pool.Subnet,
IPRange: pool.IPRange,
Gateway: pool.Gateway,
AuxAddress: pool.AuxiliaryAddresses,
})
}
ipam = &network.IPAM{
Driver: n.Ipam.Driver,
Config: config,
}
}
createOpts := moby.NetworkCreate{
// TODO NameSpace Labels
Labels: n.Labels,
Driver: n.Driver,
Options: n.DriverOpts,
Internal: n.Internal,
Attachable: n.Attachable,
IPAM: ipam,
EnableIPv6: n.EnableIPv6,
}
if n.Ipam.Driver != "" || len(n.Ipam.Config) > 0 {
createOpts.IPAM = &network.IPAM{}
}
if n.Ipam.Driver != "" {
createOpts.IPAM.Driver = n.Ipam.Driver
}
for _, ipamConfig := range n.Ipam.Config {
config := network.IPAMConfig{
Subnet: ipamConfig.Subnet,
}
createOpts.IPAM.Config = append(createOpts.IPAM.Config, config)
}
networkEventName := fmt.Sprintf("Network %s", n.Name)
w := progress.ContextWriter(ctx)
w.Event(progress.CreatingEvent(networkEventName))
if _, err := s.apiClient.NetworkCreate(ctx, n.Name, createOpts); err != nil {
w.Event(progress.ErrorEvent(networkEventName))
return errors.Wrapf(err, "failed to create network %s", n.Name)
}
w.Event(progress.CreatedEvent(networkEventName))
return nil
}
return err
}
if len(networks) == 0 {
if n.External.External {
if n.Driver == "overlay" {
// Swarm nodes do not register overlay networks that were
// created on a different node unless they're in use.
// Here we assume `driver` is relevant for a network we don't manage
// which is a non-sense, but this is our legacy ¯\(ツ)/¯
// networkAttach will later fail anyway if network actually doesn't exists
return nil
}
return fmt.Errorf("network %s declared as external, but could not be found", n.Name)
}
var ipam *network.IPAM
if n.Ipam.Config != nil {
var config []network.IPAMConfig
for _, pool := range n.Ipam.Config {
config = append(config, network.IPAMConfig{
Subnet: pool.Subnet,
IPRange: pool.IPRange,
Gateway: pool.Gateway,
AuxAddress: pool.AuxiliaryAddresses,
})
}
ipam = &network.IPAM{
Driver: n.Ipam.Driver,
Config: config,
}
}
createOpts := moby.NetworkCreate{
// TODO NameSpace Labels
Labels: n.Labels,
Driver: n.Driver,
Options: n.DriverOpts,
Internal: n.Internal,
Attachable: n.Attachable,
IPAM: ipam,
EnableIPv6: n.EnableIPv6,
}
if n.Ipam.Driver != "" || len(n.Ipam.Config) > 0 {
createOpts.IPAM = &network.IPAM{}
}
if n.Ipam.Driver != "" {
createOpts.IPAM.Driver = n.Ipam.Driver
}
for _, ipamConfig := range n.Ipam.Config {
config := network.IPAMConfig{
Subnet: ipamConfig.Subnet,
IPRange: ipamConfig.IPRange,
Gateway: ipamConfig.Gateway,
AuxAddress: ipamConfig.AuxiliaryAddresses,
}
createOpts.IPAM.Config = append(createOpts.IPAM.Config, config)
}
networkEventName := fmt.Sprintf("Network %s", n.Name)
w := progress.ContextWriter(ctx)
w.Event(progress.CreatingEvent(networkEventName))
if _, err := s.apiClient().NetworkCreate(ctx, n.Name, createOpts); err != nil {
w.Event(progress.ErrorEvent(networkEventName))
return errors.Wrapf(err, "failed to create network %s", n.Name)
}
w.Event(progress.CreatedEvent(networkEventName))
return nil
}
return nil
}
func (s *composeService) removeNetwork(ctx context.Context, networkID string, networkName string) error {
w := progress.ContextWriter(ctx)
eventName := fmt.Sprintf("Network %s", networkName)
func (s *composeService) removeNetwork(ctx context.Context, network string, w progress.Writer) error {
eventName := fmt.Sprintf("Network %s", network)
w.Event(progress.RemovingEvent(eventName))
if err := s.apiClient.NetworkRemove(ctx, networkID); err != nil {
if err := s.apiClient().NetworkRemove(ctx, network); err != nil {
w.Event(progress.ErrorEvent(eventName))
return errors.Wrapf(err, fmt.Sprintf("failed to remove network %s", networkID))
return errors.Wrapf(err, fmt.Sprintf("failed to remove network %s", network))
}
w.Event(progress.RemovedEvent(eventName))
@@ -1087,13 +1124,13 @@ func (s *composeService) removeNetwork(ctx context.Context, networkID string, ne
}
func (s *composeService) ensureVolume(ctx context.Context, volume types.VolumeConfig, project string) error {
inspected, err := s.apiClient.VolumeInspect(ctx, volume.Name)
inspected, err := s.apiClient().VolumeInspect(ctx, volume.Name)
if err != nil {
if !errdefs.IsNotFound(err) {
return err
}
if volume.External.External {
return fmt.Errorf("external volume %q not found", volume.External.Name)
return fmt.Errorf("external volume %q not found", volume.Name)
}
err := s.createVolume(ctx, volume)
return err
@@ -1118,7 +1155,7 @@ func (s *composeService) createVolume(ctx context.Context, volume types.VolumeCo
eventName := fmt.Sprintf("Volume %q", volume.Name)
w := progress.ContextWriter(ctx)
w.Event(progress.CreatingEvent(eventName))
_, err := s.apiClient.VolumeCreate(ctx, volume_api.VolumeCreateBody{
_, err := s.apiClient().VolumeCreate(ctx, volume_api.VolumeCreateBody{
Labels: volume.Labels,
Name: volume.Name,
Driver: volume.Driver,

View File

@@ -142,3 +142,74 @@ func TestBuildContainerMountOptions(t *testing.T) {
assert.Equal(t, mounts[0].Target, "/var/myvolume1")
assert.Equal(t, mounts[1].Target, "/var/myvolume2")
}
func TestGetDefaultNetworkMode(t *testing.T) {
t.Run("returns the network with the highest priority when service has multiple networks", func(t *testing.T) {
service := composetypes.ServiceConfig{
Name: "myService",
Networks: map[string]*composetypes.ServiceNetworkConfig{
"myNetwork1": {
Priority: 10,
},
"myNetwork2": {
Priority: 1000,
},
},
}
project := composetypes.Project{
Name: "myProject",
Services: []composetypes.ServiceConfig{
service,
},
Networks: composetypes.Networks(map[string]composetypes.NetworkConfig{
"myNetwork1": {
Name: "myProject_myNetwork1",
},
"myNetwork2": {
Name: "myProject_myNetwork2",
},
}),
}
assert.Equal(t, getDefaultNetworkMode(&project, service), "myProject_myNetwork2")
})
t.Run("returns default network when service has no networks", func(t *testing.T) {
service := composetypes.ServiceConfig{
Name: "myService",
}
project := composetypes.Project{
Name: "myProject",
Services: []composetypes.ServiceConfig{
service,
},
Networks: composetypes.Networks(map[string]composetypes.NetworkConfig{
"myNetwork1": {
Name: "myProject_myNetwork1",
},
"myNetwork2": {
Name: "myProject_myNetwork2",
},
"default": {
Name: "myProject_default",
},
}),
}
assert.Equal(t, getDefaultNetworkMode(&project, service), "myProject_default")
})
t.Run("returns none if project has no networks", func(t *testing.T) {
service := composetypes.ServiceConfig{
Name: "myService",
}
project := composetypes.Project{
Name: "myProject",
Services: []composetypes.ServiceConfig{
service,
},
}
assert.Equal(t, getDefaultNetworkMode(&project, service), "none")
})
}

View File

@@ -132,7 +132,7 @@ func getParents(v *Vertex) []*Vertex {
return v.GetParents()
}
// GetParents returns a slice with the parent vertexes of the a Vertex
// GetParents returns a slice with the parent vertices of the a Vertex
func (v *Vertex) GetParents() []*Vertex {
var res []*Vertex
for _, p := range v.Parents {
@@ -145,7 +145,7 @@ func getChildren(v *Vertex) []*Vertex {
return v.GetChildren()
}
// GetChildren returns a slice with the child vertexes of the a Vertex
// GetChildren returns a slice with the child vertices of the a Vertex
func (v *Vertex) GetChildren() []*Vertex {
var res []*Vertex
for _, p := range v.Children {
@@ -194,7 +194,7 @@ func (g *Graph) AddVertex(key string, service string, initialStatus ServiceStatu
g.Vertices[key] = v
}
// AddEdge adds a relationship of dependency between vertexes `source` and `destination`
// AddEdge adds a relationship of dependency between vertices `source` and `destination`
func (g *Graph) AddEdge(source string, destination string) error {
g.lock.Lock()
defer g.lock.Unlock()

View File

@@ -23,8 +23,8 @@ import (
"time"
"github.com/compose-spec/compose-go/types"
"github.com/docker/cli/cli/registry/client"
moby "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/errdefs"
"golang.org/x/sync/errgroup"
@@ -50,15 +50,19 @@ func (s *composeService) down(ctx context.Context, projectName string, options a
return err
}
if options.Project == nil {
options.Project = s.projectFromContainerLabels(containers.filter(isNotOneOff), projectName)
project := options.Project
if project == nil {
project, err = s.getProjectWithResources(ctx, containers, projectName)
if err != nil {
return err
}
}
if len(containers) > 0 {
resourceToRemove = true
}
err = InReverseDependencyOrder(ctx, options.Project, func(c context.Context, service string) error {
err = InReverseDependencyOrder(ctx, project, func(c context.Context, service string) error {
serviceContainers := containers.filter(isService(service))
err := s.removeContainers(ctx, w, serviceContainers, options.Timeout, options.Volumes)
return err
@@ -67,7 +71,7 @@ func (s *composeService) down(ctx context.Context, projectName string, options a
return err
}
orphans := containers.filter(isNotService(options.Project.ServiceNames()...))
orphans := containers.filter(isNotService(project.ServiceNames()...))
if options.RemoveOrphans && len(orphans) > 0 {
err := s.removeContainers(ctx, w, orphans, options.Timeout, false)
if err != nil {
@@ -75,25 +79,18 @@ func (s *composeService) down(ctx context.Context, projectName string, options a
}
}
ops, err := s.ensureNetworksDown(ctx, projectName)
if err != nil {
return err
}
ops := s.ensureNetworksDown(ctx, project, w)
if options.Images != "" {
ops = append(ops, s.ensureImagesDown(ctx, projectName, options, w)...)
ops = append(ops, s.ensureImagesDown(ctx, project, options, w)...)
}
if options.Volumes {
rm, err := s.ensureVolumesDown(ctx, projectName, w)
if err != nil {
return err
}
ops = append(ops, rm...)
ops = append(ops, s.ensureVolumesDown(ctx, project, w)...)
}
if !resourceToRemove && len(ops) == 0 {
w.Event(progress.NewEvent(projectName, progress.Done, "Warning: No resource found to remove"))
fmt.Fprintf(s.stderr(), "Warning: No resource found to remove for project %q.\n", projectName)
}
eg, _ := errgroup.WithContext(ctx)
@@ -103,24 +100,23 @@ func (s *composeService) down(ctx context.Context, projectName string, options a
return eg.Wait()
}
func (s *composeService) ensureVolumesDown(ctx context.Context, projectName string, w progress.Writer) ([]downOp, error) {
func (s *composeService) ensureVolumesDown(ctx context.Context, project *types.Project, w progress.Writer) []downOp {
var ops []downOp
volumes, err := s.apiClient.VolumeList(ctx, filters.NewArgs(projectFilter(projectName)))
if err != nil {
return ops, err
}
for _, vol := range volumes.Volumes {
id := vol.Name
for _, vol := range project.Volumes {
if vol.External.External {
continue
}
volumeName := vol.Name
ops = append(ops, func() error {
return s.removeVolume(ctx, id, w)
return s.removeVolume(ctx, volumeName, w)
})
}
return ops, nil
return ops
}
func (s *composeService) ensureImagesDown(ctx context.Context, projectName string, options api.DownOptions, w progress.Writer) []downOp {
func (s *composeService) ensureImagesDown(ctx context.Context, project *types.Project, options api.DownOptions, w progress.Writer) []downOp {
var ops []downOp
for image := range s.getServiceImages(options, projectName) {
for image := range s.getServiceImages(options, project) {
image := image
ops = append(ops, func() error {
return s.removeImage(ctx, image, w)
@@ -129,31 +125,34 @@ func (s *composeService) ensureImagesDown(ctx context.Context, projectName strin
return ops
}
func (s *composeService) ensureNetworksDown(ctx context.Context, projectName string) ([]downOp, error) {
func (s *composeService) ensureNetworksDown(ctx context.Context, project *types.Project, w progress.Writer) []downOp {
var ops []downOp
networks, err := s.apiClient.NetworkList(ctx, moby.NetworkListOptions{Filters: filters.NewArgs(projectFilter(projectName))})
if err != nil {
return ops, err
}
for _, n := range networks {
networkID := n.ID
for _, n := range project.Networks {
if n.External.External {
continue
}
networkName := n.Name
_, err := s.apiClient().NetworkInspect(ctx, networkName, moby.NetworkInspectOptions{})
if client.IsNotFound(err) {
return nil
}
ops = append(ops, func() error {
return s.removeNetwork(ctx, networkID, networkName)
return s.removeNetwork(ctx, networkName, w)
})
}
return ops, nil
return ops
}
func (s *composeService) getServiceImages(options api.DownOptions, projectName string) map[string]struct{} {
func (s *composeService) getServiceImages(options api.DownOptions, project *types.Project) map[string]struct{} {
images := map[string]struct{}{}
for _, service := range options.Project.Services {
for _, service := range project.Services {
image := service.Image
if options.Images == "local" && image != "" {
continue
}
if image == "" {
image = getImageName(service, projectName)
image = getImageName(service, project.Name)
}
images[image] = struct{}{}
}
@@ -163,7 +162,7 @@ func (s *composeService) getServiceImages(options api.DownOptions, projectName s
func (s *composeService) removeImage(ctx context.Context, image string, w progress.Writer) error {
id := fmt.Sprintf("Image %s", image)
w.Event(progress.NewEvent(id, progress.Working, "Removing"))
_, err := s.apiClient.ImageRemove(ctx, image, moby.ImageRemoveOptions{})
_, err := s.apiClient().ImageRemove(ctx, image, moby.ImageRemoveOptions{})
if err == nil {
w.Event(progress.NewEvent(id, progress.Done, "Removed"))
return nil
@@ -178,7 +177,7 @@ func (s *composeService) removeImage(ctx context.Context, image string, w progre
func (s *composeService) removeVolume(ctx context.Context, id string, w progress.Writer) error {
resource := fmt.Sprintf("Volume %s", id)
w.Event(progress.NewEvent(resource, progress.Working, "Removing"))
err := s.apiClient.VolumeRemove(ctx, id, true)
err := s.apiClient().VolumeRemove(ctx, id, true)
if err == nil {
w.Event(progress.NewEvent(resource, progress.Done, "Removed"))
return nil
@@ -197,7 +196,7 @@ func (s *composeService) stopContainers(ctx context.Context, w progress.Writer,
eg.Go(func() error {
eventName := getContainerProgressName(container)
w.Event(progress.StoppingEvent(eventName))
err := s.apiClient.ContainerStop(ctx, container.ID, timeout)
err := s.apiClient().ContainerStop(ctx, container.ID, timeout)
if err != nil {
w.Event(progress.ErrorMessageEvent(eventName, "Error while Stopping"))
return err
@@ -222,7 +221,7 @@ func (s *composeService) removeContainers(ctx context.Context, w progress.Writer
return err
}
w.Event(progress.RemovingEvent(eventName))
err = s.apiClient.ContainerRemove(ctx, container.ID, moby.ContainerRemoveOptions{
err = s.apiClient().ContainerRemove(ctx, container.ID, moby.ContainerRemoveOptions{
Force: true,
RemoveVolumes: volumes,
})
@@ -237,31 +236,23 @@ func (s *composeService) removeContainers(ctx context.Context, w progress.Writer
return eg.Wait()
}
func (s *composeService) projectFromContainerLabels(containers Containers, projectName string) *types.Project {
project := &types.Project{
Name: projectName,
func (s *composeService) getProjectWithResources(ctx context.Context, containers Containers, projectName string) (*types.Project, error) {
containers = containers.filter(isNotOneOff)
project, err := s.projectFromName(containers, projectName)
if err != nil && !api.IsNotFoundError(err) {
return nil, err
}
if len(containers) == 0 {
return project
volumes, err := s.actualVolumes(ctx, projectName)
if err != nil {
return nil, err
}
set := map[string]moby.Container{}
for _, c := range containers {
set[c.Labels[api.ServiceLabel]] = c
project.Volumes = volumes
networks, err := s.actualNetworks(ctx, projectName)
if err != nil {
return nil, err
}
for s, c := range set {
service := types.ServiceConfig{
Name: s,
Image: c.Image,
Labels: c.Labels,
}
dependencies := c.Labels[api.DependenciesLabel]
if len(dependencies) > 0 {
service.DependsOn = types.DependsOnConfig{}
for _, d := range strings.Split(dependencies, ",") {
service.DependsOn[d] = types.ServiceDependency{}
}
}
project.Services = append(project.Services, service)
}
return project
project.Networks = networks
return project, nil
}

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