Compare commits

...

164 Commits

Author SHA1 Message Date
Guillaume Lours
ab791877ef Merge pull request #10105 from docker/dependabot/go_modules/github.com/containerd/containerd-1.6.14
build(deps): bump github.com/containerd/containerd from 1.6.12 to 1.6.14
2022-12-20 10:17:46 +01:00
dependabot[bot]
aae5ddca27 build(deps): bump github.com/containerd/containerd from 1.6.12 to 1.6.14
Bumps [github.com/containerd/containerd](https://github.com/containerd/containerd) from 1.6.12 to 1.6.14.
- [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.12...v1.6.14)

---
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-12-20 09:06:44 +00:00
Guillaume Lours
7cf6d5ec4e Merge pull request #10104 from ndeloof/logs_race_condition
fix race condition on compose logs
2022-12-20 09:54:57 +01:00
Nicolas De Loof
0ab5079c1a fix race condition on compose logs
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-12-20 09:21:31 +01:00
Tiger Wang
89ef8198f3 update projectOptions to be public by renaming it to ProjectOptions
Signed-off-by: Tiger Wang <tigerwang@outlook.com>
2022-12-20 08:53:27 +01:00
Nicolas De Loof
b8bbdcd872 detect dependency failed to start
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-12-19 16:26:39 +01:00
Guillaume Lours
9d12eec148 Merge pull request #10100 from ndeloof/cpus
set CPU quota
2022-12-19 15:15:41 +01:00
Nicolas De Loof
d0e95ccac3 set CPU quota
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-12-19 15:05:09 +01:00
Guillaume Lours
1e682a40ac Merge pull request #10099 from laurazard/use-defaultplatform-create
Use `DOCKER_DEFAULT_PLATFORM` to determine platform when creating container
2022-12-19 14:17:49 +01:00
Laura Brehm
7bc27d441b Use DOCKER_DEFAULT_PLATFORM to determine platform when creating container
Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2022-12-19 11:58:03 +00:00
Nicolas De Loof
c1ce53c972 fix regression running pull --ignore-pull-failures
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-12-19 11:56:03 +01:00
Nicolas De Loof
e42673daed only list running containers when --all=false
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-12-16 21:46:55 +01:00
Milas Bowman
c37182b2c5 Merge pull request #10090 from milas/fix-wcow-volume
volume: fix WCOW volume mounts
2022-12-16 13:43:04 -05:00
Milas Bowman
ffb95449a2 volume: fix WCOW volume mounts
Do not use the older `Volumes` field in the API; instead rely on
the more robust `Mounts`. For Linux containers, it seems that it's
fine to set both of these. For Windows containers (WCOW), however,
there appears to be a Moby bug that causes it to normalize the
anonymous (`Volumes`) variant to lowercase, which can result in
duplicative volume definitions and an error when trying to start
the container.

Fixes #9577.

Signed-off-by: Milas Bowman <milas.bowman@docker.com>
2022-12-16 09:52:34 -05:00
Guillaume Lours
0eaa249222 Merge pull request #10084 from ndeloof/secret_uid
apply uid/gid when creating secret from environment
2022-12-15 16:26:19 +01:00
Nicolas De Loof
5c1484ece6 apply uid/gid when creating secret from environment
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-12-15 15:54:04 +01:00
Guillaume Lours
0fedddb008 Merge pull request #10083 from ndeloof/nodeps
use recently introduced `withSelectedServicesOnly` to reduce code duplication
2022-12-15 15:51:29 +01:00
Guillaume Lours
aa0720f7e5 Merge pull request #10062 from ndeloof/9554
load project from files when explicitly set by user
2022-12-15 15:50:36 +01:00
Nicolas De Loof
84984864c8 load project from explicit --files when set
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-12-15 15:38:41 +01:00
Nicolas De Loof
8566daa96e use recently introduced withSelectedServicesOnly to reduce code duplication
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-12-15 15:38:18 +01:00
Guillaume Lours
1b1f783e99 Merge pull request #10076 from ndeloof/timestamp
introduce --timestamp option on compose up
2022-12-15 15:36:56 +01:00
Nicolas De Loof
84ea395d5d introduce --timestamp option on compose up
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-12-15 12:14:22 +01:00
Gabriel Féron
1cb5536a2e Address review comments
Signed-off-by: Gabriel Féron <g@leirbag.net>
2022-12-15 11:42:14 +01:00
Gabriel Féron
e4850d9c48 Add --include-deps to push command
Signed-off-by: Gabriel Féron <g@leirbag.net>
2022-12-15 11:42:14 +01:00
Nicolas De Loof
8c39b5b7fd align --format flag and UX with docker cli
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-12-14 22:53:43 +01:00
Nicolas De Loof
bc568eeb9b align compose ps output with docker ps
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-12-14 22:53:43 +01:00
Sebastiaan van Stijn
a501ab3a2f use StatusError from docker/cli, not "dockerd"
This package is a leftover from when the "docker" cli and the "dockerd"
cli both lived in the same repository. The package in docker/docker will
be (re)moved soon, so replace it with the implementation in docker/cli,
which is the right one :)

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-12-14 22:53:18 +01:00
Nicolas De Loof
d4a4dcf4ee resolve --env-file as absolute path
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-12-14 09:43:32 +01:00
Nicolas De Loof
05e987dd0a fix parsing of repository:tag
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-12-14 09:23:07 +01:00
Nicolas De Loof
0368f19030 distinguish stdout and stderr in up logs
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-12-13 14:42:42 +01:00
Nicolas De Loof
3ee2ab87bb ContainerStart must run sequentially for engine to assing distinct ports within configured range
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-12-12 16:08:50 +01:00
Nicolas De Loof
8f991a20db Fix corner case when there's no container to attach to
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-12-09 10:02:56 +01:00
Nicolas De Loof
0234e13454 Don't stop pull for images that can be built
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-12-08 21:20:04 +01:00
Joyce Brum
c342891f3e Squashed commit of the following:
commit 72293cbe879bcd1fff610eace6929922c4a4d668
Author: Joyce Brum <joycebrum@google.com>
Date:   Thu Nov 3 10:20:52 2022 -0300

    fix: upgrade scorecard action to 2.0.6

    Signed-off-by: Joyce Brum <joycebrum@google.com>

commit 39451ef36f4ce71053c59c3a238d95752be05136
Author: Joyce <joycebrumu.u@gmail.com>
Date:   Wed Sep 14 17:52:59 2022 -0300

    Change to the original repository

    Signed-off-by: Joyce <joycebrumu.u@gmail.com>
    Signed-off-by: Joyce Brum <joycebrum@google.com>

commit ddcccaa14b8ef928a4bc8ba38429d8a442806ae9
Author: Joyce <joycebrumu.u@gmail.com>
Date:   Wed Sep 14 17:51:26 2022 -0300

    Add scorecard badge

    Signed-off-by: Joyce <joycebrumu.u@gmail.com>
    Signed-off-by: Joyce Brum <joycebrum@google.com>

commit 8ac265f0ee197e30862c0510b01dce2bc350e129
Author: Joyce <joycebrumu.u@gmail.com>
Date:   Wed Sep 14 17:49:49 2022 -0300

    Configure Scorecard action 2.0.3

    Signed-off-by: Joyce <joycebrumu.u@gmail.com>

Signed-off-by: Joyce Brum <joycebrum@google.com>
2022-12-08 21:11:39 +01:00
Guillaume Lours
40fb42e0c9 Merge pull request #10055 from docker/dependabot/go_modules/github.com/containerd/containerd-1.6.12
build(deps): bump github.com/containerd/containerd from 1.6.10 to 1.6.12
2022-12-08 20:08:34 +01:00
dependabot[bot]
8ef3494711 build(deps): bump github.com/containerd/containerd from 1.6.10 to 1.6.12
Bumps [github.com/containerd/containerd](https://github.com/containerd/containerd) from 1.6.10 to 1.6.12.
- [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.10...v1.6.12)

---
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-12-08 19:56:57 +01:00
Guillaume Lours
be74c90f50 Merge pull request #10059 from glours/remve-e2e-go.mod
remove go.* from e2e tests directory
2022-12-08 19:56:34 +01:00
Guillaume Lours
cc247fdb84 remove go.* from e2e tests directory
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2022-12-08 19:06:22 +01:00
nitin mewar
a4ac6ab694 added table of contents inside readme
Signed-off-by: nitin mewar <nitinmewar28@gmail.com>
2022-12-08 17:02:38 +01:00
Milas Bowman
a5823b12f9 Merge pull request #10048 from thaJeztah/update_go_1.19.4
update to go1.19.4
2022-12-07 17:13:35 -05:00
Milas Bowman
b27ace6c55 Merge pull request #10051 from ndeloof/9897
fix race condition collecting pulled images IDs
2022-12-07 17:12:39 -05:00
Nicolas De Loof
a73dce44b3 fix race condition collecting pulled images IDs
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-12-07 21:33:44 +01:00
Nicolas De Loof
804d7163a7 detect required service are gone to stop watching
explicit API to stop the log printer

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-12-07 21:07:27 +01:00
Sebastiaan van Stijn
cc60026c7b update to go1.19.4
Includes security fixes for net/http (CVE-2022-41717, CVE-2022-41720),
and os (CVE-2022-41720).

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

- os, net/http: avoid escapes from os.DirFS and http.Dir on Windows

  The os.DirFS function and http.Dir type provide access to a tree of files
  rooted at a given directory. These functions permitted access to Windows
  device files under that root. For example, os.DirFS("C:/tmp").Open("COM1")
  would open the COM1 device.
  Both os.DirFS and http.Dir only provide read-only filesystem access.

  In addition, on Windows, an os.DirFS for the directory \(the root of the
  current drive) can permit a maliciously crafted path to escape from the
  drive and access any path on the system.

  The behavior of os.DirFS("") has changed. Previously, an empty root was
  treated equivalently to "/", so os.DirFS("").Open("tmp") would open the
  path "/tmp". This now returns an error.

  This is CVE-2022-41720 and Go issue https://go.dev/issue/56694.

- net/http: limit canonical header cache by bytes, not entries

  An attacker can cause excessive memory growth in a Go server accepting
  HTTP/2 requests.

  HTTP/2 server connections contain a cache of HTTP header keys sent by
  the client. While the total number of entries in this cache is capped,
  an attacker sending very large keys can cause the server to allocate
  approximately 64 MiB per open connection.

  This issue is also fixed in golang.org/x/net/http2 vX.Y.Z, for users
  manually configuring HTTP/2.

  Thanks to Josselin Costanzi for reporting this issue.

  This is CVE-2022-41717 and Go issue https://go.dev/issue/56350.

View the release notes for more information:
https://go.dev/doc/devel/release#go1.19.4

And the milestone on the issue tracker:
https://github.com/golang/go/issues?q=milestone%3AGo1.19.4+label%3ACherryPickApproved

Full diff: https://github.com/golang/go/compare/go1.19.3...go1.19.4

The golang.org/x/net fix is in 1e63c2f08a

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-12-07 10:22:50 +01:00
Guillaume Lours
6b4ad0d1db Merge pull request #10047 from thaJeztah/cleanup_output
Cleanup tips from output
2022-12-06 23:37:38 +01:00
Sebastiaan van Stijn
87a0a57f70 Cleanup tips from output
The scan tip has been shown for two years, and most users will know
about it by now. Presenting the message also involved checking if the
plugin was installed, and wether or not the message was shown before,
which also caused some overhead, so cleaning up the output a bit.

The corresponding DOCKER_SCAN_SUGGEST environment-variable is also
removed with this.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-12-06 23:29:03 +01:00
Guillaume Lours
c80d52aded Merge pull request #10049 from glours/fix-flaky-tests
check only running containers in after down tests of profiles e2e tests
2022-12-06 23:28:51 +01:00
Guillaume Lours
95bc6c58b7 check only running containers in after down tests of profiles e2e tests
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2022-12-06 23:17:14 +01:00
Laura Brehm
be30c67633 Merge pull request #10045 from docker/dependabot/go_modules/go.opentelemetry.io/otel-1.11.2
build(deps): bump go.opentelemetry.io/otel from 1.11.1 to 1.11.2
2022-12-06 13:24:33 +00:00
Laura Brehm
57a1e1e0df Update e2e mod deps
Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2022-12-06 13:13:17 +00:00
dependabot[bot]
02305756b3 build(deps): bump go.opentelemetry.io/otel from 1.11.1 to 1.11.2
Bumps [go.opentelemetry.io/otel](https://github.com/open-telemetry/opentelemetry-go) from 1.11.1 to 1.11.2.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.11.1...v1.11.2)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-06 09:07:31 +00:00
Guillaume Lours
12dad4f8d0 Merge pull request #10030 from ndeloof/max_concurrency
introduce --parallel to limit concurrent engine calls
2022-12-06 09:45:57 +01:00
Nicolas De Loof
a0acc20d88 introduce --parallel to limit concurrent engine calls
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-12-06 08:15:50 +01:00
Milas Bowman
053f20edab port: improve error-handling if port not found (#10039)
This method looked slightly incomplete. If the port wasn't found,
it'd return `err`, but that was always `nil`, so we'd print out
`:0`.

Now, we construct a nice error message with the targeted port and
the ones we found.

The `--protocol` flag is also now case-insensitive to prevent any
weirdness/confusion there.

Co-authored-by: Nick Sieger <nicksieger@gmail.com>
Signed-off-by: Milas Bowman <milas.bowman@docker.com>
2022-12-05 22:11:45 +00:00
Guillaume Lours
6ed9a7928f Merge pull request #10037 from milas/go-1.19.3
ci: upgrade to Go 1.19.3 & bump deps
2022-12-02 17:40:50 +01:00
Milas Bowman
9b8d520b7d ci: upgrade to Go 1.19.3 & bump deps
Upgrade to Go 1.19.3 (from 1.19.2) and bump a couple dependencies.

Signed-off-by: Milas Bowman <milas.bowman@docker.com>
2022-12-02 11:24:46 -05:00
Milas Bowman
113fb6732d schema: add support for tmpfs.mode in mount definition (#10031)
See compose-spec/compose-go#325 for the acutal spec change. This
propagates it to the Engine API object and adds an E2E test via
Cucumber 🥒

Fixes #9873.

Signed-off-by: Milas Bowman <milas.bowman@docker.com>
2022-12-02 11:21:53 -05:00
Milas Bowman
b9e5f9e917 test: speed up Cucumber stop test (#10032)
Evidently `ping` doesn't respond to `SIGTERM`, so use `init` to
get Tini supervising it. This changes the exit code to 143 since
it's not hitting the 10s timeout and getting a `SIGKILL` (137).

Signed-off-by: Milas Bowman <milas.bowman@docker.com>
2022-12-02 09:34:06 -05:00
i-ky
c74a77e895 Make use of Containers.filter() and isService()
Signed-off-by: i-ky <gl.ivanovsky@gmail.com>
2022-12-02 12:16:35 +01:00
i-ky
7f975fa40b Fix replacing "service:x" with "container:y"
Signed-off-by: i-ky <gl.ivanovsky@gmail.com>
2022-12-02 12:16:35 +01:00
Guillaume Lours
7cf5940f4a Merge pull request #10035 from ndeloof/9323
use StringToBool to detect COMPOSE_IGNORE_ORPHANS
2022-12-01 10:06:21 +01:00
Nicolas De Loof
7369127650 use StringToBool to detect COMPOSE_IGNORE_ORPHANS
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-12-01 09:38:57 +01:00
windforce17
2e7644ff21 use api.Separator to print right image names
Signed-off-by: windforce17 <wzcboss@qq.com>
2022-12-01 09:20:09 +01:00
Nicolas De Loof
8f2b747104 use DistributionInspect to resolve image digest
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-11-30 21:18:18 +01:00
Nicolas De Loof
9ac4f69918 move image digests resolution to backend
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-11-30 21:18:18 +01:00
NitishKumar06
2bef9769e5 Broken Link fixed in compose docs
Signed-off-by: NitishKumar06 <justnitish06@gmail.com>
2022-11-30 17:33:58 +01:00
Guillaume Lours
707d55c77f add file header and cleanup profiles e2e tests
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2022-11-30 13:21:16 +01:00
Guillaume Lours
5edd783032 add e2e tests to check profile activation via targeted service
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2022-11-30 13:21:16 +01:00
Guillaume Lours
6fbef29619 add e2e tests to check no profile usages
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2022-11-30 13:21:16 +01:00
Guillaume Lours
7fe43a8b4a add e2e tests using explicitly profiles
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2022-11-30 13:21:16 +01:00
Guillaume Lours
24ec0b2d09 pass services list to projectOrName function to add profiles for targeted services
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2022-11-30 13:21:16 +01:00
Nicolas De Loof
ed38fe0da8 only stop services started by up on interruption
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-11-30 08:07:20 +01:00
Umar Faruq Chowdhury
06e71371ff docs: fix grammatical issues (#9997)
Signed-off-by: Umar Chowdhury <umarfchy@gmail.com>
Co-authored-by: Milas Bowman <milasb@gmail.com>
2022-11-29 10:52:22 -05:00
Nicolas De Loof
fb5b90ed47 implement support for oom_score_adj
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-11-25 09:03:12 +01:00
Guillaume Lours
10a5d998e6 useDockerDefaultOrServicePlatform fct should return service.platform if defined
and present in the build.platforms list (or if the list is empty)

Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2022-11-25 09:03:03 +01:00
Nicolas De Loof
c3e5e49957 configure buildx for plain output if --ansi=never has been set
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-11-24 16:02:04 +01:00
Guillaume Lours
770281e9d5 Merge pull request #10016 from glours/fix-docs-pr-creation-workflow
change the default branch of the doc repository
2022-11-23 17:03:59 +01:00
Guillaume Lours
4bf98c7053 change the default branch of the doc repository
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2022-11-23 16:28:16 +01:00
Laura Brehm
8c5d7baa7d Merge pull request #9995 from docker/dependabot/go_modules/github.com/containerd/containerd-1.6.10
build(deps): bump github.com/containerd/containerd from 1.6.9 to 1.6.10
2022-11-21 18:33:29 +01:00
Laura Brehm
d7a24e9c81 Update e2e module deps
Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2022-11-21 18:22:15 +01:00
Guillaume Lours
02818ba6c7 Merge pull request #9998 from glours/display-engine-warnings
display creation warnings from the engine
2022-11-21 15:17:26 +01:00
Guillaume Lours
481ae0aa7d Merge pull request #9999 from glours/pull-use-default-platform
use platform defined by DOCKER_DEFAULT_PLATFORM when pulling and no service platform defined
2022-11-18 14:27:41 +01:00
Laura Brehm
88c3aaf1bf Merge pull request #10007 from laurazard/add-build-run
Add `--build` to `compose run`
2022-11-17 20:00:57 +01:00
Nicolas De Loof
19d6ca9c5d ignore error parsing container number label, just warn
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2022-11-17 15:18:28 +01:00
Laura Brehm
6fe03e935e Update docs
Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2022-11-17 12:29:19 +01:00
Laura Brehm
35d31cc500 Add --build option to compose run
Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2022-11-17 00:57:35 +01:00
Guillaume Lours
7c5675c306 use platform defined by DOCKER_DEFAULT_PLATFORM when pulling and no service platform defined
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2022-11-16 13:51:44 +01:00
Guillaume Lours
ea32fc99e1 Merge pull request #9984 from glours/build-image-depends-on
check if a missing image won't be build via a service declared in depends_on section
2022-11-16 13:04:53 +01:00
Guillaume Lours
a077e8a24b display creation warnings from the engine
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2022-11-15 18:12:14 +01:00
Guillaume Lours
c53539e1cc Merge pull request #9906 from glours/profiles-priority
use COMPOSE_PROFILES value only if no command line arg profiles used
2022-11-15 18:03:11 +01:00
Guillaume Lours
8c1e2af3e1 add e2e tests to check build dependency between services
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2022-11-15 14:43:53 +01:00
Guillaume Lours
a9e070206e check if a missing image won't be build via a service declared in depends_on section
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2022-11-15 14:43:53 +01:00
Guillaume Lours
32f29b833f add --no-consistency flag to convert command
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2022-11-15 10:31:59 +01:00
Guillaume Lours
533fc61634 use COMPOSE_PROFILES value only if no command line arg profiles used
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2022-11-15 10:13:17 +01:00
Guillaume Lours
386c3554e5 Merge pull request #9992 from glours/dont-stale-proposal-issues
exclude issues with the `kind/feature` label from stale bot process
2022-11-15 10:12:47 +01:00
dependabot[bot]
bfb9e11fc2 build(deps): bump github.com/containerd/containerd from 1.6.9 to 1.6.10
Bumps [github.com/containerd/containerd](https://github.com/containerd/containerd) from 1.6.9 to 1.6.10.
- [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.9...v1.6.10)

---
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-11-15 09:04:55 +00:00
Guillaume Lours
09e742b33b exclude issues with the kind/feature label from stale bot process
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2022-11-14 11:31:03 +01:00
Guillaume Lours
754376916c Merge pull request #9982 from milas/bump-deps
ci: update dependencies to latest
2022-11-08 15:46:46 +01:00
Milas Bowman
306ae161e1 ci: upgrade to compose-go v1.7.0
https://github.com/compose-spec/compose-go/releases/tag/v1.7.0

Signed-off-by: Milas Bowman <milas.bowman@docker.com>
2022-11-08 09:24:13 -05:00
Milas Bowman
fd4aecefee ci: update dependencies to latest
Signed-off-by: Milas Bowman <milas.bowman@docker.com>
2022-11-08 09:07:27 -05:00
Sebastiaan van Stijn
34e945a598 ci: remove uses of deprecated gotest.tools v2 (#9935)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-11-07 14:07:41 -05:00
Laura Brehm
df9e605b31 Merge pull request #9947 from glours/manage-resources-reservations
add support of deploy.reservation.memory
2022-11-02 16:09:14 +01:00
Laura Brehm
6e2e19d621 Merge pull request #9949 from docker/dependabot/go_modules/github.com/containerd/containerd-1.6.9
build(deps): bump github.com/containerd/containerd from 1.6.8 to 1.6.9
2022-11-02 00:02:31 +01:00
Laura Brehm
e189942133 Update e2e module dependencies
Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2022-11-01 22:19:25 +01:00
dependabot[bot]
369e912586 build(deps): bump github.com/containerd/containerd from 1.6.8 to 1.6.9
Bumps [github.com/containerd/containerd](https://github.com/containerd/containerd) from 1.6.8 to 1.6.9.
- [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.8...v1.6.9)

---
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-10-28 10:15:30 +00:00
Guillaume Lours
71b4976e74 Merge pull request #9936 from thaJeztah/update_deps
go.mod: update docker-credential-helpers v0.7.0
2022-10-28 12:13:20 +02:00
Guillaume Lours
bd96d032df Merge branch 'v2' into update_deps
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2022-10-28 12:02:00 +02:00
Guillaume Lours
5a1f64532d Merge pull request #9944 from glours/map-spec-restart-policy-to-engine
map deploy.restart_policy.condition to engine values
2022-10-25 09:04:42 +02:00
Guillaume Lours
7ba9aac5da add support of deploy.reservation.memory
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2022-10-24 22:52:34 +02:00
Laura Brehm
f7961cc722 Merge pull request #9945 from docker/dependabot/go_modules/github.com/stretchr/testify-1.8.1
build(deps): bump github.com/stretchr/testify from 1.8.0 to 1.8.1
2022-10-24 18:36:24 +02:00
Laura Brehm
6d64242f71 Update deps for e2e module
Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2022-10-24 17:20:35 +02:00
Guillaume Lours
eaf27d9dfe map deploy.restart_policy.condition to engine values
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2022-10-24 11:38:49 +02:00
dependabot[bot]
36a9183950 build(deps): bump github.com/stretchr/testify from 1.8.0 to 1.8.1
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.0 to 1.8.1.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.0...v1.8.1)

---
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-10-24 09:27:35 +00:00
Sebastiaan van Stijn
f472ce3493 Merge pull request #9940 from thaJeztah/go1.18_compat
go.mod: docker 5aac513617f072b15322b147052cbda0d451d389 / v22.06-dev
2022-10-21 21:37:27 +02:00
Sebastiaan van Stijn
533abc3b1d go.mod: docker 5aac513617f072b15322b147052cbda0d451d389 / v22.06-dev
This restores compatibility with go1.18, which was broken since commit;
c062238ea4

cmd.Environ() is new in go1.19, and not needed for this specific case.
Without this, trying to use this package in code that uses go1.18 will fail;

    builder/remotecontext/git/gitutils.go:216:23: cmd.Environ undefined (type *exec.Cmd has no field or method Environ)

Changing to use `os.Environ()` instead restores compatibility with go1.18

Full diff: f9cb47a052...5aac513617

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-10-21 20:33:34 +02:00
Guillaume Lours
e8ea3ad29f Merge pull request #9934 from glours/bump-engine-version
update docker engine API to apply fix of CVE-2022-39253
2022-10-21 16:39:46 +02:00
Guillaume Lours
197c16904a update docker engine API to apply fix of CVE-2022-39253
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2022-10-21 16:25:08 +02:00
Sebastiaan van Stijn
c630c8d295 go.mod: update docker-credential-helpers v0.7.0
to match the version used by the cli (the cli doesn't use go.mod, so go modules
doesn't automatically pick that up);

1d6c6e2367/vendor.mod (L14)

Used code doesn't change, but we want to keep the older github.com/danieljoos/wincred v1.1.0
out of the dependency tree :)

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-10-21 15:48:25 +02:00
Sebastiaan van Stijn
41cf5ee3dc go.mod: remove replace for runc
BuildKit and Buildx no longer require this replace rule (it probably only was
needed in buildkit, which used this version to compile).

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-10-21 15:48:25 +02:00
Sebastiaan van Stijn
b7053cad8e go mod: tidy and group "require" blocks, update comments
The file had multiple "requires" blocks, which made it harder to find which
dependencies were used. Some direct modules also were in the "indirect" block.

While updating, also updated some comments.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-10-21 15:48:19 +02:00
Laura Brehm
b37a6c7f23 Merge pull request #9937 from docker/dependabot/go_modules/go.opentelemetry.io/otel-1.11.1
build(deps): bump go.opentelemetry.io/otel from 1.11.0 to 1.11.1
2022-10-21 13:31:08 +02:00
Laura Brehm
717ace9990 Update e2e module deps
Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2022-10-21 13:14:47 +02:00
dependabot[bot]
8bdfc62785 build(deps): bump go.opentelemetry.io/otel from 1.11.0 to 1.11.1
Bumps [go.opentelemetry.io/otel](https://github.com/open-telemetry/opentelemetry-go) from 1.11.0 to 1.11.1.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.11.0...v1.11.1)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-21 09:36:57 +00:00
Laura Brehm
2978f1a0bc Merge pull request #9933 from laurazard/skip-flaky-test
Skip flaky test in CI `merge` workflow
2022-10-20 18:41:24 +02:00
Laura Brehm
dd13299ede Skip flaky test in CI
Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2022-10-20 18:30:27 +02:00
Guillaume Lours
32ae036fd0 Merge pull request #9925 from glours/update-docker-dependencies
bump docker dependencies version
2022-10-18 17:47:21 +02:00
Guillaume Lours
3f0550f884 log the error object instead of the string message only
Co-authored-by: Nick Sieger <nicksieger@gmail.com>
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2022-10-18 17:34:16 +02:00
Guillaume Lours
18ce1f41b7 replace deprecated functions
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2022-10-18 16:57:53 +02:00
Guillaume Lours
3bf29d401c bump docker dependencies version
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2022-10-18 16:26:54 +02:00
Laura Brehm
c384905d70 Merge pull request #9926 from laurazard/fix-makefile-modules-target
Fix Makefile target `validate-go-mod`
2022-10-18 16:09:43 +02:00
Laura Brehm
7424a3d3c1 Fix Makefile target validate-go-mod to only run correct bakefile target
Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2022-10-18 15:38:41 +02:00
Laura Brehm
7c0b8a4c96 Merge pull request #9912 from docker/dependabot/go_modules/go.opentelemetry.io/otel-1.11.0
build(deps): bump go.opentelemetry.io/otel from 1.10.0 to 1.11.0
2022-10-18 13:53:07 +02:00
Laura Brehm
6b7e9466c4 Update e2e module deps
Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2022-10-18 13:41:47 +02:00
Ulysses Souza
a6dd996988 Merge pull request #9823 from ulyssessouza/add-codecov
Add Codecov
2022-10-17 15:56:59 +02:00
Ulysses Souza
91eae4f035 Add Codecov
Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
2022-10-17 15:32:51 +02:00
Tiago Silva
8b89721476 port: fix container name in error message (#9909)
The error message is using V1 separator hardcoded, it should be using the configured separator value.

Signed-off-by: Tiago Silva <Tiago.MB.Silva@edu.azores.gov.pt>
2022-10-13 14:47:35 -04:00
Guillaume Lours
3892e9cbc4 Merge pull request #9887 from milas/issue-template
github: switch to issue template form
2022-10-13 20:43:33 +02:00
Milas Bowman
f43a1e3ece github: add feature request template
Signed-off-by: Milas Bowman <milas.bowman@docker.com>
2022-10-13 14:31:11 -04:00
Milas Bowman
fa1ae635d1 github: switch to issue template form
Migrate the existing template into the new format and streamline
it a bit.

Signed-off-by: Milas Bowman <milas.bowman@docker.com>
2022-10-13 14:31:11 -04:00
dependabot[bot]
afc0263f5c build(deps): bump go.opentelemetry.io/otel from 1.10.0 to 1.11.0
Bumps [go.opentelemetry.io/otel](https://github.com/open-telemetry/opentelemetry-go) from 1.10.0 to 1.11.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.10.0...v1.11.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-13 09:33:25 +00:00
Laura Brehm
b15df818c7 Merge pull request #9908 from docker/dependabot/go_modules/github.com/spf13/cobra-1.6.0
build(deps): bump github.com/spf13/cobra from 1.5.0 to 1.6.0
2022-10-12 18:23:35 +02:00
Laura Brehm
bb002a7688 Update e2e mod dependencies
Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2022-10-12 18:12:11 +02:00
dependabot[bot]
2ccd57e01a build(deps): bump github.com/spf13/cobra from 1.5.0 to 1.6.0
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.5.0 to 1.6.0.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Commits](https://github.com/spf13/cobra/compare/v1.5.0...v1.6.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-10-12 10:00:55 +00:00
Nick Sieger
1c14d30777 Merge pull request #9168 from KoditkarVedant/9089-add-support-to-docker-compose-push-quiet-option
Add support to push images quietly via compose cli 🤫
2022-10-11 17:05:17 -04:00
Nick Sieger
8bd487ac43 docs: update with result of make docs
Signed-off-by: Nick Sieger <nick@nicksieger.com>
2022-10-11 15:19:24 -05:00
Vedant Koditkar
1d4cb32001 Add support to push images quietly via compose cli
Signed-off-by: Vedant Koditkar <vedant.koditkar@outlook.com>
2022-10-11 15:19:23 -05:00
Laura Brehm
19d1ab77eb Merge pull request #9905 from docker/dependabot/go_modules/gotest.tools/v3-3.4.0
build(deps): bump gotest.tools/v3 from 3.3.0 to 3.4.0
2022-10-10 15:32:42 +02:00
Laura Brehm
a01f62f5dc Bump e2e module deps
Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2022-10-10 15:23:04 +02:00
dependabot[bot]
045f5ad758 build(deps): bump gotest.tools/v3 from 3.3.0 to 3.4.0
Bumps [gotest.tools/v3](https://github.com/gotestyourself/gotest.tools) from 3.3.0 to 3.4.0.
- [Release notes](https://github.com/gotestyourself/gotest.tools/releases)
- [Commits](https://github.com/gotestyourself/gotest.tools/compare/v3.3.0...v3.4.0)

---
updated-dependencies:
- dependency-name: gotest.tools/v3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-10 09:33:24 +00:00
Guillaume Lours
b6b58d26c1 don't fail when trying to remove an orphan container during down command
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2022-10-06 16:44:37 +02:00
Guillaume Lours
55b1b9976b Merge pull request #9894 from thaJeztah/bump_go_1.19.2
Update to go 1.19.2 to address CVE-2022-2879, CVE-2022-2880, CVE-2022-41715
2022-10-04 23:10:38 +02:00
Sebastiaan van Stijn
34441c8e4a Update to go 1.19.2 to address CVE-2022-2879, CVE-2022-2880, CVE-2022-41715
From the mailing list:

We have just released Go versions 1.19.2 and 1.18.7, minor point releases.

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

- archive/tar: unbounded memory consumption when reading headers

  Reader.Read did not set a limit on the maximum size of file headers.
  A maliciously crafted archive could cause Read to allocate unbounded
  amounts of memory, potentially causing resource exhaustion or panics.
  Reader.Read now limits the maximum size of header blocks to 1 MiB.

  Thanks to Adam Korczynski (ADA Logics) and OSS-Fuzz for reporting this issue.

  This is CVE-2022-2879 and Go issue https://go.dev/issue/54853.

- net/http/httputil: ReverseProxy should not forward unparseable query parameters

  Requests forwarded by ReverseProxy included the raw query parameters from the
  inbound request, including unparseable parameters rejected by net/http. This
  could permit query parameter smuggling when a Go proxy forwards a parameter
  with an unparseable value.

  ReverseProxy will now sanitize the query parameters in the forwarded query
  when the outbound request's Form field is set after the ReverseProxy.Director
  function returns, indicating that the proxy has parsed the query parameters.
  Proxies which do not parse query parameters continue to forward the original
  query parameters unchanged.

  Thanks to Gal Goldstein (Security Researcher, Oxeye) and
  Daniel Abeles (Head of Research, Oxeye) for reporting this issue.

  This is CVE-2022-2880 and Go issue https://go.dev/issue/54663.

- regexp/syntax: limit memory used by parsing regexps

  The parsed regexp representation is linear in the size of the input,
  but in some cases the constant factor can be as high as 40,000,
  making relatively small regexps consume much larger amounts of memory.

  Each regexp being parsed is now limited to a 256 MB memory footprint.
  Regular expressions whose representation would use more space than that
  are now rejected. Normal use of regular expressions is unaffected.

  Thanks to Adam Korczynski (ADA Logics) and OSS-Fuzz for reporting this issue.

  This is CVE-2022-41715 and Go issue https://go.dev/issue/55949.

View the release notes for more information: https://go.dev/doc/devel/release#go1.19.2

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-10-04 21:27:06 +02:00
Guillaume Lours
139a6945cb Merge pull request #9886 from milas/ci-docs-repo
ci: update docs repo path
2022-09-29 19:20:49 +02:00
Milas Bowman
97a9d02dda ci: update docs repo path
The Docker docs now live at `docker/docs` instead of
`docker/docker.github.io`.

Signed-off-by: Milas Bowman <milas.bowman@docker.com>
2022-09-29 13:09:25 -04:00
Laura Brehm
25c4bcef85 Merge pull request #9824 from laurazard/cucumber-test
🥒 Cucumber PoC 🥒
2022-09-27 23:38:44 +02:00
Laura Brehm
4607dac19c Adjust modules sync validating script
Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2022-09-27 15:47:00 +02:00
Laura Brehm
c1f475d7bd Add validate-modules target to CI matrix
Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2022-09-27 02:36:15 +02:00
Laura Brehm
c6109b2e5c Add Makefile, buildx target to ensure root and e2e go.mod are kept in sync
Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2022-09-27 02:35:57 +02:00
Laura Brehm
fffe7fff57 Create new e2e module to separate out test dependencies, move cucumber tests
Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2022-09-27 02:13:52 +02:00
Laura Brehm
0a5f4e62e4 Removed tests that were replaced by Cucumber features
Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2022-09-27 02:13:52 +02:00
Laura Brehm
d88f6805e7 Update go.mod replace
Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2022-09-27 02:13:52 +02:00
Laura Brehm
266ab22d53 Rename start cucumber feature
Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2022-09-27 02:13:51 +02:00
Laura Brehm
a7476c8eeb Convert cascade_stop_test.go into a cucumber feature stop.feature
Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2022-09-27 02:13:51 +02:00
Laura Brehm
15ebff00b1 Cucumber test setup/fixtures
(run with `go test -v -run ^TestCucumberFeatures$ github.com/docker/compose/v2/pkg/e2e/cucumber`)

Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2022-09-27 02:13:47 +02:00
140 changed files with 2054 additions and 2278 deletions

View File

@@ -1,64 +0,0 @@
<!--
If you are reporting a new issue, make sure that we do not have any duplicates
already open. You can ensure this by searching the issue list for this
repository. If there is a duplicate, please close your issue and add a comment
to the existing issue instead.
If you suspect your issue is a bug, please edit your issue description to
include the BUG REPORT INFORMATION shown below. If you fail to provide this
information within 7 days, we cannot debug your issue and will close it. We
will, however, reopen it if you later provide the information.
For more information about reporting issues, see
https://github.com/docker/compose-cli/blob/master/CONTRIBUTING.md#reporting-other-issues
---------------------------------------------------
GENERAL SUPPORT INFORMATION
---------------------------------------------------
The GitHub issue tracker is for bug reports and feature requests.
General support can be found at the following locations:
- Docker Support Forums - https://forums.docker.com
- Docker Community Slack - https://dockr.ly/slack
- Post a question on StackOverflow, using the Docker tag
---------------------------------------------------
BUG REPORT INFORMATION
---------------------------------------------------
Use the commands below to provide key information from your environment:
You do NOT have to include this information if this is a FEATURE REQUEST
-->
**Description**
<!--
Briefly describe the problem you are having in a few paragraphs.
-->
**Steps to reproduce the issue:**
1.
2.
3.
**Describe the results you received:**
**Describe the results you expected:**
**Additional information you deem important (e.g. issue happens only occasionally):**
**Output of `docker compose version`:**
```
(paste your output here)
```
**Output of `docker info`:**
```
(paste your output here)
```
**Additional environment details:**

49
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,49 @@
name: 🐞 Bug
description: File a bug/issue
title: "[BUG] <title>"
labels: ['status/0-triage', 'kind/bug']
body:
- type: textarea
attributes:
label: Description
description: |
Briefly describe the problem you are having.
Include both the current behavior (what you are seeing) as well as what you expected to happen.
validations:
required: true
- type: textarea
attributes:
label: Steps To Reproduce
description: Steps to reproduce the behavior.
placeholder: |
1. In this environment...
2. With this config...
3. Run '...'
4. See error...
validations:
required: false
- type: textarea
attributes:
label: Compose Version
description: |
Paste output of `docker compose version` and `docker-compose version`.
render: Text
validations:
required: false
- type: textarea
attributes:
label: Docker Environment
description: Paste output of `docker info`.
render: Text
validations:
required: false
- type: textarea
attributes:
label: Anything else?
description: |
Links? References? Anything that will give us more context about the issue you are encountering!
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
validations:
required: false

11
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,11 @@
blank_issues_enabled: true
contact_links:
- name: Docker Community Slack
url: https://dockr.ly/slack
about: 'Use the #docker-compose channel'
- name: Docker Support Forums
url: https://forums.docker.com/c/open-source-projects/compose/15
about: 'Use the "Open Source Projects > Compose" category'
- name: 'Ask on Stack Overflow'
url: https://stackoverflow.com/questions/tagged/docker-compose
about: 'Use the [docker-compose] tag when creating new questions'

View File

@@ -0,0 +1,13 @@
name: Feature request
description: Missing functionality? Come tell us about it!
labels:
- kind/feature
- status/0-triage
body:
- type: textarea
id: description
attributes:
label: Description
description: What is the feature you want to see?
validations:
required: true

View File

@@ -3,4 +3,4 @@
**Related issue**
<!-- If this is a bug fix, make sure your description includes "fixes #xxxx", or "closes #xxxx" -->
**(not mandatory) A picture of a cute animal, if possible in relation with what you did**
**(not mandatory) A picture of a cute animal, if possible in relation to what you did**

2
.github/stale.yml vendored
View File

@@ -12,7 +12,7 @@ onlyLabels: []
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
exemptLabels:
- "enhancement ✨"
- "kind/feature"
# Set to true to ignore issues in a project (defaults to false)
exemptProjects: false

View File

@@ -176,6 +176,9 @@ jobs:
if: ${{ matrix.mode == 'plugin' }}
run: |
make e2e-compose
-
name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
-
name: Test standalone mode
if: ${{ matrix.mode == 'standalone' }}

View File

@@ -18,8 +18,8 @@ jobs:
uses: actions/checkout@v3
with:
token: ${{ secrets.GHPAT_DOCS_DISPATCH }}
repository: docker/docker.github.io
ref: master
repository: docker/docs
ref: main
-
name: Prepare
run: |

54
.github/workflows/scorecards.yml vendored Normal file
View File

@@ -0,0 +1,54 @@
name: Scorecards supply-chain security
on:
# Only the default branch is supported.
branch_protection_rule:
schedule:
- cron: '44 9 * * 4'
push:
branches: [ "v2" ]
# Declare default permissions as read only.
permissions: read-all
jobs:
analysis:
name: Scorecards analysis
runs-on: ubuntu-latest
permissions:
# Needed to upload the results to code-scanning dashboard.
security-events: write
# Used to receive a badge.
id-token: write
steps:
- name: "Checkout code"
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 # tag=v3.0.0
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@99c53751e09b9529366343771cc321ec74e9bd3d # tag=v2.0.6
with:
results_file: results.sarif
results_format: sarif
# Publish the results for public repositories to enable scorecard badges. For more details, see
# https://github.com/ossf/scorecard-action#publishing-results.
# For private repositories, `publish_results` will automatically be set to `false`, regardless
# of the value entered here.
publish_results: true
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # tag=v3.0.0
with:
name: SARIF file
path: results.sarif
retention-days: 5
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@5f532563584d71fdef14ee64d17bafb34f751ce5 # tag=v1.0.26
with:
sarif_file: results.sarif

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
bin/
/.vscode/
coverage.out

View File

@@ -11,6 +11,7 @@ linters:
- gocyclo
- gofmt
- goimports
- gomodguard
- revive
- gosimple
- govet
@@ -36,6 +37,12 @@ linters-settings:
# The io/ioutil package has been deprecated.
# https://go.dev/doc/go1.16#ioutil
- io/ioutil
gomodguard:
blocked:
versions:
- gotest.tools:
version: "< 3.0.0"
reason: "deprecated, pre-modules version"
gocritic:
# Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.
# Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags".

View File

@@ -36,7 +36,7 @@ make test
If you need to update a golden file simply do `go test ./... -test.update-golden`.
### End-to-end tests
To run e2e tests, the Compose CLI binary need to be build. All the commands to run e2e tests propose a version
To run e2e tests, the Compose CLI binary needs to be built. All the commands to run e2e tests propose a version
with the prefix `build-and-e2e` to first build the CLI before executing tests.
Note that this requires a local Docker Engine to be running.
@@ -84,8 +84,8 @@ make build-and-e2e-compose-standalone
## Releases
To create a new release:
* Check that the CI is green on the main branch for commit you want to release
* Run the release Github Actions workflow with a tag of the form vx.y.z following existing tags.
* Check that the CI is green on the main branch for the commit you want to release
* Run the release Github Actions workflow with a tag of form vx.y.z following existing tags.
This will automatically create a new tag, release and make binaries for
Windows, macOS, and Linux available for download on the

View File

@@ -2,7 +2,7 @@
Want to hack on Docker? Awesome! We have a contributor's guide that explains
[setting up a Docker development environment and the contribution
process](https://docs.docker.com/opensource/project/who-written-for/).
process](https://docs.docker.com/contribute/overview/).
This page contains information about reporting issues as well as some tips and
guidelines useful to experienced open source contributors. Finally, make sure
@@ -11,23 +11,31 @@ start participating.
## Topics
* [Reporting Security Issues](#reporting-security-issues)
* [Design and Cleanup Proposals](#design-and-cleanup-proposals)
* [Reporting Issues](#reporting-other-issues)
* [Quick Contribution Tips and Guidelines](#quick-contribution-tips-and-guidelines)
* [Community Guidelines](#docker-community-guidelines)
- [Contributing to Docker](#contributing-to-docker)
- [Topics](#topics)
- [Reporting security issues](#reporting-security-issues)
- [Reporting other issues](#reporting-other-issues)
- [Quick contribution tips and guidelines](#quick-contribution-tips-and-guidelines)
- [Pull requests are always welcome](#pull-requests-are-always-welcome)
- [Talking to other Docker users and contributors](#talking-to-other-docker-users-and-contributors)
- [Conventions](#conventions)
- [Merge approval](#merge-approval)
- [Sign your work](#sign-your-work)
- [How can I become a maintainer?](#how-can-i-become-a-maintainer)
- [Docker community guidelines](#docker-community-guidelines)
- [Coding Style](#coding-style)
## Reporting security issues
The Docker maintainers take security seriously. If you discover a security
issue, please bring it to their attention right away!
Please **DO NOT** file a public issue, instead send your report privately to
Please **DO NOT** file a public issue, instead, send your report privately to
[security@docker.com](mailto:security@docker.com).
Security reports are greatly appreciated and we will publicly thank you for it.
Security reports are greatly appreciated and we will publicly thank you for them.
We also like to send gifts&mdash;if you're into Docker swag, make sure to let
us know. We currently do not offer a paid security bounty program, but are not
us know. We currently do not offer a paid security bounty program but are not
ruling it out in the future.
@@ -39,11 +47,11 @@ and will thank you for it!
Check that [our issue database](https://github.com/docker/compose/labels/Docker%20Compose%20V2)
doesn't already include that problem or suggestion before submitting an issue.
If you find a match, you can use the "subscribe" button to get notified on
If you find a match, you can use the "subscribe" button to get notified of
updates. Do *not* leave random "+1" or "I have this too" comments, as they
only clutter the discussion, and don't help to resolve it. However, if you
have ways to reproduce the issue or have additional information that may help
resolving the issue, please leave a comment.
resolve the issue, please leave a comment.
When reporting issues, always include:
@@ -51,7 +59,7 @@ When reporting issues, always include:
* The output of `docker context show`.
* The output of `docker info`.
Also include the steps required to reproduce the problem if possible and
Also, include the steps required to reproduce the problem if possible and
applicable. This information will help us review and fix your issue faster.
When sending lengthy log files, consider posting them as a gist
(https://gist.github.com).
@@ -124,7 +132,7 @@ Fork the repository and make changes on your fork in a feature branch:
issue.
Submit unit tests for your changes. Go has a great test framework built in; use
it! Take a look at existing tests for inspiration. Also end-to-end tests are
it! Take a look at existing tests for inspiration. Also, end-to-end tests are
available. Run the full test suite, both unit tests and e2e tests on your
branch before submitting a pull request. See [BUILDING.md](BUILDING.md) for
instructions to build and run tests.
@@ -145,7 +153,7 @@ suggested modifications and push additional commits to your feature branch. Post
a comment after pushing. New commits show up in the pull request automatically,
but the reviewers are notified only when you comment.
Pull requests must be cleanly rebased on top of master without multiple branches
Pull requests must be cleanly rebased on top of the base branch without multiple branches
mixed into the PR.
**Git tip**: If your PR no longer merges cleanly, use `rebase master` in your
@@ -165,7 +173,7 @@ changes in the same pull request so that a revert would remove all traces of
the feature or fix.
Include an issue reference like `Closes #XXXX` or `Fixes #XXXX` in the pull
request description that close an issue. Including references automatically
request description that closes an issue. Including references automatically
closes the issue on a merge.
Please do not add yourself to the `AUTHORS` file, as it is regenerated regularly
@@ -256,7 +264,7 @@ your help to keep it that way. To help with this we've come up with some general
guidelines for the community as a whole:
* Be nice: Be courteous, respectful and polite to fellow community members:
no regional, racial, gender, or other abuse will be tolerated. We like
no regional, racial, gender or other abuse will be tolerated. We like
nice people way better than mean ones!
* Encourage diversity and participation: Make everyone in our community feel
@@ -270,10 +278,10 @@ guidelines for the community as a whole:
* Stay on topic: Make sure that you are posting to the correct channel and
avoid off-topic discussions. Remember when you update an issue or respond
to an email you are potentially sending to a large number of people. Please
consider this before you update. Also remember that nobody likes spam.
to an email you are potentially sending it to a large number of people. Please
consider this before you update. Also, remember that nobody likes spam.
* Don't send email to the maintainers: There's no need to send email to the
* Don't send emails to the maintainers: There's no need to send emails to the
maintainers to ask them to investigate an issue or to take a look at a
pull request. Instead of sending an email, GitHub mentions should be
used to ping maintainers to review a pull request, a proposal or an
@@ -287,7 +295,7 @@ to result in a solid, consistent codebase.
It is possible that the code base does not currently comply with these
guidelines. We are not looking for a massive PR that fixes this, since that
goes against the spirit of the guidelines. All new contributions should make a
goes against the spirit of the guidelines. All new contributors should make their
best effort to clean up and make the code base better than they left it.
Obviously, apply your best judgement. Remember, the goal here is to make the
code base easier for humans to navigate and understand. Always keep that in
@@ -301,7 +309,7 @@ The rules:
3. All code should follow the guidelines covered in [Effective
Go](http://golang.org/doc/effective_go.html) and [Go Code Review
Comments](https://github.com/golang/go/wiki/CodeReviewComments).
4. Comment the code. Tell us the why, the history and the context.
4. Include code comments. Tell us the why, the history and the context.
5. Document _all_ declarations and methods, even private ones. Declare
expectations, caveats and anything else that may be important. If a type
gets exported, having the comments already there will ensure it's ready.

View File

@@ -15,7 +15,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
ARG GO_VERSION=1.19.1
ARG GO_VERSION=1.19.4
ARG XX_VERSION=1.1.2
ARG GOLANGCI_LINT_VERSION=v1.49.0
ARG ADDLICENSE_VERSION=v1.0.0
@@ -177,7 +177,7 @@ FROM scratch AS release
COPY --from=releaser /out/ /
# docs-reference is a target used as remote context to update docs on release
# with latest changes on docker.github.io.
# with latest changes on docs.docker.com.
# see open-pr job in .github/workflows/docs.yml for more details
FROM scratch AS docs-reference
COPY docs/reference/*.yaml .

View File

@@ -33,7 +33,8 @@ ifeq ($(DETECTED_OS),Windows)
BINARY_EXT=.exe
endif
TEST_FLAGS?=
TEST_COVERAGE_FLAGS = -race -coverprofile=coverage.out -covermode=atomic
TEST_FLAGS?= -timeout 15m
E2E_TEST?=
ifeq ($(E2E_TEST),)
else
@@ -61,7 +62,7 @@ install: binary
.PHONY: e2e-compose
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
go test $(TEST_FLAGS) $(TEST_COVERAGE_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
@@ -76,6 +77,7 @@ build-and-e2e-compose-standalone: build e2e-compose-standalone ## Compile the co
.PHONY: mocks
mocks:
mockgen --version >/dev/null 2>&1 || go install github.com/golang/mock/mockgen@v1.6.0
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
@@ -130,7 +132,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
$(BUILDX_CMD) bake vendor-validate
validate: validate-go-mod validate-headers validate-docs ## Validate sources
validate: validate-go-mod validate-headers validate-docs ## Validate sources
pre-commit: validate check-dependencies lint build test e2e-compose

View File

@@ -1,15 +1,24 @@
# Table of Contents
- [Docker Compose v2](#docker-compose-v2)
- [About update and backward compatibility](#about-update-and-backward-compatibility)
- [Where to get Docker Compose](#where-to-get-docker-compose)
+ [Windows and macOS](#windows-and-macos)
+ [Linux](#linux)
- [Quick Start](#quick-start)
- [Contributing](#contributing)
# Docker Compose v2
[![GitHub release](https://img.shields.io/github/release/docker/compose.svg?style=flat-square)](https://github.com/docker/compose/releases/latest)
[![PkgGoDev](https://img.shields.io/badge/go.dev-docs-007d9c?style=flat-square&logo=go&logoColor=white)](https://pkg.go.dev/github.com/docker/compose/v2)
[![Build Status](https://img.shields.io/github/workflow/status/docker/compose/ci?label=ci&logo=github&style=flat-square)](https://github.com/docker/compose/actions?query=workflow%3Aci)
[![Go Report Card](https://goreportcard.com/badge/github.com/docker/compose/v2?style=flat-square)](https://goreportcard.com/report/github.com/docker/compose/v2)
[![Codecov](https://codecov.io/gh/docker/compose/branch/master/graph/badge.svg?token=HP3K4Y4ctu)](https://codecov.io/gh/docker/compose)
[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/docker/compose/badge)](https://api.securityscorecards.dev/projects/github.com/docker/compose)
![Docker Compose](logo.png?raw=true "Docker Compose Logo")
Docker Compose is a tool for running multi-container applications on Docker
defined using the [Compose file format](https://compose-spec.io).
A Compose file is used to define how the one or more containers that make up
A Compose file is used to define how one or more containers that make up
your application are configured.
Once you have a Compose file, you can create and start your application with a
single command: `docker compose up`.
@@ -33,7 +42,7 @@ for Windows and macOS.
You can download Docker Compose binaries from the
[release page](https://github.com/docker/compose/releases) on this repository.
Rename the relevant binary for your OS to `docker-compose` and copy it to `$HOME/.docker/cli-plugins`
Rename the relevant binary for your OS to `docker-compose` and copy it to `$HOME/.docker/cli-plugins`
Or copy it into one of these folders to install it system-wide:
@@ -46,7 +55,7 @@ Or copy it into one of these folders to install it system-wide:
Quick Start
-----------
Using Docker Compose is basically a three-step process:
Using Docker Compose is a three-step process:
1. Define your app's environment with a `Dockerfile` so it can be
reproduced anywhere.
2. Define the services that make up your app in `docker-compose.yml` so

View File

@@ -26,6 +26,7 @@ import (
"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/progress"
"github.com/docker/compose/v2/pkg/utils"
"github.com/spf13/cobra"
@@ -33,7 +34,7 @@ import (
)
type buildOptions struct {
*projectOptions
*ProjectOptions
composeOptions
quiet bool
pull bool
@@ -72,9 +73,9 @@ var printerModes = []string{
buildx.PrinterModeQuiet,
}
func buildCommand(p *projectOptions, backend api.Service) *cobra.Command {
func buildCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
opts := buildOptions{
projectOptions: p,
ProjectOptions: p,
}
cmd := &cobra.Command{
Use: "build [OPTIONS] [SERVICE...]",
@@ -100,6 +101,9 @@ func buildCommand(p *projectOptions, backend api.Service) *cobra.Command {
if cmd.Flags().Changed("ssh") && opts.ssh == "" {
opts.ssh = "default"
}
if progress.Mode == progress.ModePlain && !cmd.Flags().Changed("progress") {
opts.progress = buildx.PrinterModePlain
}
return runBuild(ctx, backend, opts, args)
}),
ValidArgsFunction: completeServiceNames(p),
@@ -125,7 +129,7 @@ func buildCommand(p *projectOptions, backend api.Service) *cobra.Command {
}
func runBuild(ctx context.Context, backend api.Service, opts buildOptions, services []string) error {
project, err := opts.toProject(services, cli.WithResolvedPaths(true))
project, err := opts.ToProject(services, cli.WithResolvedPaths(true))
if err != nil {
return err
}

View File

@@ -32,9 +32,9 @@ func noCompletion() validArgsFn {
}
}
func completeServiceNames(p *projectOptions) validArgsFn {
func completeServiceNames(p *ProjectOptions) validArgsFn {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
project, err := p.toProject(nil)
project, err := p.ToProject(nil)
if err != nil {
return nil, cobra.ShellCompDirectiveNoFileComp
}

View File

@@ -91,7 +91,7 @@ func Adapt(fn Command) func(cmd *cobra.Command, args []string) error {
})
}
type projectOptions struct {
type ProjectOptions struct {
ProjectName string
Profiles []string
ConfigPaths []string
@@ -108,16 +108,16 @@ type ProjectFunc func(ctx context.Context, project *types.Project) error
type ProjectServicesFunc func(ctx context.Context, project *types.Project, services []string) error
// WithProject creates a cobra run command from a ProjectFunc based on configured project options and selected services
func (o *projectOptions) WithProject(fn ProjectFunc) func(cmd *cobra.Command, args []string) error {
func (o *ProjectOptions) WithProject(fn ProjectFunc) func(cmd *cobra.Command, args []string) error {
return o.WithServices(func(ctx context.Context, project *types.Project, services []string) error {
return fn(ctx, project)
})
}
// WithServices creates a cobra run command from a ProjectFunc based on configured project options and selected services
func (o *projectOptions) WithServices(fn ProjectServicesFunc) func(cmd *cobra.Command, args []string) error {
func (o *ProjectOptions) WithServices(fn ProjectServicesFunc) func(cmd *cobra.Command, args []string) error {
return Adapt(func(ctx context.Context, args []string) error {
project, err := o.toProject(args, cli.WithResolvedPaths(true))
project, err := o.ToProject(args, cli.WithResolvedPaths(true))
if err != nil {
return err
}
@@ -126,7 +126,7 @@ func (o *projectOptions) WithServices(fn ProjectServicesFunc) func(cmd *cobra.Co
})
}
func (o *projectOptions) addProjectFlags(f *pflag.FlagSet) {
func (o *ProjectOptions) addProjectFlags(f *pflag.FlagSet) {
f.StringArrayVar(&o.Profiles, "profile", []string{}, "Specify a profile to enable")
f.StringVarP(&o.ProjectName, "project-name", "p", "", "Project name")
f.StringArrayVarP(&o.ConfigPaths, "file", "f", []string{}, "Compose configuration files")
@@ -137,11 +137,11 @@ func (o *projectOptions) addProjectFlags(f *pflag.FlagSet) {
_ = f.MarkHidden("workdir")
}
func (o *projectOptions) projectOrName() (*types.Project, string, error) {
func (o *ProjectOptions) projectOrName(services ...string) (*types.Project, string, error) {
name := o.ProjectName
var project *types.Project
if o.ProjectName == "" {
p, err := o.toProject(nil)
if len(o.ConfigPaths) > 0 || o.ProjectName == "" {
p, err := o.ToProject(services)
if err != nil {
envProjectName := os.Getenv("COMPOSE_PROJECT_NAME")
if envProjectName != "" {
@@ -155,7 +155,7 @@ func (o *projectOptions) projectOrName() (*types.Project, string, error) {
return project, name, nil
}
func (o *projectOptions) toProjectName() (string, error) {
func (o *ProjectOptions) toProjectName() (string, error) {
if o.ProjectName != "" {
return o.ProjectName, nil
}
@@ -165,14 +165,14 @@ func (o *projectOptions) toProjectName() (string, error) {
return envProjectName, nil
}
project, err := o.toProject(nil)
project, err := o.ToProject(nil)
if err != nil {
return "", err
}
return project.Name, nil
}
func (o *projectOptions) toProject(services []string, po ...cli.ProjectOptionsFn) (*types.Project, error) {
func (o *ProjectOptions) ToProject(services []string, po ...cli.ProjectOptionsFn) (*types.Project, error) {
options, err := o.toProjectOptions(po...)
if err != nil {
return nil, compose.WrapComposeError(err)
@@ -187,13 +187,6 @@ func (o *projectOptions) toProject(services []string, po ...cli.ProjectOptionsFn
return nil, compose.WrapComposeError(err)
}
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,
@@ -203,12 +196,16 @@ func (o *projectOptions) toProject(services []string, po ...cli.ProjectOptionsFn
api.ConfigFilesLabel: strings.Join(project.ComposeFiles, ","),
api.OneoffLabel: "False", // default, will be overridden by `run` command
}
if ef != "" {
s.CustomLabels[api.EnvironmentFileLabel] = ef
if o.EnvFile != "" {
s.CustomLabels[api.EnvironmentFileLabel] = o.EnvFile
}
project.Services[i] = s
}
if profiles, ok := options.Environment["COMPOSE_PROFILES"]; ok && len(o.Profiles) == 0 {
o.Profiles = append(o.Profiles, strings.Split(profiles, ",")...)
}
if len(services) > 0 {
s, err := project.GetServices(services...)
if err != nil {
@@ -217,10 +214,6 @@ func (o *projectOptions) toProject(services []string, po ...cli.ProjectOptionsFn
o.Profiles = append(o.Profiles, s.GetProfiles()...)
}
if profiles, ok := options.Environment["COMPOSE_PROFILES"]; ok {
o.Profiles = append(o.Profiles, strings.Split(profiles, ",")...)
}
project.ApplyProfiles(o.Profiles)
project.WithoutUnnecessaryResources()
@@ -229,7 +222,7 @@ func (o *projectOptions) toProject(services []string, po ...cli.ProjectOptionsFn
return project, err
}
func (o *projectOptions) toProjectOptions(po ...cli.ProjectOptionsFn) (*cli.ProjectOptions, error) {
func (o *ProjectOptions) toProjectOptions(po ...cli.ProjectOptionsFn) (*cli.ProjectOptions, error) {
return cli.NewProjectOptions(o.ConfigPaths,
append(po,
cli.WithWorkingDirectory(o.ProjectDir),
@@ -250,7 +243,7 @@ func RunningAsStandalone() bool {
}
// RootCommand returns the compose command with its child commands
func RootCommand(dockerCli command.Cli, backend api.Service) *cobra.Command {
func RootCommand(dockerCli command.Cli, backend api.Service) *cobra.Command { //nolint:gocyclo
// filter out useless commandConn.CloseWrite warning message that can occur
// when using a remote context that is unreachable: "commandConn.CloseWrite: commandconn: failed to wait: signal: killed"
// https://github.com/docker/cli/blob/e1f24d3c93df6752d3c27c8d61d18260f141310c/cli/connhelper/commandconn/commandconn.go#L203-L215
@@ -261,12 +254,13 @@ func RootCommand(dockerCli command.Cli, backend api.Service) *cobra.Command {
"commandConn.CloseRead:",
))
opts := projectOptions{}
opts := ProjectOptions{}
var (
ansi string
noAnsi bool
verbose bool
version bool
ansi string
noAnsi bool
verbose bool
version bool
parallel int
)
c := &cobra.Command{
Short: "Docker Compose",
@@ -325,6 +319,15 @@ func RootCommand(dockerCli command.Cli, backend api.Service) *cobra.Command {
opts.ProjectDir = opts.WorkDir
fmt.Fprint(os.Stderr, aec.Apply("option '--workdir' is DEPRECATED at root level! Please use '--project-directory' instead.\n", aec.RedF))
}
if opts.EnvFile != "" && !filepath.IsAbs(opts.EnvFile) {
opts.EnvFile, err = filepath.Abs(opts.EnvFile)
if err != nil {
return err
}
}
if parallel > 0 {
backend.MaxConcurrency(parallel)
}
return nil
},
}
@@ -370,6 +373,7 @@ func RootCommand(dockerCli command.Cli, backend api.Service) *cobra.Command {
)
c.Flags().StringVar(&ansi, "ansi", "auto", `Control when to print ANSI control characters ("never"|"always"|"auto")`)
c.Flags().IntVar(&parallel, "parallel", -1, `Control max parallelism, -1 for unlimited`)
c.Flags().BoolVarP(&version, "version", "v", false, "Show the Docker Compose version information")
c.Flags().MarkHidden("version") //nolint:errcheck
c.Flags().BoolVar(&noAnsi, "no-ansi", false, `Do not print ANSI control characters (DEPRECATED)`)
@@ -379,7 +383,7 @@ func RootCommand(dockerCli command.Cli, backend api.Service) *cobra.Command {
return c
}
func setEnvWithDotEnv(prjOpts *projectOptions) error {
func setEnvWithDotEnv(prjOpts *ProjectOptions) error {
options, err := prjOpts.toProjectOptions()
if err != nil {
return compose.WrapComposeError(err)

View File

@@ -26,12 +26,8 @@ import (
"sort"
"strings"
"github.com/cnabio/cnab-to-oci/remotes"
"github.com/compose-spec/compose-go/cli"
"github.com/compose-spec/compose-go/types"
"github.com/distribution/distribution/v3/reference"
cliconfig "github.com/docker/cli/cli/config"
"github.com/opencontainers/go-digest"
"github.com/spf13/cobra"
"github.com/docker/compose/v2/pkg/api"
@@ -39,7 +35,7 @@ import (
)
type convertOptions struct {
*projectOptions
*ProjectOptions
Format string
Output string
quiet bool
@@ -51,11 +47,12 @@ type convertOptions struct {
profiles bool
images bool
hash string
noConsistency bool
}
func convertCommand(p *projectOptions, backend api.Service) *cobra.Command {
func convertCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
opts := convertOptions{
projectOptions: p,
ProjectOptions: p,
}
cmd := &cobra.Command{
Aliases: []string{"config"},
@@ -101,6 +98,7 @@ func convertCommand(p *projectOptions, backend api.Service) *cobra.Command {
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Only validate the configuration, don't print anything.")
flags.BoolVar(&opts.noInterpolate, "no-interpolate", false, "Don't interpolate environment variables.")
flags.BoolVar(&opts.noNormalize, "no-normalize", false, "Don't normalize compose model.")
flags.BoolVar(&opts.noConsistency, "no-consistency", false, "Don't check model consistency - warning: may produce invalid Compose output")
flags.BoolVar(&opts.services, "services", false, "Print the service names, one per line.")
flags.BoolVar(&opts.volumes, "volumes", false, "Print the volume names, one per line.")
@@ -114,32 +112,20 @@ func convertCommand(p *projectOptions, backend api.Service) *cobra.Command {
func runConvert(ctx context.Context, backend api.Service, opts convertOptions, services []string) error {
var content []byte
project, err := opts.toProject(services,
project, err := opts.ToProject(services,
cli.WithInterpolation(!opts.noInterpolate),
cli.WithResolvedPaths(true),
cli.WithNormalization(!opts.noNormalize),
cli.WithConsistency(!opts.noConsistency),
cli.WithDiscardEnvFile)
if err != nil {
return err
}
if opts.resolveImageDigests {
configFile := cliconfig.LoadDefaultConfigFile(os.Stderr)
resolver := remotes.CreateResolver(configFile)
err = project.ResolveImages(func(named reference.Named) (digest.Digest, error) {
_, desc, err := resolver.Resolve(ctx, named.String())
return desc.Digest, err
})
if err != nil {
return err
}
}
content, err = backend.Convert(ctx, project, api.ConvertOptions{
Format: opts.Format,
Output: opts.Output,
Format: opts.Format,
Output: opts.Output,
ResolveImageDigests: opts.resolveImageDigests,
})
if err != nil {
return err
@@ -166,7 +152,7 @@ func runConvert(ctx context.Context, backend api.Service, opts convertOptions, s
}
func runServices(opts convertOptions) error {
project, err := opts.toProject(nil)
project, err := opts.ToProject(nil)
if err != nil {
return err
}
@@ -177,7 +163,7 @@ func runServices(opts convertOptions) error {
}
func runVolumes(opts convertOptions) error {
project, err := opts.toProject(nil)
project, err := opts.ToProject(nil)
if err != nil {
return err
}
@@ -192,7 +178,7 @@ func runHash(opts convertOptions) error {
if opts.hash != "*" {
services = append(services, strings.Split(opts.hash, ",")...)
}
project, err := opts.toProject(services)
project, err := opts.ToProject(services)
if err != nil {
return err
}
@@ -208,7 +194,7 @@ func runHash(opts convertOptions) error {
func runProfiles(opts convertOptions, services []string) error {
set := map[string]struct{}{}
project, err := opts.toProject(services)
project, err := opts.ToProject(services)
if err != nil {
return err
}
@@ -229,7 +215,7 @@ func runProfiles(opts convertOptions, services []string) error {
}
func runConfigImages(opts convertOptions, services []string) error {
project, err := opts.toProject(services)
project, err := opts.ToProject(services)
if err != nil {
return err
}
@@ -237,7 +223,7 @@ func runConfigImages(opts convertOptions, services []string) error {
if s.Image != "" {
fmt.Println(s.Image)
} else {
fmt.Printf("%s_%s\n", project.Name, s.Name)
fmt.Printf("%s%s%s\n", project.Name, api.Separator, s.Name)
}
}
return nil

View File

@@ -27,7 +27,7 @@ import (
)
type copyOptions struct {
*projectOptions
*ProjectOptions
source string
destination string
@@ -37,9 +37,9 @@ type copyOptions struct {
copyUIDGID bool
}
func copyCommand(p *projectOptions, backend api.Service) *cobra.Command {
func copyCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
opts := copyOptions{
projectOptions: p,
ProjectOptions: p,
}
copyCmd := &cobra.Command{
Use: `cp [OPTIONS] SERVICE:SRC_PATH DEST_PATH|-

View File

@@ -43,7 +43,7 @@ type createOptions struct {
quietPull bool
}
func createCommand(p *projectOptions, backend api.Service) *cobra.Command {
func createCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
opts := createOptions{}
cmd := &cobra.Command{
Use: "create [OPTIONS] [SERVICE...]",

View File

@@ -31,7 +31,7 @@ import (
)
type downOptions struct {
*projectOptions
*ProjectOptions
removeOrphans bool
timeChanged bool
timeout int
@@ -39,9 +39,9 @@ type downOptions struct {
images string
}
func downCommand(p *projectOptions, backend api.Service) *cobra.Command {
func downCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
opts := downOptions{
projectOptions: p,
ProjectOptions: p,
}
downCmd := &cobra.Command{
Use: "down [OPTIONS]",

View File

@@ -31,10 +31,10 @@ type eventsOpts struct {
json bool
}
func eventsCommand(p *projectOptions, backend api.Service) *cobra.Command {
func eventsCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
opts := eventsOpts{
composeOptions: &composeOptions{
projectOptions: p,
ProjectOptions: p,
},
}
cmd := &cobra.Command{

View File

@@ -43,10 +43,10 @@ type execOpts struct {
interactive bool
}
func execCommand(p *projectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
func execCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
opts := execOpts{
composeOptions: &composeOptions{
projectOptions: p,
ProjectOptions: p,
},
}
runCmd := &cobra.Command{

View File

@@ -34,13 +34,14 @@ import (
)
type imageOptions struct {
*projectOptions
Quiet bool
*ProjectOptions
Quiet bool
Format string
}
func imagesCommand(p *projectOptions, backend api.Service) *cobra.Command {
func imagesCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
opts := imageOptions{
projectOptions: p,
ProjectOptions: p,
}
imgCmd := &cobra.Command{
Use: "images [OPTIONS] [SERVICE...]",
@@ -50,6 +51,7 @@ func imagesCommand(p *projectOptions, backend api.Service) *cobra.Command {
}),
ValidArgsFunction: completeServiceNames(p),
}
imgCmd.Flags().StringVar(&opts.Format, "format", "table", "Format the output. Values: [table | json].")
imgCmd.Flags().BoolVarP(&opts.Quiet, "quiet", "q", false, "Only display IDs")
return imgCmd
}
@@ -88,7 +90,7 @@ func runImages(ctx context.Context, backend api.Service, opts imageOptions, serv
return images[i].ContainerName < images[j].ContainerName
})
return formatter.Print(images, formatter.PRETTY, os.Stdout,
return formatter.Print(images, opts.Format, os.Stdout,
func(w io.Writer) {
for _, img := range images {
id := stringid.TruncateID(img.ID)
@@ -104,5 +106,5 @@ func runImages(ctx context.Context, backend api.Service, opts imageOptions, serv
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", img.ContainerName, repo, tag, id, size)
}
},
"Container", "Repository", "Tag", "Image Id", "Size")
"CONTAINER", "REPOSITORY", "TAG", "IMAGE ID", "SIZE")
}

View File

@@ -27,14 +27,14 @@ import (
)
type killOptions struct {
*projectOptions
*ProjectOptions
removeOrphans bool
signal string
}
func killCommand(p *projectOptions, backend api.Service) *cobra.Command {
func killCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
opts := killOptions{
projectOptions: p,
ProjectOptions: p,
}
cmd := &cobra.Command{
Use: "kill [OPTIONS] [SERVICE...]",
@@ -54,7 +54,7 @@ func killCommand(p *projectOptions, backend api.Service) *cobra.Command {
}
func runKill(ctx context.Context, backend api.Service, opts killOptions, services []string) error {
project, name, err := opts.projectOrName()
project, name, err := opts.projectOrName(services...)
if err != nil {
return err
}
@@ -65,5 +65,4 @@ func runKill(ctx context.Context, backend api.Service, opts killOptions, service
Services: services,
Signal: opts.signal,
})
}

View File

@@ -49,7 +49,7 @@ func listCommand(backend api.Service) *cobra.Command {
Args: cobra.NoArgs,
ValidArgsFunction: noCompletion(),
}
lsCmd.Flags().StringVar(&lsOpts.Format, "format", "pretty", "Format the output. Values: [pretty | json].")
lsCmd.Flags().StringVar(&lsOpts.Format, "format", "table", "Format the output. Values: [table | json].")
lsCmd.Flags().BoolVarP(&lsOpts.Quiet, "quiet", "q", false, "Only display IDs.")
lsCmd.Flags().Var(&lsOpts.Filter, "filter", "Filter output based on conditions provided.")
lsCmd.Flags().BoolVarP(&lsOpts.All, "all", "a", false, "Show all stopped Compose projects")

View File

@@ -20,15 +20,14 @@ import (
"context"
"os"
"github.com/docker/compose/v2/cmd/formatter"
"github.com/spf13/cobra"
"github.com/docker/compose/v2/cmd/formatter"
"github.com/docker/compose/v2/pkg/api"
)
type logsOptions struct {
*projectOptions
*ProjectOptions
composeOptions
follow bool
tail string
@@ -39,9 +38,9 @@ type logsOptions struct {
timestamps bool
}
func logsCommand(p *projectOptions, backend api.Service) *cobra.Command {
func logsCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
opts := logsOptions{
projectOptions: p,
ProjectOptions: p,
}
logsCmd := &cobra.Command{
Use: "logs [OPTIONS] [SERVICE...]",
@@ -63,11 +62,11 @@ func logsCommand(p *projectOptions, backend api.Service) *cobra.Command {
}
func runLogs(ctx context.Context, backend api.Service, opts logsOptions, services []string) error {
project, name, err := opts.projectOrName()
project, name, err := opts.projectOrName(services...)
if err != nil {
return err
}
consumer := formatter.NewLogConsumer(ctx, os.Stdout, !opts.noColor, !opts.noPrefix)
consumer := formatter.NewLogConsumer(ctx, os.Stdout, os.Stderr, !opts.noColor, !opts.noPrefix, false)
return backend.Logs(ctx, name, consumer, api.LogOptions{
Project: project,
Services: services,

View File

@@ -25,12 +25,12 @@ import (
)
type pauseOptions struct {
*projectOptions
*ProjectOptions
}
func pauseCommand(p *projectOptions, backend api.Service) *cobra.Command {
func pauseCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
opts := pauseOptions{
projectOptions: p,
ProjectOptions: p,
}
cmd := &cobra.Command{
Use: "pause [SERVICE...]",
@@ -44,7 +44,7 @@ func pauseCommand(p *projectOptions, backend api.Service) *cobra.Command {
}
func runPause(ctx context.Context, backend api.Service, opts pauseOptions, services []string) error {
project, name, err := opts.projectOrName()
project, name, err := opts.projectOrName(services...)
if err != nil {
return err
}
@@ -56,12 +56,12 @@ func runPause(ctx context.Context, backend api.Service, opts pauseOptions, servi
}
type unpauseOptions struct {
*projectOptions
*ProjectOptions
}
func unpauseCommand(p *projectOptions, backend api.Service) *cobra.Command {
func unpauseCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
opts := unpauseOptions{
projectOptions: p,
ProjectOptions: p,
}
cmd := &cobra.Command{
Use: "unpause [SERVICE...]",
@@ -75,7 +75,7 @@ func unpauseCommand(p *projectOptions, backend api.Service) *cobra.Command {
}
func runUnPause(ctx context.Context, backend api.Service, opts unpauseOptions, services []string) error {
project, name, err := opts.projectOrName()
project, name, err := opts.projectOrName(services...)
if err != nil {
return err
}

View File

@@ -20,6 +20,7 @@ import (
"context"
"fmt"
"strconv"
"strings"
"github.com/spf13/cobra"
@@ -27,26 +28,27 @@ import (
)
type portOptions struct {
*projectOptions
port int
*ProjectOptions
port uint16
protocol string
index int
}
func portCommand(p *projectOptions, backend api.Service) *cobra.Command {
func portCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
opts := portOptions{
projectOptions: p,
ProjectOptions: p,
}
cmd := &cobra.Command{
Use: "port [OPTIONS] SERVICE PRIVATE_PORT",
Short: "Print the public port for a port binding.",
Args: cobra.MinimumNArgs(2),
PreRunE: Adapt(func(ctx context.Context, args []string) error {
port, err := strconv.Atoi(args[1])
port, err := strconv.ParseUint(args[1], 10, 16)
if err != nil {
return err
}
opts.port = port
opts.port = uint16(port)
opts.protocol = strings.ToLower(opts.protocol)
return nil
}),
RunE: Adapt(func(ctx context.Context, args []string) error {

View File

@@ -24,12 +24,14 @@ import (
"sort"
"strconv"
"strings"
"time"
"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/docker/go-units"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@@ -37,7 +39,7 @@ import (
)
type psOptions struct {
*projectOptions
*ProjectOptions
Format string
All bool
Quiet bool
@@ -65,9 +67,9 @@ func (p *psOptions) parseFilter() error {
return nil
}
func psCommand(p *projectOptions, backend api.Service) *cobra.Command {
func psCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
opts := psOptions{
projectOptions: p,
ProjectOptions: p,
}
psCmd := &cobra.Command{
Use: "ps [OPTIONS] [SERVICE...]",
@@ -81,7 +83,7 @@ func psCommand(p *projectOptions, backend api.Service) *cobra.Command {
ValidArgsFunction: completeServiceNames(p),
}
flags := psCmd.Flags()
flags.StringVar(&opts.Format, "format", "pretty", "Format the output. Values: [pretty | json]")
flags.StringVar(&opts.Format, "format", "table", "Format the output. Values: [table | json]")
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")
@@ -91,7 +93,7 @@ func psCommand(p *projectOptions, backend api.Service) *cobra.Command {
}
func runPs(ctx context.Context, backend api.Service, services []string, opts psOptions) error {
project, name, err := opts.projectOrName()
project, name, err := opts.projectOrName(services...)
if err != nil {
return err
}
@@ -142,21 +144,18 @@ SERVICES:
return formatter.Print(containers, opts.Format, os.Stdout,
writer(containers),
"NAME", "COMMAND", "SERVICE", "STATUS", "PORTS")
"NAME", "IMAGE", "COMMAND", "SERVICE", "CREATED", "STATUS", "PORTS")
}
func writer(containers []api.ContainerSummary) func(w io.Writer) {
return func(w io.Writer) {
for _, container := range containers {
ports := displayablePorts(container)
status := container.State
if status == "running" && container.Health != "" {
status = fmt.Sprintf("%s (%s)", container.State, container.Health)
} else if status == "exited" || status == "dead" {
status = fmt.Sprintf("%s (%d)", container.State, container.ExitCode)
}
createdAt := time.Unix(container.Created, 0)
created := units.HumanDuration(time.Now().UTC().Sub(createdAt)) + " ago"
status := container.Status
command := formatter2.Ellipsis(container.Command, 20)
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", container.Name, strconv.Quote(command), container.Service, status, ports)
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", container.Name, container.Image, strconv.Quote(command), container.Service, created, status, ports)
}
}
}

View File

@@ -28,14 +28,15 @@ import (
"github.com/stretchr/testify/assert"
)
func TestPsPretty(t *testing.T) {
func TestPsTable(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"))
out := filepath.Join(dir, "output.txt")
f, err := os.Create(out)
if err != nil {
t.Fatal("could not create output file")
}
@@ -51,8 +52,9 @@ func TestPsPretty(t *testing.T) {
DoAndReturn(func(ctx context.Context, projectName string, options api.PsOptions) ([]api.ContainerSummary, error) {
return []api.ContainerSummary{
{
ID: "abc123",
Name: "ABC",
ID: "abc123",
Name: "ABC",
Image: "foo/bar",
Publishers: api.PortPublishers{
{
TargetPort: 8080,
@@ -69,15 +71,14 @@ func TestPsPretty(t *testing.T) {
}, nil
}).AnyTimes()
opts := psOptions{projectOptions: &projectOptions{ProjectName: "test"}}
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)
output, err := os.ReadFile(out)
assert.NoError(t, err)
assert.Contains(t, string(output), "8080/tcp, 8443/tcp")

View File

@@ -21,6 +21,7 @@ import (
"fmt"
"os"
"github.com/compose-spec/compose-go/types"
"github.com/morikuni/aec"
"github.com/spf13/cobra"
@@ -29,7 +30,7 @@ import (
)
type pullOptions struct {
*projectOptions
*ProjectOptions
composeOptions
quiet bool
parallel bool
@@ -38,9 +39,9 @@ type pullOptions struct {
ignorePullFailures bool
}
func pullCommand(p *projectOptions, backend api.Service) *cobra.Command {
func pullCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
opts := pullOptions{
projectOptions: p,
ProjectOptions: p,
}
cmd := &cobra.Command{
Use: "pull [OPTIONS] [SERVICE...]",
@@ -67,23 +68,32 @@ func pullCommand(p *projectOptions, backend api.Service) *cobra.Command {
return cmd
}
func withSelectedServicesOnly(project *types.Project, services []string) error {
enabled, err := project.GetServices(services...)
if err != nil {
return err
}
for _, s := range project.Services {
if !utils.StringContains(services, s.Name) {
project.DisabledServices = append(project.DisabledServices, s)
}
}
project.Services = enabled
return nil
}
func runPull(ctx context.Context, backend api.Service, opts pullOptions, services []string) error {
project, err := opts.toProject(services)
project, err := opts.ToProject(services)
if err != nil {
return err
}
if !opts.includeDeps {
enabled, err := project.GetServices(services...)
err := withSelectedServicesOnly(project, services)
if err != nil {
return err
}
for _, s := range project.Services {
if !utils.StringContains(services, s.Name) {
project.DisabledServices = append(project.DisabledServices, s)
}
}
project.Services = enabled
}
return backend.Pull(ctx, project, api.PullOptions{

View File

@@ -25,15 +25,16 @@ import (
)
type pushOptions struct {
*projectOptions
*ProjectOptions
composeOptions
IncludeDeps bool
Ignorefailures bool
Quiet bool
}
func pushCommand(p *projectOptions, backend api.Service) *cobra.Command {
func pushCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
opts := pushOptions{
projectOptions: p,
ProjectOptions: p,
}
pushCmd := &cobra.Command{
Use: "push [OPTIONS] [SERVICE...]",
@@ -44,17 +45,27 @@ func pushCommand(p *projectOptions, backend api.Service) *cobra.Command {
ValidArgsFunction: completeServiceNames(p),
}
pushCmd.Flags().BoolVar(&opts.Ignorefailures, "ignore-push-failures", false, "Push what it can and ignores images with push failures")
pushCmd.Flags().BoolVar(&opts.IncludeDeps, "include-deps", false, "Also push images of services declared as dependencies")
pushCmd.Flags().BoolVarP(&opts.Quiet, "quiet", "q", false, "Push without printing progress information")
return pushCmd
}
func runPush(ctx context.Context, backend api.Service, opts pushOptions, services []string) error {
project, err := opts.toProject(services)
project, err := opts.ToProject(services)
if err != nil {
return err
}
if !opts.IncludeDeps {
err := withSelectedServicesOnly(project, services)
if err != nil {
return err
}
}
return backend.Push(ctx, project, api.PushOptions{
IgnoreFailures: opts.Ignorefailures,
Quiet: opts.Quiet,
})
}

View File

@@ -24,15 +24,15 @@ import (
)
type removeOptions struct {
*projectOptions
*ProjectOptions
force bool
stop bool
volumes bool
}
func removeCommand(p *projectOptions, backend api.Service) *cobra.Command {
func removeCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
opts := removeOptions{
projectOptions: p,
ProjectOptions: p,
}
cmd := &cobra.Command{
Use: "rm [OPTIONS] [SERVICE...]",
@@ -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, name, err := opts.projectOrName()
project, name, err := opts.projectOrName(services...)
if err != nil {
return err
}

View File

@@ -26,13 +26,13 @@ import (
)
type restartOptions struct {
*projectOptions
*ProjectOptions
timeout int
}
func restartCommand(p *projectOptions, backend api.Service) *cobra.Command {
func restartCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
opts := restartOptions{
projectOptions: p,
ProjectOptions: p,
}
restartCmd := &cobra.Command{
Use: "restart [OPTIONS] [SERVICE...]",
@@ -49,7 +49,7 @@ func restartCommand(p *projectOptions, backend api.Service) *cobra.Command {
}
func runRestart(ctx context.Context, backend api.Service, opts restartOptions, services []string) error {
project, name, err := opts.projectOrName()
project, name, err := opts.projectOrName(services...)
if err != nil {
return err
}

View File

@@ -32,6 +32,7 @@ import (
"github.com/docker/cli/cli"
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/progress"
"github.com/docker/compose/v2/pkg/utils"
)
type runOptions struct {
@@ -107,12 +108,13 @@ func (opts runOptions) apply(project *types.Project) error {
return nil
}
func runCommand(p *projectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
func runCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
opts := runOptions{
composeOptions: &composeOptions{
projectOptions: p,
ProjectOptions: p,
},
}
createOpts := createOptions{}
cmd := &cobra.Command{
Use: "run [OPTIONS] SERVICE [COMMAND] [ARGS...]",
Short: "Run a one-off command on a service.",
@@ -135,13 +137,12 @@ func runCommand(p *projectOptions, dockerCli command.Cli, backend api.Service) *
return nil
}),
RunE: Adapt(func(ctx context.Context, args []string) error {
project, err := p.toProject([]string{opts.Service}, cgo.WithResolvedPaths(true))
project, err := p.ToProject([]string{opts.Service}, cgo.WithResolvedPaths(true))
if err != nil {
return err
}
ignore := project.Environment["COMPOSE_IGNORE_ORPHANS"]
opts.ignoreOrphans = strings.ToLower(ignore) == "true"
return runRun(ctx, backend, project, opts)
opts.ignoreOrphans = utils.StringToBool(project.Environment["COMPOSE_IGNORE_ORPHANS"])
return runRun(ctx, backend, project, opts, createOpts)
}),
ValidArgsFunction: completeServiceNames(p),
}
@@ -161,6 +162,7 @@ func runCommand(p *projectOptions, dockerCli command.Cli, backend api.Service) *
flags.BoolVar(&opts.useAliases, "use-aliases", false, "Use the service's network useAliases in the network(s) the container connects to.")
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.")
flags.BoolVar(&createOpts.Build, "build", false, "Build image before starting container.")
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.")
@@ -181,12 +183,14 @@ func normalizeRunFlags(f *pflag.FlagSet, name string) pflag.NormalizedName {
return pflag.NormalizedName(name)
}
func runRun(ctx context.Context, backend api.Service, project *types.Project, opts runOptions) error {
func runRun(ctx context.Context, backend api.Service, project *types.Project, opts runOptions, createOpts createOptions) error {
err := opts.apply(project)
if err != nil {
return err
}
createOpts.Apply(project)
err = progress.Run(ctx, func(ctx context.Context) error {
return startDependencies(ctx, backend, *project, opts.Service, opts.ignoreOrphans)
})

View File

@@ -24,12 +24,12 @@ import (
)
type startOptions struct {
*projectOptions
*ProjectOptions
}
func startCommand(p *projectOptions, backend api.Service) *cobra.Command {
func startCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
opts := startOptions{
projectOptions: p,
ProjectOptions: p,
}
startCmd := &cobra.Command{
Use: "start [SERVICE...]",
@@ -43,7 +43,7 @@ func startCommand(p *projectOptions, backend api.Service) *cobra.Command {
}
func runStart(ctx context.Context, backend api.Service, opts startOptions, services []string) error {
project, name, err := opts.projectOrName()
project, name, err := opts.projectOrName(services...)
if err != nil {
return err
}

View File

@@ -26,14 +26,14 @@ import (
)
type stopOptions struct {
*projectOptions
*ProjectOptions
timeChanged bool
timeout int
}
func stopCommand(p *projectOptions, backend api.Service) *cobra.Command {
func stopCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
opts := stopOptions{
projectOptions: p,
ProjectOptions: p,
}
cmd := &cobra.Command{
Use: "stop [OPTIONS] [SERVICE...]",
@@ -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, name, err := opts.projectOrName()
project, name, err := opts.projectOrName(services...)
if err != nil {
return err
}

View File

@@ -31,12 +31,12 @@ import (
)
type topOptions struct {
*projectOptions
*ProjectOptions
}
func topCommand(p *projectOptions, backend api.Service) *cobra.Command {
func topCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
opts := topOptions{
projectOptions: p,
ProjectOptions: p,
}
topCmd := &cobra.Command{
Use: "top [SERVICES...]",

View File

@@ -34,7 +34,7 @@ import (
// composeOptions hold options common to `up` and `run` to run compose project
type composeOptions struct {
*projectOptions
*ProjectOptions
}
type upOptions struct {
@@ -49,21 +49,16 @@ type upOptions struct {
noPrefix bool
attachDependencies bool
attach []string
timestamp bool
wait bool
}
func (opts upOptions) apply(project *types.Project, services []string) error {
if opts.noDeps {
enabled, err := project.GetServices(services...)
err := withSelectedServicesOnly(project, services)
if err != nil {
return err
}
for _, s := range project.Services {
if !utils.StringContains(services, s.Name) {
project.DisabledServices = append(project.DisabledServices, s)
}
}
project.Services = enabled
}
if opts.exitCodeFrom != "" {
@@ -92,7 +87,7 @@ func (opts upOptions) apply(project *types.Project, services []string) error {
return nil
}
func upCommand(p *projectOptions, backend api.Service) *cobra.Command {
func upCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
up := upOptions{}
create := createOptions{}
upCmd := &cobra.Command{
@@ -126,6 +121,7 @@ func upCommand(p *projectOptions, backend api.Service) *cobra.Command {
flags.BoolVar(&up.cascadeStop, "abort-on-container-exit", false, "Stops all containers if any container was stopped. Incompatible with -d")
flags.StringVar(&up.exitCodeFrom, "exit-code-from", "", "Return the exit code of the selected service container. Implies --abort-on-container-exit")
flags.IntVarP(&create.timeout, "timeout", "t", 10, "Use this timeout in seconds for container shutdown when attached or when containers are already running.")
flags.BoolVar(&up.timestamp, "timestamps", false, "Show timestamps.")
flags.BoolVar(&up.noDeps, "no-deps", false, "Don't start linked services.")
flags.BoolVar(&create.recreateDeps, "always-recreate-deps", false, "Recreate dependent containers. Incompatible with --no-recreate.")
flags.BoolVarP(&create.noInherit, "renew-anon-volumes", "V", false, "Recreate anonymous volumes instead of retrieving data from the previous containers.")
@@ -176,7 +172,7 @@ func runUp(ctx context.Context, backend api.Service, createOptions createOptions
var consumer api.LogConsumer
if !upOptions.Detach {
consumer = formatter.NewLogConsumer(ctx, os.Stdout, !upOptions.noColor, !upOptions.noPrefix)
consumer = formatter.NewLogConsumer(ctx, os.Stdout, os.Stderr, !upOptions.noColor, !upOptions.noPrefix, upOptions.timestamp)
}
attachTo := services
@@ -214,6 +210,7 @@ func runUp(ctx context.Context, backend api.Service, createOptions createOptions
ExitCodeFrom: upOptions.exitCodeFrom,
CascadeStop: upOptions.cascadeStop,
Wait: upOptions.wait,
Services: services,
},
})
}

View File

@@ -17,10 +17,13 @@
package formatter
const (
// JSON is the constant for Json formats on list commands
// JSON Print in JSON format
JSON = "json"
// TemplateLegacyJSON the legacy json formatting value using go template
TemplateLegacyJSON = "{{json.}}"
// PRETTY is the constant for default formats on list commands
// Deprecated: use TABLE
PRETTY = "pretty"
// TABLE Print output in table format with column headers (default)
TABLE = "table"
)

View File

@@ -30,7 +30,7 @@ import (
// Print prints formatted lists in different formats
func Print(toJSON interface{}, format string, outWriter io.Writer, writerFn func(w io.Writer), headers ...string) error {
switch strings.ToLower(format) {
case PRETTY, "":
case TABLE, PRETTY, "":
return PrintPrettySection(outWriter, writerFn, headers...)
case TemplateLegacyJSON:
switch reflect.TypeOf(toJSON).Kind() {

View File

@@ -22,7 +22,7 @@ import (
"io"
"testing"
"gotest.tools/assert"
"gotest.tools/v3/assert"
)
type testStruct struct {

View File

@@ -23,8 +23,10 @@ import (
"strconv"
"strings"
"sync"
"time"
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/docker/pkg/jsonmessage"
)
// LogConsumer consume logs from services and format them
@@ -32,20 +34,24 @@ type logConsumer struct {
ctx context.Context
presenters sync.Map // map[string]*presenter
width int
writer io.Writer
stdout io.Writer
stderr io.Writer
color bool
prefix bool
timestamp bool
}
// NewLogConsumer creates a new LogConsumer
func NewLogConsumer(ctx context.Context, w io.Writer, color bool, prefix bool) api.LogConsumer {
func NewLogConsumer(ctx context.Context, stdout, stderr io.Writer, color, prefix, timestamp bool) api.LogConsumer {
return &logConsumer{
ctx: ctx,
presenters: sync.Map{},
width: 0,
writer: w,
stdout: stdout,
stderr: stderr,
color: color,
prefix: prefix,
timestamp: timestamp,
}
}
@@ -83,20 +89,34 @@ func (l *logConsumer) getPresenter(container string) *presenter {
}
// Log formats a log message as received from name/container
func (l *logConsumer) Log(container, service, message string) {
func (l *logConsumer) Log(container, message string) {
l.write(l.stdout, container, message)
}
// Log formats a log message as received from name/container
func (l *logConsumer) Err(container, message string) {
l.write(l.stderr, container, message)
}
func (l *logConsumer) write(w io.Writer, container, message string) {
if l.ctx.Err() != nil {
return
}
p := l.getPresenter(container)
timestamp := time.Now().Format(jsonmessage.RFC3339NanoFixed)
for _, line := range strings.Split(message, "\n") {
fmt.Fprintf(l.writer, "%s%s\n", p.prefix, line)
if l.timestamp {
fmt.Fprintf(w, "%s%s%s\n", p.prefix, timestamp, line)
} else {
fmt.Fprintf(w, "%s%s\n", p.prefix, line)
}
}
}
func (l *logConsumer) Status(container, msg string) {
p := l.getPresenter(container)
s := p.colors(fmt.Sprintf("%s %s\n", container, msg))
l.writer.Write([]byte(s)) //nolint:errcheck
l.stdout.Write([]byte(s)) //nolint:errcheck
}
func (l *logConsumer) computeWidth() {

View File

@@ -13,7 +13,7 @@
// limitations under the License.
variable "GO_VERSION" {
default = "1.19.1"
default = "1.19.4"
}
variable "BUILD_TAGS" {
@@ -71,7 +71,7 @@ target "vendor-validate" {
output = ["type=cacheonly"]
}
target "vendor" {
target "vendor-update" {
inherits = ["_common"]
target = "vendor-update"
output = ["."]

View File

@@ -42,6 +42,7 @@ Docker Compose
| `--compatibility` | | | Run compose in backward compatibility mode |
| `--env-file` | `string` | | Specify an alternate environment file. |
| `-f`, `--file` | `stringArray` | | Compose configuration files |
| `--parallel` | `int` | `-1` | Control max parallelism, -1 for unlimited |
| `--profile` | `stringArray` | | Specify a profile to enable |
| `--project-directory` | `string` | | Specify an alternate working directory
(default: the path of the, first specified, Compose file) |
@@ -55,7 +56,7 @@ Docker Compose
You can use compose subcommand, `docker compose [-f <arg>...] [options] [COMMAND] [ARGS...]`, to build and manage
multiple services in Docker containers.
### Use `-f` to specify name and path of one or more Compose files
### Use `-f` to specify the name and path of one or more Compose files
Use the `-f` flag to specify the location of a Compose configuration file.
#### Specifying multiple Compose files
@@ -118,8 +119,8 @@ Each configuration has a project name. If you supply a `-p` flag, you can specif
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
project name to retrieve the relevant resources.
Many Compose subcommands can be run without a Compose file by passing
the project name.
```console
$ docker compose -p my_project ps -a
@@ -145,10 +146,10 @@ Profiles can also be set by `COMPOSE_PROFILES` environment variable.
You can set environment variables for various docker compose options, including the `-f`, `-p` and `--profiles` flags.
Setting the `COMPOSE_FILE` environment variable is equivalent to passing the `-f` flag,
`COMPOSE_PROJECT_NAME` environment variable does the same for to the `-p` flag,
and so does `COMPOSE_PROFILES` environment variable for to the `--profiles` flag.
`COMPOSE_PROJECT_NAME` environment variable does the same as the `-p` flag,
and `COMPOSE_PROFILES` environment variable is equivalent to the `--profiles` flag.
If flags are explicitly set on command line, associated environment variable is ignored
If flags are explicitly set on the command line, the 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

@@ -14,6 +14,7 @@ Converts the compose file to platform's canonical format
| `--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-consistency` | | | Don't check model consistency - warning: may produce invalid Compose output |
| `--no-interpolate` | | | Don't interpolate environment variables. |
| `--no-normalize` | | | Don't normalize compose model. |
| `-o`, `--output` | `string` | | Save to file (default to stdout) |
@@ -28,8 +29,8 @@ Converts the compose file to platform's canonical format
## 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.
`docker compose convert` renders the actual data model to be applied on the target platform. When used with the Docker engine,
it merges the Compose files set by `-f` flags, resolves variables in the Compose file, and expands short-notation into
the canonical format.
To allow smooth migration from docker-compose, this subcommand declares alias `docker compose config`

View File

@@ -22,5 +22,5 @@ Execute a command in a running container.
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 allocate a TTY by default, so
you can use a command such as `docker compose exec web sh` to get an interactive prompt.

View File

@@ -7,6 +7,7 @@ List images used by the created containers
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `--format` | `string` | `table` | Format the output. Values: [table \| json]. |
| `-q`, `--quiet` | | | Only display IDs |

View File

@@ -9,7 +9,7 @@ List running compose projects
| --- | --- | --- | --- |
| `-a`, `--all` | | | Show all stopped Compose projects |
| `--filter` | `filter` | | Filter output based on conditions provided. |
| `--format` | `string` | `pretty` | Format the output. Values: [pretty \| json]. |
| `--format` | `string` | `table` | Format the output. Values: [table \| json]. |
| `-q`, `--quiet` | | | Only display IDs. |
@@ -17,4 +17,4 @@ List running compose projects
## Description
List Compose projects running on platform.
Lists running Compose projects.

View File

@@ -9,7 +9,7 @@ List containers
| --- | --- | --- | --- |
| `-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] |
| [`--format`](#format) | `string` | `table` | Format the output. Values: [table \| json] |
| `-q`, `--quiet` | | | Only display IDs |
| `--services` | | | Display services |
| [`--status`](#status) | `stringArray` | | Filter services by status. Values: [paused \| restarting \| removing \| running \| dead \| created \| exited] |
@@ -35,7 +35,7 @@ example-foo-1 "/docker-entrypoint.…" foo running 0.0.0.0:8080->8
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),
presentations for the output. Currently, supported options are `pretty` (default),
and `json`, which outputs information about the containers as a JSON array:
```console
@@ -85,7 +85,7 @@ $ docker compose ps --format json | jq .
### <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:
to show only containers that are running or only containers that have exited:
```console
$ docker compose ps --status=running
@@ -99,7 +99,7 @@ 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>`
The [`--status` flag](#status) is a convenient 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:
@@ -114,4 +114,4 @@ 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.
option, but additional filter options may be added in the future.

View File

@@ -8,6 +8,8 @@ Push service images
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `--ignore-push-failures` | | | Push what it can and ignores images with push failures |
| `--include-deps` | | | Also push images of services declared as dependencies |
| `-q`, `--quiet` | | | Push without printing progress information |
<!---MARKER_GEN_END-->

View File

@@ -7,6 +7,7 @@ Run a one-off command on a service.
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `--build` | | | Build image before starting container. |
| `-d`, `--detach` | | | Run container in background and print container ID |
| `--entrypoint` | `string` | | Override the entrypoint of the image |
| `-e`, `--env` | `stringArray` | | Set environment variables |

View File

@@ -27,6 +27,7 @@ Create and start containers
| `-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. |
| `--timestamps` | | | Show timestamps. |
| `--wait` | | | Wait for services to be running\|healthy. Implies detached mode. |

View File

@@ -4,7 +4,7 @@ long: |-
You can use compose subcommand, `docker compose [-f <arg>...] [options] [COMMAND] [ARGS...]`, to build and manage
multiple services in Docker containers.
### Use `-f` to specify name and path of one or more Compose files
### Use `-f` to specify the name and path of one or more Compose files
Use the `-f` flag to specify the location of a Compose configuration file.
#### Specifying multiple Compose files
@@ -67,8 +67,8 @@ long: |-
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
project name to retrieve the relevant resources.
Many Compose subcommands can be run without a Compose file by passing
the project name.
```console
$ docker compose -p my_project ps -a
@@ -94,10 +94,10 @@ long: |-
You can set environment variables for various docker compose options, including the `-f`, `-p` and `--profiles` flags.
Setting the `COMPOSE_FILE` environment variable is equivalent to passing the `-f` flag,
`COMPOSE_PROJECT_NAME` environment variable does the same for to the `-p` flag,
and so does `COMPOSE_PROFILES` environment variable for to the `--profiles` flag.
`COMPOSE_PROJECT_NAME` environment variable does the same as the `-p` flag,
and `COMPOSE_PROFILES` environment variable is equivalent to the `--profiles` flag.
If flags are explicitly set on command line, associated environment variable is ignored
If flags are explicitly set on the command line, the 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.
@@ -208,6 +208,16 @@ options:
experimentalcli: false
kubernetes: false
swarm: false
- option: parallel
value_type: int
default_value: "-1"
description: Control max parallelism, -1 for unlimited
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: profile
value_type: stringArray
default_value: '[]'

View File

@@ -2,9 +2,9 @@ command: docker compose convert
aliases: docker compose convert, docker compose config
short: Converts the compose file to platform's canonical format
long: |-
`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.
`docker compose convert` renders the actual data model to be applied on the target platform. When used with the Docker engine,
it merges the Compose files set by `-f` flags, resolves variables in the Compose file, and expands short-notation into
the canonical format.
To allow smooth migration from docker-compose, this subcommand declares alias `docker compose config`
usage: docker compose convert [OPTIONS] [SERVICE...]
@@ -40,6 +40,17 @@ options:
experimentalcli: false
kubernetes: false
swarm: false
- option: no-consistency
value_type: bool
default_value: "false"
description: |
Don't check model consistency - warning: may produce invalid Compose output
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: no-interpolate
value_type: bool
default_value: "false"

View File

@@ -3,7 +3,7 @@ short: Execute a command in a running container.
long: |-
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 allocate a TTY by default, so
you can use a command such as `docker compose exec web sh` to get an interactive prompt.
usage: docker compose exec [OPTIONS] SERVICE COMMAND [ARGS...]
pname: docker compose

View File

@@ -5,6 +5,16 @@ usage: docker compose images [OPTIONS] [SERVICE...]
pname: docker compose
plink: docker_compose.yaml
options:
- option: format
value_type: string
default_value: table
description: 'Format the output. Values: [table | json].'
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: quiet
shorthand: q
value_type: bool

View File

@@ -1,6 +1,6 @@
command: docker compose ls
short: List running compose projects
long: List Compose projects running on platform.
long: Lists running Compose projects.
usage: docker compose ls [OPTIONS]
pname: docker compose
plink: docker_compose.yaml
@@ -27,8 +27,8 @@ options:
swarm: false
- option: format
value_type: string
default_value: pretty
description: 'Format the output. Values: [pretty | json].'
default_value: table
description: 'Format the output. Values: [table | json].'
deprecated: false
hidden: false
experimental: false

View File

@@ -38,8 +38,8 @@ options:
swarm: false
- option: format
value_type: string
default_value: pretty
description: 'Format the output. Values: [pretty | json]'
default_value: table
description: 'Format the output. Values: [table | json]'
details_url: '#format'
deprecated: false
hidden: false
@@ -85,7 +85,7 @@ examples: |-
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),
presentations for the output. Currently, supported options are `pretty` (default),
and `json`, which outputs information about the containers as a JSON array:
```console
@@ -135,7 +135,7 @@ examples: |-
### 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:
to show only containers that are running or only containers that have exited:
```console
$ docker compose ps --status=running
@@ -149,7 +149,7 @@ examples: |-
### Filter containers by status (--filter) {#filter}
The [`--status` flag](#status) is a convenience shorthand for the `--filter status=<status>`
The [`--status` flag](#status) is a convenient 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:
@@ -164,7 +164,7 @@ examples: |-
```
The `docker compose ps` command currently only supports the `--filter status=<status>`
option, but additional filter options may be added in future.
option, but additional filter options may be added in the future.
deprecated: false
experimental: false
experimentalcli: false

View File

@@ -33,6 +33,27 @@ options:
experimentalcli: false
kubernetes: false
swarm: false
- option: include-deps
value_type: bool
default_value: "false"
description: Also push images of services declared as dependencies
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: quiet
shorthand: q
value_type: bool
default_value: "false"
description: Push without printing progress information
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
deprecated: false
experimental: false
experimentalcli: false

View File

@@ -58,6 +58,16 @@ usage: docker compose run [OPTIONS] SERVICE [COMMAND] [ARGS...]
pname: docker compose
plink: docker_compose.yaml
options:
- option: build
value_type: bool
default_value: "false"
description: Build image before starting container.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: detach
shorthand: d
value_type: bool

View File

@@ -230,6 +230,16 @@ options:
experimentalcli: false
kubernetes: false
swarm: false
- option: timestamps
value_type: bool
default_value: "false"
description: Show timestamps.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: wait
value_type: bool
default_value: "false"

View File

@@ -0,0 +1,5 @@
Feature: Down
Scenario: No resources to remove
When I run "compose down"
Then the output contains "Warning: No resource found to remove for project "no_resources_to_remove""

View File

@@ -0,0 +1,15 @@
Feature: Simple service up
Background:
Given a compose file
"""
services:
simple:
image: alpine
command: top
"""
Scenario: compose up
When I run "compose up -d"
Then the output contains "simple-1 Started"
And service "simple" is "running"

View File

@@ -0,0 +1,21 @@
Feature: Start
Background:
Given a compose file
"""
services:
simple:
image: alpine
command: top
another:
image: alpine
command: top
"""
Scenario: Start single service
When I run "compose create"
Then the output contains "simple-1 Created"
And the output contains "another-1 Created"
Then I run "compose start another"
And service "another" is "running"
And service "simple" is "created"

View File

@@ -0,0 +1,31 @@
Feature: Stop
Background:
Given a compose file
"""
services:
should_fail:
image: alpine
command: ls /does_not_exist
sleep: # will be killed
image: alpine
command: ping localhost
init: true
"""
Scenario: Cascade stop
When I run "compose up --abort-on-container-exit"
Then the output contains "should_fail-1 exited with code 1"
And the output contains "Aborting on container exit..."
And the exit code is 1
Scenario: Exit code from
When I run "compose up --exit-code-from sleep"
Then the output contains "should_fail-1 exited with code 1"
And the output contains "Aborting on container exit..."
And the exit code is 143
Scenario: Exit code from unknown service
When I run "compose up --exit-code-from unknown"
Then the output contains "no such service: unknown"
And the exit code is 1

View File

@@ -0,0 +1,23 @@
Feature: Volume: tmpfs
Background:
Given a compose file
"""
services:
svc:
image: busybox
volumes:
- type: tmpfs
target: /volumes/tmpfs
tmpfs:
size: 2M
mode: 0o647
"""
Scenario: tmpfs Permissions Set
When I run "compose run --rm svc stat -c "%a" /volumes/tmpfs"
Then the output contains "647"
Scenario: tmpfs Size Set
When I run "compose run --rm svc sh -c 'df /volumes/tmpfs | tail -n1 | awk '"'"'{print $4}'"'"'' "
Then the output contains "2048"

135
e2e/cucumber_test.go Normal file
View File

@@ -0,0 +1,135 @@
/*
Copyright 2022 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 cucumber
import (
"context"
"fmt"
"os"
"regexp"
"strings"
"testing"
"github.com/cucumber/godog"
"github.com/cucumber/godog/colors"
"github.com/mattn/go-shellwords"
"gotest.tools/v3/icmd"
"github.com/docker/compose/v2/pkg/e2e"
)
func TestCucumber(t *testing.T) {
testingOptions := godog.Options{
TestingT: t,
Paths: []string{"./cucumber-features"},
Output: colors.Colored(os.Stdout),
Format: "pretty",
}
status := godog.TestSuite{
Name: "godogs",
Options: &testingOptions,
ScenarioInitializer: setup,
}.Run()
if status == 2 {
t.SkipNow()
}
if status != 0 {
t.Fatalf("zero status code expected, %d received", status)
}
}
func setup(s *godog.ScenarioContext) {
t := s.TestingT()
cli := e2e.NewCLI(t, e2e.WithEnv(
fmt.Sprintf("COMPOSE_PROJECT_NAME=%s", strings.Split(t.Name(), "/")[1]),
))
th := testHelper{
T: t,
CLI: cli,
}
s.Before(func(ctx context.Context, sc *godog.Scenario) (context.Context, error) {
cli.RunDockerComposeCmd(t, "down", "--remove-orphans", "-v", "-t", "0")
return ctx, nil
})
s.After(func(ctx context.Context, sc *godog.Scenario, err error) (context.Context, error) {
cli.RunDockerComposeCmd(t, "down", "--remove-orphans", "-v", "-t", "0")
return ctx, nil
})
s.Step(`^a compose file$`, th.setComposeFile)
s.Step(`^I run "compose (.*)"$`, th.runComposeCommand)
s.Step(`service "(.*)" is "(.*)"$`, th.serviceIsStatus)
s.Step(`output contains "(.*)"$`, th.outputContains)
s.Step(`exit code is (\d+)$`, th.exitCodeIs)
}
type testHelper struct {
T *testing.T
ComposeFile string
CommandOutput string
CommandExitCode int
CLI *e2e.CLI
}
func (th *testHelper) serviceIsStatus(service, status string) error {
res := th.CLI.RunDockerComposeCmd(th.T, "ps", "-a")
statusRegex := fmt.Sprintf("%s\\s+%s", service, status)
r, _ := regexp.Compile(statusRegex)
if !r.MatchString(res.Combined()) {
return fmt.Errorf("Missing/incorrect ps output:\n%s", res.Combined())
}
return nil
}
func (th *testHelper) outputContains(substring string) error {
if !strings.Contains(th.CommandOutput, substring) {
return fmt.Errorf("Missing output substring: %s\noutput: %s", substring, th.CommandOutput)
}
return nil
}
func (th *testHelper) exitCodeIs(exitCode int) error {
if exitCode != th.CommandExitCode {
return fmt.Errorf("Wrong exit code: %d expected: %d", th.CommandExitCode, exitCode)
}
return nil
}
func (th *testHelper) runComposeCommand(command string) error {
commandArgs, err := shellwords.Parse(command)
if err != nil {
return err
}
commandArgs = append([]string{"-f", "-"}, commandArgs...)
cmd := th.CLI.NewDockerComposeCmd(th.T, commandArgs...)
cmd.Stdin = strings.NewReader(th.ComposeFile)
res := icmd.RunCmd(cmd)
th.CommandOutput = res.Combined()
th.CommandExitCode = res.ExitCode
return nil
}
func (th *testHelper) setComposeFile(composeString string) error {
th.ComposeFile = composeString
return nil
}

117
go.mod
View File

@@ -5,15 +5,14 @@ go 1.19
require (
github.com/AlecAivazis/survey/v2 v2.3.6
github.com/buger/goterm v1.0.4
github.com/cnabio/cnab-to-oci v0.3.7
github.com/compose-spec/compose-go v1.6.0
github.com/compose-spec/compose-go v1.8.0
github.com/containerd/console v1.0.3
github.com/containerd/containerd v1.6.8
github.com/distribution/distribution/v3 v3.0.0-20220902125104-0122d7ddaec0
github.com/docker/buildx v0.8.2 // when updating, also update the replace rules accordingly
github.com/docker/cli v20.10.17+incompatible
github.com/containerd/containerd v1.6.14
github.com/distribution/distribution/v3 v3.0.0-20221201083218-92d136e113cf
github.com/docker/buildx v0.9.1 // when updating, also update the replace rules accordingly
github.com/docker/cli v20.10.20+incompatible // replaced; see replace rule for actual version
github.com/docker/cli-docs-tool v0.5.0
github.com/docker/docker v20.10.17+incompatible
github.com/docker/docker v20.10.20+incompatible // replaced; see replace rule for actual version
github.com/docker/go-connections v0.4.0
github.com/docker/go-units v0.5.0
github.com/golang/mock v1.6.0
@@ -21,36 +20,36 @@ require (
github.com/hashicorp/go-version v1.6.0
github.com/mattn/go-isatty v0.0.16
github.com/mattn/go-shellwords v1.0.12
github.com/moby/buildkit v0.10.4
github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae
github.com/moby/buildkit v0.10.4 // replaced; see replace rule for actual version
github.com/moby/term v0.0.0-20221128092401-c43b287e0e0f
github.com/morikuni/aec v1.0.0
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799
github.com/opencontainers/image-spec v1.1.0-rc2
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.9.0
github.com/spf13/cobra v1.5.0
github.com/spf13/cobra v1.6.1
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.8.0
github.com/stretchr/testify v1.8.1
github.com/theupdateframework/notary v0.7.0
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde
go.opentelemetry.io/otel v1.11.2
golang.org/x/sync v0.1.0
gopkg.in/yaml.v2 v2.4.0
gotest.tools v2.2.0+incompatible
gotest.tools/v3 v3.3.0
gotest.tools/v3 v3.4.0
k8s.io/client-go v0.24.1 // replaced; see replace for the actual version used
)
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.5.2 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.1.2 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cnabio/cnab-go v0.23.4 // indirect
github.com/containerd/continuity v0.2.3-0.20220330195504-d132b287edc8 // indirect
github.com/containerd/continuity v0.3.0 // 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.8.1+incompatible // indirect
github.com/docker/docker-credential-helpers v0.6.4 // indirect
github.com/docker/docker-credential-helpers v0.7.0 // indirect
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect
github.com/docker/go-metrics v0.0.1 // indirect
github.com/felixge/httpsnoop v1.0.2 // indirect
@@ -61,99 +60,111 @@ require (
github.com/gogo/googleapis v1.4.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-cmp v0.5.8 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/googleapis/gnostic v0.5.5 // indirect
github.com/gorilla/mux v1.8.0 // 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/inconshreveable/mousetrap v1.0.1 // 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.15.1 // indirect
github.com/klauspost/compress v1.15.9 // 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/mattn/go-runewidth v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // 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/signal v0.6.0 // indirect
github.com/moby/patternmatcher v0.5.0 // indirect
github.com/moby/spdystream v0.2.0 // indirect
github.com/moby/sys/sequential v0.5.0 // indirect
github.com/moby/sys/signal v0.7.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.2 // indirect
github.com/opencontainers/runc v1.1.2 // indirect
github.com/opencontainers/runc v1.1.3 // 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.12.1 // indirect
github.com/prometheus/client_golang v1.12.2 // indirect
github.com/prometheus/client_model v0.2.0 // 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/rivo/uniseg v0.2.0 // indirect
github.com/serialx/hashring v0.0.0-20190422032157-8b2912629002 // indirect
github.com/tonistiigi/fsutil v0.0.0-20220930225714-4638ad635be5 // 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
github.com/zmap/zcrypto v0.0.0-20220605182715-4dfcec6e9a8c // indirect
github.com/zmap/zlint v1.1.0 // 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.10.0
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.4.1 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.4.1 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.4.1 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp 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.10.0 // indirect
go.opentelemetry.io/otel/trace v1.11.2 // indirect
go.opentelemetry.io/proto/otlp v0.12.0 // indirect
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect
golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88 // indirect
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c // indirect
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 // 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-20220210224613-90d013bbcef8 // indirect
google.golang.org/appengine v1.6.7 // 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
google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect
google.golang.org/grpc v1.47.0 // indirect
google.golang.org/protobuf v1.28.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apimachinery v0.24.1 // indirect; see replace for the actual version used
k8s.io/client-go v0.24.1 // see replace for the actual version used
k8s.io/api v0.24.1 // indirect; replaced; see replace for the actual version used
k8s.io/apimachinery v0.24.1 // indirect; replaced; see replace for the actual version used
k8s.io/klog/v2 v2.60.1 // indirect
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect
sigs.k8s.io/yaml v1.2.0 // indirect
)
require github.com/cucumber/godog v0.0.0-00010101000000-000000000000
require (
github.com/cenkalti/backoff/v4 v4.1.2 // indirect
github.com/cyberphone/json-canonicalization v0.0.0-20210303052042-6bc126869bf4 // indirect
github.com/googleapis/gnostic v0.5.5 // indirect
github.com/moby/spdystream v0.2.0 // indirect
github.com/serialx/hashring v0.0.0-20190422032157-8b2912629002 // indirect
github.com/zmap/zcrypto v0.0.0-20220605182715-4dfcec6e9a8c // indirect
github.com/zmap/zlint v1.1.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.4.1 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.4.1 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.4.1 // indirect
k8s.io/api v0.24.1 // indirect
github.com/bugsnag/bugsnag-go v1.5.0 // indirect
github.com/cloudflare/cfssl v1.4.1 // indirect
github.com/cucumber/gherkin-go/v19 v19.0.3 // indirect
github.com/cucumber/messages-go/v16 v16.0.1 // indirect
github.com/gofrs/uuid v4.2.0+incompatible // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-memdb v1.3.2 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/jinzhu/gorm v1.9.11 // indirect
github.com/spf13/viper v1.4.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
// Override for e2e tests
github.com/cucumber/godog => github.com/laurazard/godog v0.0.0-20220922095256-4c4b17abdae7
github.com/opencontainers/runc => github.com/opencontainers/runc v1.1.2 // Can be removed on next bump of containerd to > 1.6.4
github.com/docker/cli => github.com/docker/cli v20.10.3-0.20221013132413-1d6c6e2367e2+incompatible // 22.06 master branch
github.com/docker/docker => github.com/docker/docker v20.10.3-0.20221021173910-5aac513617f0+incompatible // 22.06 branch
github.com/moby/buildkit => github.com/moby/buildkit v0.10.1-0.20220816171719-55ba9d14360a // same as buildx
// 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
// https://github.com/docker/buildx/blob/v0.9.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

1382
go.sum

File diff suppressed because it is too large Load Diff

View File

@@ -72,9 +72,11 @@ type Service interface {
// Events executes the equivalent to a `compose events`
Events(ctx context.Context, projectName string, options EventsOptions) error
// Port executes the equivalent to a `compose port`
Port(ctx context.Context, projectName string, service string, port int, options PortOptions) (string, int, error)
Port(ctx context.Context, projectName string, service string, port uint16, options PortOptions) (string, int, error)
// Images executes the equivalent of a `compose images`
Images(ctx context.Context, projectName string, options ImagesOptions) ([]ImageSummary, error)
// MaxConcurrency defines upper limit for concurrent operations against engine API
MaxConcurrency(parallel int)
}
// BuildOptions group options of the Build API
@@ -179,10 +181,13 @@ type ConvertOptions struct {
Format string
// Output defines the path to save the application model
Output string
// Resolve image reference to digests
ResolveImageDigests bool
}
// PushOptions group options of the Push API
type PushOptions struct {
Quiet bool
IgnoreFailures bool
}
@@ -313,10 +318,13 @@ type PortPublisher struct {
type ContainerSummary struct {
ID string
Name string
Image any
Command string
Project string
Service string
Created int64
State string
Status string
Health string
ExitCode int
Publishers PortPublishers
@@ -432,7 +440,8 @@ type Stack struct {
// LogConsumer is a callback to process log messages from services
type LogConsumer interface {
Log(containerName, service, message string)
Log(containerName, message string)
Err(containerName, message string)
Status(container, msg string)
Register(container string)
}
@@ -456,8 +465,10 @@ type ContainerEvent struct {
}
const (
// ContainerEventLog is a ContainerEvent of type log. Line is set
// ContainerEventLog is a ContainerEvent of type log on stdout. Line is set
ContainerEventLog = iota
// ContainerEventErr is a ContainerEvent of type log on stderr. Line is set
ContainerEventErr
// ContainerEventAttach is a ContainerEvent of type attach. First event sent about a container
ContainerEventAttach
// ContainerEventStopped is a ContainerEvent of type stopped.

View File

@@ -48,8 +48,9 @@ type ServiceProxy struct {
UnPauseFn func(ctx context.Context, project string, options PauseOptions) error
TopFn func(ctx context.Context, projectName string, services []string) ([]ContainerProcSummary, error)
EventsFn func(ctx context.Context, project string, options EventsOptions) error
PortFn func(ctx context.Context, project string, service string, port int, options PortOptions) (string, int, error)
PortFn func(ctx context.Context, project string, service string, port uint16, options PortOptions) (string, int, error)
ImagesFn func(ctx context.Context, projectName string, options ImagesOptions) ([]ImageSummary, error)
MaxConcurrencyFn func(parallel int)
interceptors []Interceptor
}
@@ -87,6 +88,7 @@ func (s *ServiceProxy) WithService(service Service) *ServiceProxy {
s.EventsFn = service.Events
s.PortFn = service.Port
s.ImagesFn = service.Images
s.MaxConcurrencyFn = service.MaxConcurrency
return s
}
@@ -294,7 +296,7 @@ func (s *ServiceProxy) Events(ctx context.Context, projectName string, options E
}
// Port implements Service interface
func (s *ServiceProxy) Port(ctx context.Context, projectName string, service string, port int, options PortOptions) (string, int, error) {
func (s *ServiceProxy) Port(ctx context.Context, projectName string, service string, port uint16, options PortOptions) (string, int, error) {
if s.PortFn == nil {
return "", 0, ErrNotImplemented
}
@@ -308,3 +310,7 @@ func (s *ServiceProxy) Images(ctx context.Context, project string, options Image
}
return s.ImagesFn(ctx, project, options)
}
func (s *ServiceProxy) MaxConcurrency(i int) {
s.MaxConcurrencyFn(i)
}

View File

@@ -37,6 +37,9 @@ func (s *composeService) attach(ctx context.Context, project *types.Project, lis
if err != nil {
return nil, err
}
if len(containers) == 0 {
return containers, nil
}
containers.sorted() // This enforce predictable colors assignment
@@ -66,7 +69,7 @@ func (s *composeService) attachContainer(ctx context.Context, container moby.Con
Service: serviceName,
})
w := utils.GetWriter(func(line string) {
wOut := utils.GetWriter(func(line string) {
listener(api.ContainerEvent{
Type: api.ContainerEventLog,
Container: containerName,
@@ -74,13 +77,21 @@ func (s *composeService) attachContainer(ctx context.Context, container moby.Con
Line: line,
})
})
wErr := utils.GetWriter(func(line string) {
listener(api.ContainerEvent{
Type: api.ContainerEventErr,
Container: containerName,
Service: serviceName,
Line: line,
})
})
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)
_, _, err = s.attachContainerStreams(ctx, container.ID, inspect.Config.Tty, nil, wOut, wErr)
return err
}

View File

@@ -27,7 +27,7 @@ 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/docker/pkg/urlutil"
"github.com/docker/docker/builder/remotecontext/urlutil"
bclient "github.com/moby/buildkit/client"
"github.com/moby/buildkit/session"
"github.com/moby/buildkit/session/auth/authprovider"
@@ -48,8 +48,6 @@ func (s *composeService) Build(ctx context.Context, project *types.Project, opti
func (s *composeService) build(ctx context.Context, project *types.Project, options api.BuildOptions) error {
opts := map[string]build.Options{}
var imagesToBuild []string
args := flatten(options.Args.Resolve(envResolver(project.Environment)))
services, err := project.GetServices(options.Services...)
@@ -62,7 +60,6 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
continue
}
imageName := api.GetImageNameOrDefault(service, project.Name)
imagesToBuild = append(imagesToBuild, imageName)
buildOptions, err := s.toBuildOptions(project, service, imageName, options.SSHs)
if err != nil {
return err
@@ -97,12 +94,6 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
}
_, err = s.doBuild(ctx, project, opts, options.Progress)
if err == nil {
if len(imagesToBuild) > 0 && !options.Quiet {
utils.DisplayScanSuggestMsg()
}
}
return err
}
@@ -136,9 +127,6 @@ func (s *composeService) ensureImagesExists(ctx context.Context, project *types.
return err
}
if len(builtImages) > 0 {
utils.DisplayScanSuggestMsg()
}
for name, digest := range builtImages {
images[name] = digest
}
@@ -249,7 +237,7 @@ func (s *composeService) toBuildOptions(project *types.Project, service types.Se
}
sessionConfig := []session.Attachable{
authprovider.NewDockerAuthProvider(s.stderr()),
authprovider.NewDockerAuthProvider(s.configFile()),
}
if len(sshKeys) > 0 || len(service.Build.SSH) > 0 {
sshAgentProvider, err := sshAgentProvider(append(service.Build.SSH, sshKeys...))
@@ -415,8 +403,8 @@ func useDockerDefaultOrServicePlatform(project *types.Project, service types.Ser
return plats, err
}
if service.Platform != "" && !utils.StringContains(service.Build.Platforms, service.Platform) {
if len(service.Build.Platforms) > 0 {
if service.Platform != "" {
if len(service.Build.Platforms) > 0 && !utils.StringContains(service.Build.Platforms, service.Platform) {
return nil, fmt.Errorf("service.platform %q should be part of the service.build.platforms: %q", service.Platform, service.Build.Platforms)
}
// User defined a service platform and no build platforms, so we should keep the one define on the service level

View File

@@ -95,7 +95,7 @@ func (s *composeService) getDrivers(ctx context.Context) ([]build.DriverInfo, er
dis := make([]build.DriverInfo, len(ng.Nodes))
var f driver.Factory
if ng.Driver != "" {
factories := driver.GetFactories()
factories := driver.GetFactories(true)
for _, fac := range factories {
if fac.Name() == ng.Driver {
f = fac
@@ -103,8 +103,8 @@ func (s *composeService) getDrivers(ctx context.Context) ([]build.DriverInfo, er
}
}
if f == nil {
if f = driver.GetFactory(ng.Driver, true); f == nil {
return nil, fmt.Errorf("failed to find buildx driver %q", ng.Driver)
if f, err = driver.GetFactory(ng.Driver, true); f == nil || err != nil {
return nil, fmt.Errorf("failed to find buildx driver %q, error: %w", ng.Driver, err)
}
}
} else {
@@ -113,7 +113,7 @@ func (s *composeService) getDrivers(ctx context.Context) ([]build.DriverInfo, er
if err != nil {
return nil, err
}
f, err = driver.GetDefaultFactory(ctx, dockerapi, false)
f, err = driver.GetDefaultFactory(ctx, ep, dockerapi, false)
if err != nil {
return nil, err
}
@@ -176,7 +176,7 @@ func (s *composeService) getDrivers(ctx context.Context) ([]build.DriverInfo, er
}
}
d, err := driver.GetDriver(ctx, "buildx_buildkit_"+n.Name, f, dockerapi, imageopt.Auth, kcc, n.Flags, n.Files, n.DriverOpts, n.Platforms, "")
d, err := driver.GetDriver(ctx, "buildx_buildkit_"+n.Name, f, n.Endpoint, dockerapi, imageopt.Auth, kcc, n.Flags, n.Files, n.DriverOpts, n.Platforms, "")
if err != nil {
di.Err = err
return nil

View File

@@ -28,15 +28,15 @@ import (
"github.com/compose-spec/compose-go/types"
buildx "github.com/docker/buildx/build"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command/image/build"
dockertypes "github.com/docker/docker/api/types"
"github.com/docker/docker/cli"
"github.com/docker/docker/builder/remotecontext/urlutil"
"github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/docker/pkg/progress"
"github.com/docker/docker/pkg/streamformatter"
"github.com/docker/docker/pkg/urlutil"
"github.com/hashicorp/go-multierror"
"github.com/pkg/errors"

View File

@@ -23,16 +23,17 @@ import (
"io"
"strings"
"gopkg.in/yaml.v2"
"github.com/compose-spec/compose-go/types"
"github.com/distribution/distribution/v3/reference"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/config/configfile"
"github.com/docker/cli/cli/streams"
moby "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/client"
"github.com/opencontainers/go-digest"
"github.com/pkg/errors"
"gopkg.in/yaml.v2"
"github.com/docker/compose/v2/pkg/api"
)
@@ -40,12 +41,14 @@ import (
// NewComposeService create a local implementation of the compose.Service API
func NewComposeService(dockerCli command.Cli) api.Service {
return &composeService{
dockerCli: dockerCli,
dockerCli: dockerCli,
maxConcurrency: -1,
}
}
type composeService struct {
dockerCli command.Cli
dockerCli command.Cli
maxConcurrency int
}
func (s *composeService) apiClient() client.APIClient {
@@ -56,6 +59,10 @@ func (s *composeService) configFile() *configfile.ConfigFile {
return s.dockerCli.ConfigFile()
}
func (s *composeService) MaxConcurrency(i int) {
s.maxConcurrency = i
}
func (s *composeService) stdout() *streams.Out {
return s.dockerCli.Out()
}
@@ -93,13 +100,34 @@ func getContainerNameWithoutProject(c moby.Container) string {
}
func (s *composeService) Convert(ctx context.Context, project *types.Project, options api.ConvertOptions) ([]byte, error) {
if options.ResolveImageDigests {
info, err := s.apiClient().Info(ctx)
if err != nil {
return nil, err
}
err = project.ResolveImages(func(named reference.Named) (digest.Digest, error) {
auth, err := encodedAuth(named, info, s.configFile())
if err != nil {
return "", err
}
inspect, err := s.apiClient().DistributionInspect(ctx, named.String(), auth)
if err != nil {
return "", err
}
return inspect.Descriptor.Digest, nil
})
if err != nil {
return nil, err
}
}
switch options.Format {
case "json":
return json.MarshalIndent(project, "", " ")
case "yaml":
return yaml.Marshal(project)
default:
return nil, fmt.Errorf("unsupported format %q", options)
return nil, fmt.Errorf("unsupported format %q", options.Format)
}
}

View File

@@ -141,3 +141,14 @@ func (containers Containers) sorted() Containers {
})
return containers
}
func (containers Containers) remove(id string) Containers {
for i, c := range containers {
if c.ID == id {
l := len(containers) - 1
containers[i] = containers[l]
return containers[:l]
}
}
return containers
}

View File

@@ -27,9 +27,11 @@ import (
"github.com/compose-spec/compose-go/types"
"github.com/containerd/containerd/platforms"
moby "github.com/docker/docker/api/types"
containerType "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/network"
specs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/sync/errgroup"
@@ -125,17 +127,13 @@ func updateServices(service *types.ServiceConfig, cnts Containers) {
if len(cnts) == 0 {
return
}
cnt := cnts[0]
serviceName := cnt.Labels[api.ServiceLabel]
if d := getDependentServiceFromMode(service.NetworkMode); d == serviceName {
service.NetworkMode = types.NetworkModeContainerPrefix + cnt.ID
}
if d := getDependentServiceFromMode(service.Ipc); d == serviceName {
service.Ipc = types.NetworkModeContainerPrefix + cnt.ID
}
if d := getDependentServiceFromMode(service.Pid); d == serviceName {
service.Pid = types.NetworkModeContainerPrefix + cnt.ID
for _, str := range []*string{&service.NetworkMode, &service.Ipc, &service.Pid} {
if d := getDependentServiceFromMode(*str); d != "" {
if serviceContainers := cnts.filter(isService(d)); len(serviceContainers) > 0 {
*str = types.NetworkModeContainerPrefix + serviceContainers[0].ID
}
}
}
var links []string
for _, serviceLink := range service.Links {
@@ -180,7 +178,10 @@ 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)
timeoutInSecond := utils.DurationSecondToInt(timeout)
err := c.service.apiClient().ContainerStop(ctx, container.ID, containerType.StopOptions{
Timeout: timeoutInSecond,
})
if err != nil {
return err
}
@@ -222,10 +223,7 @@ func (c *convergence) ensureService(ctx context.Context, project *types.Project,
updated[i] = container
}
next, err := nextContainerNumber(containers)
if err != nil {
return err
}
next := nextContainerNumber(containers)
for i := 0; i < expected-actual; i++ {
// Scale UP
number := next + i
@@ -280,7 +278,7 @@ func containerEvents(containers Containers, eventFunc func(string) progress.Even
return events
}
// ServiceConditionRunningOrHealthy is a service condition on statys running or healthy
// ServiceConditionRunningOrHealthy is a service condition on status running or healthy
const ServiceConditionRunningOrHealthy = "running_or_healthy"
func (s *composeService) waitDependencies(ctx context.Context, project *types.Project, dependencies types.DependsOnConfig) error {
@@ -318,7 +316,8 @@ func (s *composeService) waitDependencies(ctx context.Context, project *types.Pr
case types.ServiceConditionHealthy:
healthy, err := s.isServiceHealthy(ctx, project, dep, false)
if err != nil {
return err
w.Events(containerEvents(containers, progress.ErrorEvent))
return errors.Wrap(err, "dependency failed to start")
}
if healthy {
w.Events(containerEvents(containers, progress.Healthy))
@@ -366,18 +365,23 @@ func shouldWaitForDependency(serviceName string, dependencyConfig types.ServiceD
return true, nil
}
func nextContainerNumber(containers []moby.Container) (int, error) {
func nextContainerNumber(containers []moby.Container) int {
max := 0
for _, c := range containers {
n, err := strconv.Atoi(c.Labels[api.ContainerNumberLabel])
s, ok := c.Labels[api.ContainerNumberLabel]
if !ok {
logrus.Warnf("container %s is missing %s label", c.ID, api.ContainerNumberLabel)
}
n, err := strconv.Atoi(s)
if err != nil {
return 0, err
logrus.Warnf("container %s has invalid %s label: %s", c.ID, api.ContainerNumberLabel, s)
continue
}
if n > max {
max = n
}
}
return max + 1, nil
return max + 1
}
@@ -399,7 +403,7 @@ func (s *composeService) createContainer(ctx context.Context, project *types.Pro
w := progress.ContextWriter(ctx)
eventName := "Container " + name
w.Event(progress.CreatingEvent(eventName))
container, err = s.createMobyContainer(ctx, project, service, name, number, nil, autoRemove, useNetworkAliases, attachStdin)
container, err = s.createMobyContainer(ctx, project, service, name, number, nil, autoRemove, useNetworkAliases, attachStdin, w)
if err != nil {
return
}
@@ -412,7 +416,8 @@ 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)
timeoutInSecond := utils.DurationSecondToInt(timeout)
err := s.apiClient().ContainerStop(ctx, replaced.ID, containerType.StopOptions{Timeout: timeoutInSecond})
if err != nil {
return created, err
}
@@ -432,7 +437,7 @@ func (s *composeService) recreateContainer(ctx context.Context, project *types.P
inherited = &replaced
}
name = getContainerName(project.Name, service, number)
created, err = s.createMobyContainer(ctx, project, service, name, number, inherited, false, true, false)
created, err = s.createMobyContainer(ctx, project, service, name, number, inherited, false, true, false, w)
if err != nil {
return created, err
}
@@ -470,16 +475,20 @@ func (s *composeService) startContainer(ctx context.Context, container moby.Cont
}
func (s *composeService) createMobyContainer(ctx context.Context, project *types.Project, service types.ServiceConfig,
name string, number int, inherit *moby.Container, autoRemove bool, useNetworkAliases bool, attachStdin bool) (moby.Container, error) {
name string, number int, inherit *moby.Container, autoRemove bool, useNetworkAliases bool, attachStdin bool, w progress.Writer) (moby.Container, error) {
var created moby.Container
containerConfig, hostConfig, networkingConfig, err := s.getCreateOptions(ctx, project, service, number, inherit, autoRemove, attachStdin)
if err != nil {
return created, err
}
platform := service.Platform
if platform == "" {
platform = project.Environment["DOCKER_DEFAULT_PLATFORM"]
}
var plat *specs.Platform
if service.Platform != "" {
if platform != "" {
var p specs.Platform
p, err = platforms.Parse(service.Platform)
p, err = platforms.Parse(platform)
if err != nil {
return created, err
}
@@ -489,6 +498,13 @@ func (s *composeService) createMobyContainer(ctx context.Context, project *types
if err != nil {
return created, err
}
for _, warning := range response.Warnings {
w.Event(progress.Event{
ID: service.Name,
Status: progress.Warning,
Text: warning,
})
}
inspectedContainer, err := s.apiClient().ContainerInspect(ctx, response.ID)
if err != nil {
return created, err
@@ -630,7 +646,7 @@ func (s *composeService) connectContainerToNetwork(ctx context.Context, id strin
}
func (s *composeService) isServiceHealthy(ctx context.Context, project *types.Project, service string, fallbackRunning bool) (bool, error) {
containers, err := s.getContainers(ctx, project.Name, oneOffExclude, false, service)
containers, err := s.getContainers(ctx, project.Name, oneOffExclude, true, service)
if err != nil {
return false, err
}
@@ -648,6 +664,10 @@ func (s *composeService) isServiceHealthy(ctx context.Context, project *types.Pr
return container.State != nil && container.State.Status == "running", nil
}
if container.State.Status == "exited" {
return false, fmt.Errorf("container for service %q exited (%d)", service, container.State.ExitCode)
}
if container.State == nil || container.State.Health == nil {
return false, fmt.Errorf("container for service %q has no healthcheck configured", service)
}
@@ -711,21 +731,17 @@ func (s *composeService) startService(ctx context.Context, project *types.Projec
}
w := progress.ContextWriter(ctx)
eg, ctx := errgroup.WithContext(ctx)
for _, container := range containers {
if container.State == ContainerRunning {
continue
}
container := container
eg.Go(func() error {
eventName := getContainerProgressName(container)
w.Event(progress.StartingEvent(eventName))
err := s.apiClient().ContainerStart(ctx, container.ID, moby.ContainerStartOptions{})
if err == nil {
w.Event(progress.StartedEvent(eventName))
}
eventName := getContainerProgressName(container)
w.Event(progress.StartingEvent(eventName))
err := s.apiClient().ContainerStart(ctx, container.ID, moby.ContainerStartOptions{})
if err != nil {
return err
})
}
w.Event(progress.StartedEvent(eventName))
}
return eg.Wait()
return nil
}

View File

@@ -26,7 +26,7 @@ import (
moby "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/golang/mock/gomock"
"gotest.tools/assert"
"gotest.tools/v3/assert"
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/mocks"

View File

@@ -27,7 +27,6 @@ import (
"strconv"
"strings"
"github.com/compose-spec/compose-go/types"
moby "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/blkiodev"
"github.com/docker/docker/api/types/container"
@@ -42,6 +41,8 @@ import (
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/compose-spec/compose-go/types"
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/progress"
"github.com/docker/compose/v2/pkg/utils"
@@ -259,7 +260,7 @@ func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project,
stdinOpen = service.StdinOpen
)
volumeMounts, binds, mounts, err := s.buildContainerVolumes(ctx, *p, service, inherit)
binds, mounts, err := s.buildContainerVolumes(ctx, *p, service, inherit)
if err != nil {
return nil, nil, nil, err
}
@@ -288,7 +289,6 @@ func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project,
StopSignal: service.StopSignal,
Env: ToMobyEnv(env),
Healthcheck: ToMobyHealthCheck(service.HealthCheck),
Volumes: volumeMounts,
StopTimeout: ToSeconds(service.StopGracePeriod),
}
@@ -397,6 +397,7 @@ func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project,
LogConfig: logConfig,
GroupAdd: service.GroupAdd,
Links: links,
OomScoreAdj: int(service.OomScoreAdj),
}
return &containerConfig, &hostConfig, networkConfig, nil
@@ -488,13 +489,27 @@ func getRestartPolicy(service types.ServiceConfig) container.RestartPolicy {
attempts = int(*policy.MaxAttempts)
}
restart = container.RestartPolicy{
Name: policy.Condition,
Name: mapRestartPolicyCondition(policy.Condition),
MaximumRetryCount: attempts,
}
}
return restart
}
func mapRestartPolicyCondition(condition string) string {
// map definitions of deploy.restart_policy to engine definitions
switch condition {
case "none", "no":
return "no"
case "on-failure", "unless-stopped":
return condition
case "any", "always":
return "always"
default:
return condition
}
}
func getDeployResources(s types.ServiceConfig) container.Resources {
var swappiness *int64
if s.MemSwappiness != 0 {
@@ -514,7 +529,8 @@ func getDeployResources(s types.ServiceConfig) container.Resources {
CPURealtimePeriod: s.CPURTPeriod,
CPURealtimeRuntime: s.CPURTRuntime,
CPUShares: s.CPUShares,
CPUPercent: int64(s.CPUS * 100),
NanoCPUs: int64(s.CPUS * 1e9),
CPUPercent: int64(s.CPUPercent * 100),
CpusetCpus: s.CPUSet,
DeviceCgroupRules: s.DeviceCgroupRules,
}
@@ -578,6 +594,12 @@ func setReservations(reservations *types.Resource, resources *container.Resource
if reservations == nil {
return
}
// Cpu reservation is a swarm option and PIDs is only a limit
// So we only need to map memory reservation and devices
if reservations.MemoryBytes != 0 {
resources.MemoryReservation = int64(reservations.MemoryBytes)
}
for _, device := range reservations.Devices {
resources.DeviceRequests = append(resources.DeviceRequests, container.DeviceRequest{
Capabilities: [][]string{device.Capabilities},
@@ -709,30 +731,32 @@ func getDependentServiceFromMode(mode string) string {
return ""
}
func (s *composeService) buildContainerVolumes(ctx context.Context, p types.Project, service types.ServiceConfig,
inherit *moby.Container) (map[string]struct{}, []string, []mount.Mount, error) {
var mounts = []mount.Mount{}
func (s *composeService) buildContainerVolumes(
ctx context.Context,
p types.Project,
service types.ServiceConfig,
inherit *moby.Container,
) ([]string, []mount.Mount, error) {
var mounts []mount.Mount
var binds []string
image := api.GetImageNameOrDefault(service, p.Name)
imgInspect, _, err := s.apiClient().ImageInspectWithRaw(ctx, image)
if err != nil {
return nil, nil, nil, err
return nil, nil, err
}
mountOptions, err := buildContainerMountOptions(p, service, imgInspect, inherit)
if err != nil {
return nil, nil, nil, err
return nil, nil, err
}
volumeMounts := map[string]struct{}{}
binds := []string{}
MOUNTS:
for _, m := range mountOptions {
if m.Type == mount.TypeNamedPipe {
mounts = append(mounts, m)
continue
}
volumeMounts[m.Target] = struct{}{}
if m.Type == mount.TypeBind {
// `Mount` is preferred but does not offer option to created host path if missing
// so `Bind` API is used here with raw volume string
@@ -752,7 +776,7 @@ MOUNTS:
}
mounts = append(mounts, m)
}
return volumeMounts, binds, mounts, nil
return binds, mounts, nil
}
func buildContainerMountOptions(p types.Project, s types.ServiceConfig, img moby.ImageInspect, inherit *moby.Container) ([]mount.Mount, error) {
@@ -968,7 +992,7 @@ func buildMountOptions(project types.Project, volume types.ServiceVolumeConfig)
logrus.Warnf("mount of type `bind` should not define `volume` option")
}
if volume.Tmpfs != nil {
logrus.Warnf("mount of type `tmpfs` should not define `tmpfs` option")
logrus.Warnf("mount of type `bind` should not define `tmpfs` option")
}
return buildBindOption(volume.Bind), nil, nil
case "volume":
@@ -1023,7 +1047,7 @@ func buildTmpfsOptions(tmpfs *types.ServiceVolumeTmpfs) *mount.TmpfsOptions {
}
return &mount.TmpfsOptions{
SizeBytes: int64(tmpfs.Size),
// Mode: , // FIXME missing from model ?
Mode: os.FileMode(tmpfs.Mode),
}
}
@@ -1155,7 +1179,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.CreateOptions{
Labels: volume.Labels,
Name: volume.Name,
Driver: volume.Driver,

View File

@@ -24,7 +24,7 @@ import (
"github.com/compose-spec/compose-go/types"
testify "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gotest.tools/assert"
"gotest.tools/v3/assert"
)
var project = types.Project{

View File

@@ -22,8 +22,11 @@ import (
"strings"
"time"
"github.com/docker/compose/v2/pkg/utils"
"github.com/compose-spec/compose-go/types"
moby "github.com/docker/docker/api/types"
containerType "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/errdefs"
"github.com/pkg/errors"
@@ -241,7 +244,8 @@ 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)
timeoutInSecond := utils.DurationSecondToInt(timeout)
err := s.apiClient().ContainerStop(ctx, container.ID, containerType.StopOptions{Timeout: timeoutInSecond})
if err != nil {
w.Event(progress.ErrorMessageEvent(eventName, "Error while Stopping"))
return err
@@ -270,7 +274,7 @@ func (s *composeService) removeContainers(ctx context.Context, w progress.Writer
Force: true,
RemoveVolumes: volumes,
})
if err != nil {
if err != nil && !errdefs.IsNotFound(err) {
w.Event(progress.ErrorMessageEvent(eventName, "Error while Removing"))
return err
}

View File

@@ -24,6 +24,7 @@ import (
"github.com/compose-spec/compose-go/types"
moby "github.com/docker/docker/api/types"
containerType "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/volume"
"github.com/docker/docker/errdefs"
@@ -53,7 +54,7 @@ func TestDown(t *testing.T) {
testContainer("service_orphan", "321", true),
}, nil)
api.EXPECT().VolumeList(gomock.Any(), filters.NewArgs(projectFilter(strings.ToLower(testProject)))).
Return(volume.VolumeListOKBody{}, nil)
Return(volume.ListResponse{}, nil)
// network names are not guaranteed to be unique, ensure Compose handles
// cleanup properly if duplicates are inadvertently created
@@ -63,9 +64,10 @@ func TestDown(t *testing.T) {
{ID: "def456", Name: "myProject_default"},
}, nil)
api.EXPECT().ContainerStop(gomock.Any(), "123", nil).Return(nil)
api.EXPECT().ContainerStop(gomock.Any(), "456", nil).Return(nil)
api.EXPECT().ContainerStop(gomock.Any(), "789", nil).Return(nil)
stopOptions := containerType.StopOptions{}
api.EXPECT().ContainerStop(gomock.Any(), "123", stopOptions).Return(nil)
api.EXPECT().ContainerStop(gomock.Any(), "456", stopOptions).Return(nil)
api.EXPECT().ContainerStop(gomock.Any(), "789", stopOptions).Return(nil)
api.EXPECT().ContainerRemove(gomock.Any(), "123", moby.ContainerRemoveOptions{Force: true}).Return(nil)
api.EXPECT().ContainerRemove(gomock.Any(), "456", moby.ContainerRemoveOptions{Force: true}).Return(nil)
@@ -102,13 +104,14 @@ func TestDownRemoveOrphans(t *testing.T) {
testContainer("service_orphan", "321", true),
}, nil)
api.EXPECT().VolumeList(gomock.Any(), filters.NewArgs(projectFilter(strings.ToLower(testProject)))).
Return(volume.VolumeListOKBody{}, nil)
Return(volume.ListResponse{}, nil)
api.EXPECT().NetworkList(gomock.Any(), moby.NetworkListOptions{Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject)))}).
Return([]moby.NetworkResource{{Name: "myProject_default"}}, nil)
api.EXPECT().ContainerStop(gomock.Any(), "123", nil).Return(nil)
api.EXPECT().ContainerStop(gomock.Any(), "789", nil).Return(nil)
api.EXPECT().ContainerStop(gomock.Any(), "321", nil).Return(nil)
stopOptions := containerType.StopOptions{}
api.EXPECT().ContainerStop(gomock.Any(), "123", stopOptions).Return(nil)
api.EXPECT().ContainerStop(gomock.Any(), "789", stopOptions).Return(nil)
api.EXPECT().ContainerStop(gomock.Any(), "321", stopOptions).Return(nil)
api.EXPECT().ContainerRemove(gomock.Any(), "123", moby.ContainerRemoveOptions{Force: true}).Return(nil)
api.EXPECT().ContainerRemove(gomock.Any(), "789", moby.ContainerRemoveOptions{Force: true}).Return(nil)
@@ -137,13 +140,13 @@ func TestDownRemoveVolumes(t *testing.T) {
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return(
[]moby.Container{testContainer("service1", "123", false)}, nil)
api.EXPECT().VolumeList(gomock.Any(), filters.NewArgs(projectFilter(strings.ToLower(testProject)))).
Return(volume.VolumeListOKBody{
Volumes: []*moby.Volume{{Name: "myProject_volume"}},
Return(volume.ListResponse{
Volumes: []*volume.Volume{{Name: "myProject_volume"}},
}, nil)
api.EXPECT().NetworkList(gomock.Any(), moby.NetworkListOptions{Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject)))}).
Return(nil, nil)
api.EXPECT().ContainerStop(gomock.Any(), "123", nil).Return(nil)
api.EXPECT().ContainerStop(gomock.Any(), "123", containerType.StopOptions{}).Return(nil)
api.EXPECT().ContainerRemove(gomock.Any(), "123", moby.ContainerRemoveOptions{Force: true, RemoveVolumes: true}).Return(nil)
api.EXPECT().VolumeRemove(gomock.Any(), "myProject_volume", true).Return(nil)
@@ -274,8 +277,8 @@ func TestDownRemoveImages_NoLabel(t *testing.T) {
[]moby.Container{container}, nil)
api.EXPECT().VolumeList(gomock.Any(), filters.NewArgs(projectFilter(strings.ToLower(testProject)))).
Return(volume.VolumeListOKBody{
Volumes: []*moby.Volume{{Name: "myProject_volume"}},
Return(volume.ListResponse{
Volumes: []*volume.Volume{{Name: "myProject_volume"}},
}, nil)
api.EXPECT().NetworkList(gomock.Any(), moby.NetworkListOptions{Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject)))}).
Return(nil, nil)
@@ -292,7 +295,7 @@ func TestDownRemoveImages_NoLabel(t *testing.T) {
api.EXPECT().ImageInspectWithRaw(gomock.Any(), "testproject-service1").
Return(moby.ImageInspect{}, nil, nil)
api.EXPECT().ContainerStop(gomock.Any(), "123", nil).Return(nil)
api.EXPECT().ContainerStop(gomock.Any(), "123", containerType.StopOptions{}).Return(nil)
api.EXPECT().ContainerRemove(gomock.Any(), "123", moby.ContainerRemoveOptions{Force: true}).Return(nil)
api.EXPECT().ImageRemove(gomock.Any(), "testproject-service1:latest", moby.ImageRemoveOptions{}).Return(nil, nil)

View File

@@ -19,7 +19,7 @@ package compose
import (
"testing"
"gotest.tools/assert"
"gotest.tools/v3/assert"
)
func Test_EnvResolverWithCase(t *testing.T) {

View File

@@ -22,6 +22,7 @@ import (
"strings"
"sync"
"github.com/distribution/distribution/v3/reference"
moby "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/errdefs"
@@ -94,10 +95,13 @@ func (s *composeService) getImages(ctx context.Context, images []string) (map[st
tag := ""
repository := ""
if len(inspect.RepoTags) > 0 {
repotag := strings.Split(inspect.RepoTags[0], ":")
repository = repotag[0]
if len(repotag) > 1 {
tag = repotag[1]
ref, err := reference.ParseDockerRef(inspect.RepoTags[0])
if err != nil {
return err
}
repository = reference.FamiliarName(ref)
if tagged, ok := ref.(reference.Tagged); ok {
tag = tagged.Tag()
}
}
l.Lock()

View File

@@ -54,7 +54,7 @@ func TestKillAll(t *testing.T) {
}).Return(
[]moby.Container{testContainer("service1", "123", false), testContainer("service1", "456", false), testContainer("service2", "789", false)}, nil)
api.EXPECT().VolumeList(gomock.Any(), filters.NewArgs(projectFilter(strings.ToLower(testProject)))).
Return(volume.VolumeListOKBody{}, nil)
Return(volume.ListResponse{}, nil)
api.EXPECT().NetworkList(gomock.Any(), moby.NetworkListOptions{Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject)))}).
Return([]moby.NetworkResource{
{ID: "abc123", Name: "testProject_default"},
@@ -87,7 +87,7 @@ func TestKillSignal(t *testing.T) {
ctx := context.Background()
api.EXPECT().ContainerList(ctx, listOptions).Return([]moby.Container{testContainer(serviceName, "123", false)}, nil)
api.EXPECT().VolumeList(gomock.Any(), filters.NewArgs(projectFilter(strings.ToLower(testProject)))).
Return(volume.VolumeListOKBody{}, nil)
Return(volume.ListResponse{}, nil)
api.EXPECT().NetworkList(gomock.Any(), moby.NetworkListOptions{Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject)))}).
Return([]moby.NetworkResource{
{ID: "abc123", Name: "testProject_default"},

View File

@@ -66,18 +66,20 @@ func (s *composeService) Logs(
if options.Follow {
printer := newLogPrinter(consumer)
eg.Go(func() error {
for _, c := range containers {
printer.HandleEvent(api.ContainerEvent{
Type: api.ContainerEventAttach,
Container: getContainerNameWithoutProject(c),
Service: c.Labels[api.ServiceLabel],
})
}
return nil
_, err := printer.Run(false, "", nil)
return err
})
for _, c := range containers {
printer.HandleEvent(api.ContainerEvent{
Type: api.ContainerEventAttach,
Container: getContainerNameWithoutProject(c),
Service: c.Labels[api.ServiceLabel],
})
}
eg.Go(func() error {
return s.watchContainers(ctx, projectName, options.Services, printer.HandleEvent, containers, func(c types.Container) error {
err := s.watchContainers(ctx, projectName, options.Services, nil, printer.HandleEvent, containers, func(c types.Container) error {
printer.HandleEvent(api.ContainerEvent{
Type: api.ContainerEventAttach,
Container: getContainerNameWithoutProject(c),
@@ -85,10 +87,7 @@ func (s *composeService) Logs(
})
return s.logContainers(ctx, consumer, c, options)
})
})
eg.Go(func() error {
_, err := printer.Run(ctx, false, "", nil)
printer.Stop()
return err
})
}
@@ -102,7 +101,6 @@ func (s *composeService) logContainers(ctx context.Context, consumer api.LogCons
return err
}
service := c.Labels[api.ServiceLabel]
r, err := s.apiClient().ContainerLogs(ctx, cnt.ID, types.ContainerLogsOptions{
ShowStdout: true,
ShowStderr: true,
@@ -119,7 +117,7 @@ func (s *composeService) logContainers(ctx context.Context, consumer api.LogCons
name := getContainerNameWithoutProject(c)
w := utils.GetWriter(func(line string) {
consumer.Log(name, service, line)
consumer.Log(name, line)
})
if cnt.Config.Tty {
_, err = io.Copy(w, r)

View File

@@ -98,7 +98,7 @@ func TestComposeService_Logs_Demux(t *testing.T) {
require.Equal(
t,
[]string{"hello stdout", "hello stderr"},
consumer.LogsForContainer("service", "c"),
consumer.LogsForContainer("c"),
)
}
@@ -169,36 +169,37 @@ func TestComposeService_Logs_ServiceFiltering(t *testing.T) {
err := tested.Logs(ctx, name, consumer, opts)
require.NoError(t, err)
require.Equal(t, []string{"hello c1"}, consumer.LogsForContainer("serviceA", "c1"))
require.Equal(t, []string{"hello c2"}, consumer.LogsForContainer("serviceA", "c2"))
require.Empty(t, consumer.LogsForContainer("serviceB", "c3"))
require.Equal(t, []string{"hello c4"}, consumer.LogsForContainer("serviceC", "c4"))
require.Equal(t, []string{"hello c1"}, consumer.LogsForContainer("c1"))
require.Equal(t, []string{"hello c2"}, consumer.LogsForContainer("c2"))
require.Empty(t, consumer.LogsForContainer("c3"))
require.Equal(t, []string{"hello c4"}, consumer.LogsForContainer("c4"))
}
type testLogConsumer struct {
mu sync.Mutex
// logs is keyed by service, then container; values are log lines
logs map[string]map[string][]string
// logs is keyed container; values are log lines
logs map[string][]string
}
func (l *testLogConsumer) Log(containerName, service, message string) {
func (l *testLogConsumer) Log(containerName, message string) {
l.mu.Lock()
defer l.mu.Unlock()
if l.logs == nil {
l.logs = make(map[string]map[string][]string)
l.logs = make(map[string][]string)
}
if l.logs[service] == nil {
l.logs[service] = make(map[string][]string)
}
l.logs[service][containerName] = append(l.logs[service][containerName], message)
l.logs[containerName] = append(l.logs[containerName], message)
}
func (l *testLogConsumer) Err(containerName, message string) {
l.Log(containerName, message)
}
func (l *testLogConsumer) Status(containerName, msg string) {}
func (l *testLogConsumer) Register(containerName string) {}
func (l *testLogConsumer) LogsForContainer(svc string, containerName string) []string {
func (l *testLogConsumer) LogsForContainer(containerName string) []string {
l.mu.Lock()
defer l.mu.Unlock()
return l.logs[svc][containerName]
return l.logs[containerName]
}

View File

@@ -27,7 +27,7 @@ import (
"github.com/docker/docker/api/types/filters"
)
func (s *composeService) Port(ctx context.Context, projectName string, service string, port int, options api.PortOptions) (string, int, error) {
func (s *composeService) Port(ctx context.Context, projectName string, service string, port uint16, options api.PortOptions) (string, int, error) {
projectName = strings.ToLower(projectName)
list, err := s.apiClient().ContainerList(ctx, moby.ContainerListOptions{
Filters: filters.NewArgs(
@@ -40,13 +40,27 @@ func (s *composeService) Port(ctx context.Context, projectName string, service s
return "", 0, err
}
if len(list) == 0 {
return "", 0, fmt.Errorf("no container found for %s_%d", service, options.Index)
return "", 0, fmt.Errorf("no container found for %s%s%d", service, api.Separator, options.Index)
}
container := list[0]
for _, p := range container.Ports {
if p.PrivatePort == uint16(port) && p.Type == options.Protocol {
if p.PrivatePort == port && p.Type == options.Protocol {
return p.IP, int(p.PublicPort), nil
}
}
return "", 0, err
return "", 0, portNotFoundError(options.Protocol, port, container)
}
func portNotFoundError(protocol string, port uint16, ctr moby.Container) error {
formatPort := func(protocol string, port uint16) string {
return fmt.Sprintf("%d/%s", port, protocol)
}
var containerPorts []string
for _, p := range ctr.Ports {
containerPorts = append(containerPorts, formatPort(p.Type, p.PublicPort))
}
name := strings.TrimPrefix(ctr.Names[0], "/")
return fmt.Errorf("no port %s for container %s: %s", formatPort(protocol, port), name, strings.Join(containerPorts, ", "))
}

View File

@@ -17,7 +17,6 @@
package compose
import (
"context"
"fmt"
"github.com/docker/compose/v2/pkg/api"
@@ -26,21 +25,25 @@ import (
// logPrinter watch application containers an collect their logs
type logPrinter interface {
HandleEvent(event api.ContainerEvent)
Run(ctx context.Context, cascadeStop bool, exitCodeFrom string, stopFn func() error) (int, error)
Run(cascadeStop bool, exitCodeFrom string, stopFn func() error) (int, error)
Cancel()
Stop()
}
type printer struct {
queue chan api.ContainerEvent
consumer api.LogConsumer
stopCh chan struct{}
}
// newLogPrinter builds a LogPrinter passing containers logs to LogConsumer
func newLogPrinter(consumer api.LogConsumer) logPrinter {
queue := make(chan api.ContainerEvent)
stopCh := make(chan struct{}, 1) // printer MAY stop on his own, so Stop MUST not be blocking
printer := printer{
consumer: consumer,
queue: queue,
stopCh: stopCh,
}
return &printer
}
@@ -51,12 +54,16 @@ func (p *printer) Cancel() {
}
}
func (p *printer) Stop() {
p.stopCh <- struct{}{}
}
func (p *printer) HandleEvent(event api.ContainerEvent) {
p.queue <- event
}
//nolint:gocyclo
func (p *printer) Run(ctx context.Context, cascadeStop bool, exitCodeFrom string, stopFn func() error) (int, error) {
func (p *printer) Run(cascadeStop bool, exitCodeFrom string, stopFn func() error) (int, error) {
var (
aborting bool
exitCode int
@@ -64,8 +71,8 @@ func (p *printer) Run(ctx context.Context, cascadeStop bool, exitCodeFrom string
containers := map[string]struct{}{}
for {
select {
case <-ctx.Done():
return exitCode, ctx.Err()
case <-p.stopCh:
return exitCode, nil
case event := <-p.queue:
container := event.Container
switch event.Type {
@@ -108,7 +115,11 @@ func (p *printer) Run(ctx context.Context, cascadeStop bool, exitCodeFrom string
}
case api.ContainerEventLog:
if !aborting {
p.consumer.Log(container, event.Service, event.Line)
p.consumer.Log(container, event.Line)
}
case api.ContainerEventErr:
if !aborting {
p.consumer.Err(container, event.Line)
}
}
}

View File

@@ -32,7 +32,7 @@ func (s *composeService) Ps(ctx context.Context, projectName string, options api
if options.All {
oneOff = oneOffInclude
}
containers, err := s.getContainers(ctx, projectName, oneOff, true, options.Services...)
containers, err := s.getContainers(ctx, projectName, oneOff, options.All, options.Services...)
if err != nil {
return nil, err
}
@@ -91,10 +91,13 @@ func (s *composeService) Ps(ctx context.Context, projectName string, options api
summary[i] = api.ContainerSummary{
ID: container.ID,
Name: getCanonicalContainerName(container),
Image: container.Image,
Project: container.Labels[api.ProjectLabel],
Service: container.Labels[api.ServiceLabel],
Command: container.Command,
State: container.State,
Status: container.Status,
Created: container.Created,
Health: health,
ExitCode: exitCode,
Publishers: publishers,

View File

@@ -46,12 +46,12 @@ func TestPs(t *testing.T) {
ctx := context.Background()
args := filters.NewArgs(projectFilter(strings.ToLower(testProject)))
args.Add("label", "com.docker.compose.oneoff=False")
listOpts := moby.ContainerListOptions{Filters: args, All: true}
listOpts := moby.ContainerListOptions{Filters: args, All: false}
c1, inspect1 := containerDetails("service1", "123", "running", "healthy", 0)
c2, inspect2 := containerDetails("service1", "456", "running", "", 0)
c2.Ports = []moby.Port{{PublicPort: 80, PrivatePort: 90, IP: "localhost"}}
c3, inspect3 := containerDetails("service2", "789", "exited", "", 130)
api.EXPECT().VolumeList(ctx, gomock.Any()).Return(volume.VolumeListOKBody{}, nil)
api.EXPECT().VolumeList(ctx, gomock.Any()).Return(volume.ListResponse{}, nil)
api.EXPECT().NetworkList(ctx, gomock.Any()).Return([]moby.NetworkResource{}, nil)
api.EXPECT().ContainerList(ctx, listOpts).Return([]moby.Container{c1, c2, c3}, nil)
api.EXPECT().ContainerInspect(anyCancellableContext(), "123").Return(inspect1, nil)
@@ -61,10 +61,13 @@ func TestPs(t *testing.T) {
containers, err := tested.Ps(ctx, strings.ToLower(testProject), compose.PsOptions{})
expected := []compose.ContainerSummary{
{ID: "123", Name: "123", Project: strings.ToLower(testProject), Service: "service1", State: "running", Health: "healthy", Publishers: nil},
{ID: "456", Name: "456", Project: strings.ToLower(testProject), Service: "service1", State: "running", Health: "", Publishers: []compose.PortPublisher{{URL: "localhost", TargetPort: 90,
PublishedPort: 80}}},
{ID: "789", Name: "789", Project: strings.ToLower(testProject), Service: "service2", State: "exited", Health: "", ExitCode: 130, Publishers: nil},
{ID: "123", Name: "123", Image: "foo", Project: strings.ToLower(testProject), Service: "service1",
State: "running", Health: "healthy", Publishers: nil},
{ID: "456", Name: "456", Image: "foo", Project: strings.ToLower(testProject), Service: "service1",
State: "running", Health: "",
Publishers: []compose.PortPublisher{{URL: "localhost", TargetPort: 90, PublishedPort: 80}}},
{ID: "789", Name: "789", Image: "foo", Project: strings.ToLower(testProject), Service: "service2",
State: "exited", Health: "", ExitCode: 130, Publishers: nil},
}
assert.NilError(t, err)
assert.DeepEqual(t, containers, expected)
@@ -74,6 +77,7 @@ func containerDetails(service string, id string, status string, health string, e
container := moby.Container{
ID: id,
Names: []string{"/" + id},
Image: "foo",
Labels: containerLabels(service, false),
State: status,
}

View File

@@ -31,6 +31,7 @@ import (
moby "github.com/docker/docker/api/types"
"github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/docker/registry"
"github.com/hashicorp/go-multierror"
"golang.org/x/sync/errgroup"
"github.com/docker/compose/v2/pkg/api"
@@ -46,7 +47,7 @@ func (s *composeService) Pull(ctx context.Context, project *types.Project, optio
})
}
func (s *composeService) pull(ctx context.Context, project *types.Project, opts api.PullOptions) error {
func (s *composeService) pull(ctx context.Context, project *types.Project, opts api.PullOptions) error { //nolint:gocyclo
info, err := s.apiClient().Info(ctx)
if err != nil {
return err
@@ -63,13 +64,16 @@ func (s *composeService) pull(ctx context.Context, project *types.Project, opts
w := progress.ContextWriter(ctx)
eg, ctx := errgroup.WithContext(ctx)
eg.SetLimit(s.maxConcurrency)
var mustBuild []string
var (
mustBuild []string
pullErrors = make([]error, len(project.Services))
imagesBeingPulled = map[string]string{}
)
imagesBeingPulled := map[string]string{}
for _, service := range project.Services {
service := service
for i, service := range project.Services {
i, service := i, service
if service.Image == "" {
w.Event(progress.Event{
ID: service.Name,
@@ -110,15 +114,16 @@ func (s *composeService) pull(ctx context.Context, project *types.Project, opts
imagesBeingPulled[service.Image] = service.Name
eg.Go(func() error {
_, err := s.pullServiceImage(ctx, service, info, s.configFile(), w, false)
_, err := s.pullServiceImage(ctx, service, info, s.configFile(), w, false, project.Environment["DOCKER_DEFAULT_PLATFORM"])
if err != nil {
if !opts.IgnoreFailures {
if service.Build != nil {
mustBuild = append(mustBuild, service.Name)
}
pullErrors[i] = err
if service.Build != nil {
mustBuild = append(mustBuild, service.Name)
}
if !opts.IgnoreFailures && service.Build == nil {
// fail fast if image can't be pulled nor built
return err
}
w.TailMsgf("Pulling %s: %s", service.Name, err.Error())
}
return nil
})
@@ -126,11 +131,17 @@ func (s *composeService) pull(ctx context.Context, project *types.Project, opts
err = eg.Wait()
if !opts.IgnoreFailures && len(mustBuild) > 0 {
if len(mustBuild) > 0 {
w.TailMsgf("WARNING: Some service image(s) must be built from source by running:\n docker compose build %s", strings.Join(mustBuild, " "))
}
return err
if err != nil {
return err
}
if opts.IgnoreFailures {
return nil
}
return multierror.Append(nil, pullErrors...).ErrorOrNil()
}
func imageAlreadyPresent(serviceImage string, localImages map[string]string) bool {
@@ -146,7 +157,8 @@ func imageAlreadyPresent(serviceImage string, localImages map[string]string) boo
return ok && tagged.Tag() != "latest"
}
func (s *composeService) pullServiceImage(ctx context.Context, service types.ServiceConfig, info moby.Info, configFile driver.Auth, w progress.Writer, quietPull bool) (string, error) {
func (s *composeService) pullServiceImage(ctx context.Context, service types.ServiceConfig, info moby.Info,
configFile driver.Auth, w progress.Writer, quietPull bool, defaultPlatform string) (string, error) {
w.Event(progress.Event{
ID: service.Name,
Status: progress.Working,
@@ -157,29 +169,19 @@ func (s *composeService) pullServiceImage(ctx context.Context, service types.Ser
return "", err
}
repoInfo, err := registry.ParseRepositoryInfo(ref)
encodedAuth, err := encodedAuth(ref, info, configFile)
if err != nil {
return "", err
}
key := repoInfo.Index.Name
if repoInfo.Index.Official {
key = info.IndexServerAddress
}
authConfig, err := configFile.GetAuthConfig(key)
if err != nil {
return "", err
}
buf, err := json.Marshal(authConfig)
if err != nil {
return "", err
platform := service.Platform
if platform == "" {
platform = defaultPlatform
}
stream, err := s.apiClient().ImagePull(ctx, service.Image, moby.ImagePullOptions{
RegistryAuth: base64.URLEncoding.EncodeToString(buf),
Platform: service.Platform,
RegistryAuth: encodedAuth,
Platform: platform,
})
// check if has error and the service has a build section
@@ -231,6 +233,29 @@ func (s *composeService) pullServiceImage(ctx context.Context, service types.Ser
return inspected.ID, nil
}
func encodedAuth(ref reference.Named, info moby.Info, configFile driver.Auth) (string, error) {
repoInfo, err := registry.ParseRepositoryInfo(ref)
if err != nil {
return "", err
}
key := repoInfo.Index.Name
if repoInfo.Index.Official {
key = info.IndexServerAddress
}
authConfig, err := configFile.GetAuthConfig(key)
if err != nil {
return "", err
}
buf, err := json.Marshal(authConfig)
if err != nil {
return "", err
}
return base64.URLEncoding.EncodeToString(buf), nil
}
func (s *composeService) pullRequiredImages(ctx context.Context, project *types.Project, images map[string]string, quietPull bool) error {
info, err := s.apiClient().Info(ctx)
if err != nil {
@@ -265,32 +290,43 @@ func (s *composeService) pullRequiredImages(ctx context.Context, project *types.
return progress.Run(ctx, func(ctx context.Context) error {
w := progress.ContextWriter(ctx)
eg, ctx := errgroup.WithContext(ctx)
eg.SetLimit(s.maxConcurrency)
pulledImages := make([]string, len(needPull))
for i, service := range needPull {
i, service := i, service
eg.Go(func() error {
id, err := s.pullServiceImage(ctx, service, info, s.configFile(), w, quietPull)
id, err := s.pullServiceImage(ctx, service, info, s.configFile(), w, quietPull, project.Environment["DOCKER_DEFAULT_PLATFORM"])
pulledImages[i] = id
if err != nil && service.Build != nil {
if err != nil && isServiceImageToBuild(service, project.Services) {
// image can be built, so we can ignore pull failure
return nil
}
return err
})
}
err := eg.Wait()
for i, service := range needPull {
if pulledImages[i] != "" {
images[service.Image] = pulledImages[i]
}
}
err := eg.Wait()
if err != nil {
return err
}
return err
})
}
func isServiceImageToBuild(service types.ServiceConfig, services []types.ServiceConfig) bool {
if service.Build != nil {
return true
}
for _, depService := range services {
if depService.Image == service.Image && depService.Build != nil {
return true
}
}
return false
}
func toPullProgressEvent(parent string, jm jsonmessage.JSONMessage, w progress.Writer) {
if jm.ID == "" || jm.Progress == nil {
return

View File

@@ -37,6 +37,9 @@ import (
)
func (s *composeService) Push(ctx context.Context, project *types.Project, options api.PushOptions) error {
if options.Quiet {
return s.push(ctx, project, options)
}
return progress.Run(ctx, func(ctx context.Context) error {
return s.push(ctx, project, options)
})
@@ -44,6 +47,7 @@ func (s *composeService) Push(ctx context.Context, project *types.Project, optio
func (s *composeService) push(ctx context.Context, project *types.Project, options api.PushOptions) error {
eg, ctx := errgroup.WithContext(ctx)
eg.SetLimit(s.maxConcurrency)
info, err := s.apiClient().Info(ctx)
if err != nil {
@@ -65,7 +69,7 @@ func (s *composeService) push(ctx context.Context, project *types.Project, optio
}
service := service
eg.Go(func() error {
err := s.pushServiceImage(ctx, service, info, s.configFile(), w)
err := s.pushServiceImage(ctx, service, info, s.configFile(), w, options.Quiet)
if err != nil {
if !options.IgnoreFailures {
return err
@@ -78,7 +82,7 @@ func (s *composeService) push(ctx context.Context, project *types.Project, optio
return eg.Wait()
}
func (s *composeService) pushServiceImage(ctx context.Context, service types.ServiceConfig, info moby.Info, configFile driver.Auth, w progress.Writer) error {
func (s *composeService) pushServiceImage(ctx context.Context, service types.ServiceConfig, info moby.Info, configFile driver.Auth, w progress.Writer, quietPush bool) error {
ref, err := reference.ParseNormalizedNamed(service.Image)
if err != nil {
return err
@@ -121,7 +125,10 @@ func (s *composeService) pushServiceImage(ctx context.Context, service types.Ser
if jm.Error != nil {
return errors.New(jm.Error.Message)
}
toPushProgressEvent(service.Name, jm, w)
if !quietPush {
toPushProgressEvent(service.Name, jm, w)
}
}
return nil
}

View File

@@ -21,10 +21,10 @@ import (
"strings"
"github.com/docker/compose/v2/pkg/api"
"golang.org/x/sync/errgroup"
"github.com/docker/compose/v2/pkg/progress"
"github.com/docker/compose/v2/pkg/utils"
containerType "github.com/docker/docker/api/types/container"
"golang.org/x/sync/errgroup"
)
func (s *composeService) Restart(ctx context.Context, projectName string, options api.RestartOptions) error {
@@ -62,7 +62,8 @@ func (s *composeService) restart(ctx context.Context, projectName string, option
eg.Go(func() error {
eventName := getContainerProgressName(container)
w.Event(progress.RestartingEvent(eventName))
err := s.apiClient().ContainerRestart(ctx, container.ID, options.Timeout)
timeout := utils.DurationSecondToInt(options.Timeout)
err := s.apiClient().ContainerRestart(ctx, container.ID, containerType.StopOptions{Timeout: timeout})
if err == nil {
w.Event(progress.StartedEvent(eventName))
}

View File

@@ -21,6 +21,7 @@ import (
"bytes"
"context"
"fmt"
"strconv"
"time"
"github.com/compose-spec/compose-go/types"
@@ -69,11 +70,29 @@ func createTar(env string, config types.ServiceSecretConfig) (bytes.Buffer, erro
target = "/run/secrets/" + config.Target
}
var uid, gid int
if config.UID != "" {
v, err := strconv.Atoi(config.UID)
if err != nil {
return b, err
}
uid = v
}
if config.GID != "" {
v, err := strconv.Atoi(config.GID)
if err != nil {
return b, err
}
gid = v
}
header := &tar.Header{
Name: target,
Size: int64(len(value)),
Mode: int64(mode),
ModTime: time.Now(),
Uid: uid,
Gid: gid,
}
err := tarWriter.WriteHeader(header)
if err != nil {

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