mirror of
https://github.com/docker/compose.git
synced 2026-02-10 18:49:28 +08:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
00bd108aec |
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@@ -19,7 +19,7 @@ on:
|
||||
default: "false"
|
||||
|
||||
env:
|
||||
DOCKER_CLI_VERSION: "25.0.0"
|
||||
DOCKER_CLI_VERSION: "20.10.17"
|
||||
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
@@ -138,6 +138,7 @@ jobs:
|
||||
mode:
|
||||
- plugin
|
||||
- standalone
|
||||
- cucumber
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
@@ -197,6 +198,11 @@ jobs:
|
||||
rm -f /usr/local/bin/docker-compose
|
||||
cp bin/build/docker-compose /usr/local/bin
|
||||
make e2e-compose-standalone
|
||||
-
|
||||
name: Run cucumber tests
|
||||
if: ${{ matrix.mode == 'cucumber'}}
|
||||
run: |
|
||||
make test-cucumber
|
||||
|
||||
coverage:
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
58
.github/workflows/codeql.yml
vendored
58
.github/workflows/codeql.yml
vendored
@@ -1,58 +0,0 @@
|
||||
name: codeql
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
paths-ignore:
|
||||
- '**/*.md'
|
||||
- '**/*.txt'
|
||||
- '**/*.yaml'
|
||||
- '**/*_test.go'
|
||||
pull_request:
|
||||
branches:
|
||||
- 'main'
|
||||
paths-ignore:
|
||||
- '**/*.md'
|
||||
- '**/*.txt'
|
||||
- '**/*.yaml'
|
||||
- '**/*_test.go'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: 'ubuntu-latest'
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language:
|
||||
- go
|
||||
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
-
|
||||
name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
-
|
||||
name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
-
|
||||
name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
42
.github/workflows/docs-upstream.yml
vendored
42
.github/workflows/docs-upstream.yml
vendored
@@ -1,42 +0,0 @@
|
||||
# this workflow runs the remote validate bake target from docker/docs
|
||||
# to check if yaml reference docs used in this repo are valid
|
||||
name: docs-upstream
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
- 'v[0-9]*'
|
||||
paths:
|
||||
- '.github/workflows/docs-upstream.yml'
|
||||
- 'docs/**'
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/docs-upstream.yml'
|
||||
- 'docs/**'
|
||||
|
||||
jobs:
|
||||
docs-yaml:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Upload reference YAML docs
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: docs-yaml
|
||||
path: docs/reference
|
||||
retention-days: 1
|
||||
|
||||
validate:
|
||||
uses: docker/docs/.github/workflows/validate-upstream.yml@main
|
||||
needs:
|
||||
- docs-yaml
|
||||
with:
|
||||
module-name: docker/compose
|
||||
56
.github/workflows/docs.yml
vendored
Normal file
56
.github/workflows/docs.yml
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
name: Docs
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
permissions: {}
|
||||
jobs:
|
||||
open-pr:
|
||||
permissions:
|
||||
contents: write # to create branch (peter-evans/create-pull-request)
|
||||
pull-requests: write # to create a PR (peter-evans/create-pull-request)
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout docs repo
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
token: ${{ secrets.GHPAT_DOCS_DISPATCH }}
|
||||
repository: docker/docs
|
||||
ref: main
|
||||
-
|
||||
name: Prepare
|
||||
run: |
|
||||
rm -rf ./_data/compose-cli/*
|
||||
-
|
||||
name: Build
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: ${{ github.server_url }}/${{ github.repository }}.git#${{ github.event.release.name }}
|
||||
target: docs-reference
|
||||
outputs: ./_data/compose-cli
|
||||
-
|
||||
name: Update compose_version in _config.yml
|
||||
run: |
|
||||
sed -i "s|^compose_version\:.*|compose_version\: \"${{ github.event.release.name }}\"|g" _config.yml
|
||||
cat _config.yml | yq .compose_version
|
||||
-
|
||||
name: Commit changes
|
||||
run: |
|
||||
git add -A .
|
||||
-
|
||||
name: Create PR on docs repo
|
||||
uses: peter-evans/create-pull-request@923ad837f191474af6b1721408744feb989a4c27 # v4.0.4
|
||||
with:
|
||||
token: ${{ secrets.GHPAT_DOCS_DISPATCH }}
|
||||
push-to-fork: docker-tools-robot/docker.github.io
|
||||
commit-message: Update Compose reference API to ${{ github.event.release.name }}
|
||||
signoff: true
|
||||
branch: dispatch/compose-api-reference-${{ github.event.release.name }}
|
||||
delete-branch: true
|
||||
title: Update Compose reference API to ${{ github.event.release.name }}
|
||||
body: |
|
||||
Update the Compose reference API documentation to keep in sync with the latest release `${{ github.event.release.name }}`
|
||||
draft: false
|
||||
@@ -7,7 +7,6 @@ linters:
|
||||
enable:
|
||||
- depguard
|
||||
- errcheck
|
||||
- errorlint
|
||||
- gocritic
|
||||
- gocyclo
|
||||
- gofmt
|
||||
@@ -37,18 +36,9 @@ linters-settings:
|
||||
deny:
|
||||
- pkg: io/ioutil
|
||||
desc: 'io/ioutil package has been deprecated'
|
||||
- pkg: gopkg.in/yaml.v2
|
||||
desc: 'compose-go uses yaml.v3'
|
||||
gomodguard:
|
||||
blocked:
|
||||
modules:
|
||||
- github.com/pkg/errors:
|
||||
recommendations:
|
||||
- errors
|
||||
- fmt
|
||||
versions:
|
||||
- github.com/distribution/distribution:
|
||||
reason: "use distribution/reference"
|
||||
- gotest.tools:
|
||||
version: "< 3.0.0"
|
||||
reason: "deprecated, pre-modules version"
|
||||
@@ -71,3 +61,5 @@ issues:
|
||||
# golangci hides some golint warnings (the warning about exported things
|
||||
# withtout documentation for example), this will make it show them anyway.
|
||||
exclude-use-default: false
|
||||
exclude:
|
||||
- should not use dot imports
|
||||
|
||||
@@ -66,11 +66,6 @@ When sending lengthy log files, consider posting them as a gist
|
||||
Don't forget to remove sensitive data from your log files before posting (you
|
||||
can replace those parts with "REDACTED").
|
||||
|
||||
_Note:_
|
||||
Maintainers might request additional information to diagnose an issue,
|
||||
if initial reporter doesn't answer within a reasonable delay (a few weeks),
|
||||
issue will be closed.
|
||||
|
||||
## Quick contribution tips and guidelines
|
||||
|
||||
This section gives the experienced contributor some tips and guidelines.
|
||||
|
||||
13
Dockerfile
13
Dockerfile
@@ -15,9 +15,9 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
ARG GO_VERSION=1.21.6
|
||||
ARG GO_VERSION=1.21.0
|
||||
ARG XX_VERSION=1.2.1
|
||||
ARG GOLANGCI_LINT_VERSION=v1.55.2
|
||||
ARG GOLANGCI_LINT_VERSION=v1.53.2
|
||||
ARG ADDLICENSE_VERSION=v1.0.0
|
||||
|
||||
ARG BUILD_TAGS="e2e"
|
||||
@@ -89,13 +89,10 @@ RUN --mount=type=bind,target=. \
|
||||
|
||||
FROM build-base AS lint
|
||||
ARG BUILD_TAGS
|
||||
ENV GOLANGCI_LINT_CACHE=/cache/golangci-lint
|
||||
RUN --mount=type=bind,target=. \
|
||||
--mount=type=cache,target=/root/.cache \
|
||||
--mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=type=cache,target=/cache/golangci-lint \
|
||||
--mount=from=golangci-lint,source=/usr/bin/golangci-lint,target=/usr/bin/golangci-lint \
|
||||
golangci-lint cache status && \
|
||||
golangci-lint run --build-tags "$BUILD_TAGS" ./...
|
||||
|
||||
FROM build-base AS test
|
||||
@@ -192,3 +189,9 @@ RUN --mount=from=binary \
|
||||
|
||||
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 docs.docker.com.
|
||||
# see open-pr job in .github/workflows/docs.yml for more details
|
||||
FROM scratch AS docs-reference
|
||||
COPY docs/reference/*.yaml .
|
||||
|
||||
18
Makefile
18
Makefile
@@ -32,13 +32,9 @@ endif
|
||||
BUILD_FLAGS?=
|
||||
TEST_FLAGS?=
|
||||
E2E_TEST?=
|
||||
ifneq ($(E2E_TEST),)
|
||||
TEST_FLAGS:=$(TEST_FLAGS) -run '$(E2E_TEST)'
|
||||
endif
|
||||
|
||||
EXCLUDE_E2E_TESTS?=
|
||||
ifneq ($(EXCLUDE_E2E_TESTS),)
|
||||
TEST_FLAGS:=$(TEST_FLAGS) -skip '$(EXCLUDE_E2E_TESTS)'
|
||||
ifeq ($(E2E_TEST),)
|
||||
else
|
||||
TEST_FLAGS=-run $(E2E_TEST)
|
||||
endif
|
||||
|
||||
BUILDX_CMD ?= docker buildx
|
||||
@@ -75,12 +71,16 @@ 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
|
||||
go test -v $(TEST_FLAGS) -count=1 ./pkg/e2e
|
||||
go test $(TEST_FLAGS) -count=1 ./pkg/e2e
|
||||
|
||||
.PHONY: e2e-compose-standalone
|
||||
e2e-compose-standalone: ## Run End to end local tests in standalone mode. Set E2E_TEST=TestName to run a single test
|
||||
go test $(TEST_FLAGS) -v -count=1 -parallel=1 --tags=standalone ./pkg/e2e
|
||||
|
||||
.PHONY: test-cucumber
|
||||
test-cucumber:
|
||||
go test $(TEST_FLAGS) -v -count=1 -parallel=1 ./e2e
|
||||
|
||||
.PHONY: build-and-e2e-compose
|
||||
build-and-e2e-compose: build e2e-compose ## Compile the compose cli-plugin and run end to end local tests in plugin mode. Set E2E_TEST=TestName to run a single test
|
||||
|
||||
@@ -89,7 +89,7 @@ build-and-e2e-compose-standalone: build e2e-compose-standalone ## Compile the co
|
||||
|
||||
.PHONY: mocks
|
||||
mocks:
|
||||
mockgen --version >/dev/null 2>&1 || go install go.uber.org/mock/mockgen@v0.3.0
|
||||
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
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# 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)
|
||||
@@ -52,7 +53,7 @@ Quick Start
|
||||
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 `compose.yaml` so
|
||||
2. Define the services that make up your app in `docker-compose.yml` so
|
||||
they can be run together in an isolated environment.
|
||||
3. Lastly, run `docker compose up` and Compose will start and run your entire
|
||||
app.
|
||||
@@ -83,4 +84,4 @@ If you find an issue, please report it on the
|
||||
Legacy
|
||||
-------------
|
||||
|
||||
The Python version of Compose is available under the `v1` [branch](https://github.com/docker/compose/tree/v1).
|
||||
The Python version of Compose is available under the `v1` [branch](https://github.com/docker/compose/tree/v1)
|
||||
@@ -29,7 +29,6 @@ import (
|
||||
commands "github.com/docker/compose/v2/cmd/compose"
|
||||
"github.com/docker/compose/v2/internal/tracing"
|
||||
"github.com/spf13/cobra"
|
||||
flag "github.com/spf13/pflag"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
@@ -43,7 +42,7 @@ import (
|
||||
// vars, creates a root span for the command, and wraps the actual
|
||||
// command invocation to ensure the span is properly finalized and
|
||||
// exported before exit.
|
||||
func Setup(cmd *cobra.Command, dockerCli command.Cli, args []string) error {
|
||||
func Setup(cmd *cobra.Command, dockerCli command.Cli) error {
|
||||
tracingShutdown, err := tracing.InitTracing(dockerCli)
|
||||
if err != nil {
|
||||
return fmt.Errorf("initializing tracing: %w", err)
|
||||
@@ -54,9 +53,6 @@ func Setup(cmd *cobra.Command, dockerCli command.Cli, args []string) error {
|
||||
ctx,
|
||||
"cli/"+strings.Join(commandName(cmd), "-"),
|
||||
)
|
||||
cmdSpan.SetAttributes(attribute.StringSlice("cli.args", args))
|
||||
cmdSpan.SetAttributes(attribute.StringSlice("cli.flags", getFlags(cmd.Flags())))
|
||||
|
||||
cmd.SetContext(ctx)
|
||||
wrapRunE(cmd, cmdSpan, tracingShutdown)
|
||||
return nil
|
||||
@@ -133,11 +129,3 @@ func commandName(cmd *cobra.Command) []string {
|
||||
sort.Sort(sort.Reverse(sort.StringSlice(name)))
|
||||
return name
|
||||
}
|
||||
|
||||
func getFlags(fs *flag.FlagSet) []string {
|
||||
var result []string
|
||||
fs.Visit(func(flag *flag.Flag) {
|
||||
result = append(result, flag.Name)
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
Copyright 2020 Docker Compose CLI authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cmdtrace
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
flag "github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func TestGetFlags(t *testing.T) {
|
||||
// Initialize flagSet with flags
|
||||
fs := flag.NewFlagSet("up", flag.ContinueOnError)
|
||||
var (
|
||||
detach string
|
||||
timeout string
|
||||
)
|
||||
fs.StringVar(&detach, "detach", "d", "")
|
||||
fs.StringVar(&timeout, "timeout", "t", "")
|
||||
_ = fs.Set("detach", "detach")
|
||||
_ = fs.Set("timeout", "timeout")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input *flag.FlagSet
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
name: "NoFlags",
|
||||
input: flag.NewFlagSet("NoFlags", flag.ContinueOnError),
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "Flags",
|
||||
input: fs,
|
||||
expected: []string{"detach", "timeout"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
result := getFlags(test.input)
|
||||
if !reflect.DeepEqual(result, test.expected) {
|
||||
t.Errorf("Expected %v, but got %v", test.expected, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,13 +15,12 @@
|
||||
package compose
|
||||
|
||||
import (
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// alphaCommand groups all experimental subcommands
|
||||
func alphaCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func alphaCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Short: "Experimental commands",
|
||||
Use: "alpha [COMMAND]",
|
||||
@@ -31,8 +30,9 @@ func alphaCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
},
|
||||
}
|
||||
cmd.AddCommand(
|
||||
vizCommand(p, dockerCli, backend),
|
||||
publishCommand(p, dockerCli, backend),
|
||||
watchCommand(p, backend),
|
||||
vizCommand(p, backend),
|
||||
publishCommand(p, backend),
|
||||
)
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
Copyright 2020 Docker Compose CLI authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package compose
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type attachOpts struct {
|
||||
*composeOptions
|
||||
|
||||
service string
|
||||
index int
|
||||
|
||||
detachKeys string
|
||||
noStdin bool
|
||||
proxy bool
|
||||
}
|
||||
|
||||
func attachCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
opts := attachOpts{
|
||||
composeOptions: &composeOptions{
|
||||
ProjectOptions: p,
|
||||
},
|
||||
}
|
||||
runCmd := &cobra.Command{
|
||||
Use: "attach [OPTIONS] SERVICE",
|
||||
Short: "Attach local standard input, output, and error streams to a service's running container.",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
PreRunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
opts.service = args[0]
|
||||
return nil
|
||||
}),
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
return runAttach(ctx, dockerCli, backend, opts)
|
||||
}),
|
||||
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||
}
|
||||
|
||||
runCmd.Flags().IntVar(&opts.index, "index", 0, "index of the container if service has multiple replicas.")
|
||||
runCmd.Flags().StringVarP(&opts.detachKeys, "detach-keys", "", "", "Override the key sequence for detaching from a container.")
|
||||
|
||||
runCmd.Flags().BoolVar(&opts.noStdin, "no-stdin", false, "Do not attach STDIN")
|
||||
runCmd.Flags().BoolVar(&opts.proxy, "sig-proxy", true, "Proxy all received signals to the process")
|
||||
return runCmd
|
||||
}
|
||||
|
||||
func runAttach(ctx context.Context, dockerCli command.Cli, backend api.Service, opts attachOpts) error {
|
||||
projectName, err := opts.toProjectName(dockerCli)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
attachOpts := api.AttachOptions{
|
||||
Service: opts.service,
|
||||
Index: opts.index,
|
||||
DetachKeys: opts.detachKeys,
|
||||
NoStdin: opts.noStdin,
|
||||
Proxy: opts.proxy,
|
||||
}
|
||||
return backend.Attach(ctx, projectName, attachOpts)
|
||||
}
|
||||
@@ -22,12 +22,12 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/cli"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/compose-spec/compose-go/cli"
|
||||
"github.com/compose-spec/compose-go/loader"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
buildx "github.com/docker/buildx/util/progress"
|
||||
cliopts "github.com/docker/cli/opts"
|
||||
ui "github.com/docker/compose/v2/pkg/progress"
|
||||
buildkit "github.com/moby/buildkit/util/progress/progressui"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
@@ -35,6 +35,7 @@ import (
|
||||
|
||||
type buildOptions struct {
|
||||
*ProjectOptions
|
||||
composeOptions
|
||||
quiet bool
|
||||
pull bool
|
||||
push bool
|
||||
@@ -43,21 +44,13 @@ type buildOptions struct {
|
||||
memory cliopts.MemBytes
|
||||
ssh string
|
||||
builder string
|
||||
deps bool
|
||||
}
|
||||
|
||||
func (opts buildOptions) toAPIBuildOptions(services []string) (api.BuildOptions, error) {
|
||||
var SSHKeys []types.SSHKey
|
||||
var err error
|
||||
if opts.ssh != "" {
|
||||
id, path, found := strings.Cut(opts.ssh, "=")
|
||||
if !found && id != "default" {
|
||||
return api.BuildOptions{}, fmt.Errorf("invalid ssh key %q", opts.ssh)
|
||||
}
|
||||
SSHKeys = append(SSHKeys, types.SSHKey{
|
||||
ID: id,
|
||||
Path: path,
|
||||
})
|
||||
SSHKeys, err = loader.ParseShortSSHSyntax(opts.ssh)
|
||||
if err != nil {
|
||||
return api.BuildOptions{}, err
|
||||
}
|
||||
@@ -75,13 +68,12 @@ func (opts buildOptions) toAPIBuildOptions(services []string) (api.BuildOptions,
|
||||
NoCache: opts.noCache,
|
||||
Quiet: opts.quiet,
|
||||
Services: services,
|
||||
Deps: opts.deps,
|
||||
SSHs: SSHKeys,
|
||||
Builder: builderName,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func buildCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func buildCommand(p *ProjectOptions, progress *string, backend api.Service) *cobra.Command {
|
||||
opts := buildOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -104,47 +96,40 @@ func buildCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
opts.ssh = "default"
|
||||
}
|
||||
if cmd.Flags().Changed("progress") && opts.ssh == "" {
|
||||
fmt.Fprint(os.Stderr, "--progress is a global compose flag, better use `docker compose --progress xx build ...\n")
|
||||
fmt.Fprint(os.Stderr, "--progress is a global compose flag, better use `docker compose --progress xx build ...")
|
||||
}
|
||||
return runBuild(ctx, dockerCli, backend, opts, args)
|
||||
return runBuild(ctx, backend, opts, args)
|
||||
}),
|
||||
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||
ValidArgsFunction: completeServiceNames(p),
|
||||
}
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVar(&opts.push, "push", false, "Push service images.")
|
||||
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Don't print anything to STDOUT")
|
||||
flags.BoolVar(&opts.pull, "pull", false, "Always attempt to pull a newer version of the image.")
|
||||
flags.StringArrayVar(&opts.args, "build-arg", []string{}, "Set build-time variables for services.")
|
||||
flags.StringVar(&opts.ssh, "ssh", "", "Set SSH authentications used when building service images. (use 'default' for using your default SSH Agent)")
|
||||
flags.StringVar(&opts.builder, "builder", "", "Set builder to use.")
|
||||
flags.BoolVar(&opts.deps, "with-dependencies", false, "Also build dependencies (transitively).")
|
||||
|
||||
flags.Bool("parallel", true, "Build images in parallel. DEPRECATED")
|
||||
flags.MarkHidden("parallel") //nolint:errcheck
|
||||
flags.Bool("compress", true, "Compress the build context using gzip. DEPRECATED")
|
||||
flags.MarkHidden("compress") //nolint:errcheck
|
||||
flags.Bool("force-rm", true, "Always remove intermediate containers. DEPRECATED")
|
||||
flags.MarkHidden("force-rm") //nolint:errcheck
|
||||
flags.BoolVar(&opts.noCache, "no-cache", false, "Do not use cache when building the image")
|
||||
flags.Bool("no-rm", false, "Do not remove intermediate containers after a successful build. DEPRECATED")
|
||||
flags.MarkHidden("no-rm") //nolint:errcheck
|
||||
flags.VarP(&opts.memory, "memory", "m", "Set memory limit for the build container. Not supported by BuildKit.")
|
||||
flags.StringVar(&p.Progress, "progress", string(buildkit.AutoMode), fmt.Sprintf(`Set type of ui output (%s)`, strings.Join(printerModes, ", ")))
|
||||
flags.MarkHidden("progress") //nolint:errcheck
|
||||
cmd.Flags().BoolVar(&opts.push, "push", false, "Push service images.")
|
||||
cmd.Flags().BoolVarP(&opts.quiet, "quiet", "q", false, "Don't print anything to STDOUT")
|
||||
cmd.Flags().BoolVar(&opts.pull, "pull", false, "Always attempt to pull a newer version of the image.")
|
||||
cmd.Flags().StringArrayVar(&opts.args, "build-arg", []string{}, "Set build-time variables for services.")
|
||||
cmd.Flags().StringVar(&opts.ssh, "ssh", "", "Set SSH authentications used when building service images. (use 'default' for using your default SSH Agent)")
|
||||
cmd.Flags().StringVar(&opts.builder, "builder", "", "Set builder to use.")
|
||||
cmd.Flags().Bool("parallel", true, "Build images in parallel. DEPRECATED")
|
||||
cmd.Flags().MarkHidden("parallel") //nolint:errcheck
|
||||
cmd.Flags().Bool("compress", true, "Compress the build context using gzip. DEPRECATED")
|
||||
cmd.Flags().MarkHidden("compress") //nolint:errcheck
|
||||
cmd.Flags().Bool("force-rm", true, "Always remove intermediate containers. DEPRECATED")
|
||||
cmd.Flags().MarkHidden("force-rm") //nolint:errcheck
|
||||
cmd.Flags().BoolVar(&opts.noCache, "no-cache", false, "Do not use cache when building the image")
|
||||
cmd.Flags().Bool("no-rm", false, "Do not remove intermediate containers after a successful build. DEPRECATED")
|
||||
cmd.Flags().MarkHidden("no-rm") //nolint:errcheck
|
||||
cmd.Flags().VarP(&opts.memory, "memory", "m", "Set memory limit for the build container. Not supported by BuildKit.")
|
||||
cmd.Flags().StringVar(progress, "progress", buildx.PrinterModeAuto, fmt.Sprintf(`Set type of ui output (%s)`, strings.Join(printerModes, ", ")))
|
||||
cmd.Flags().MarkHidden("progress") //nolint:errcheck
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runBuild(ctx context.Context, dockerCli command.Cli, backend api.Service, opts buildOptions, services []string) error {
|
||||
project, err := opts.ToProject(dockerCli, services, cli.WithResolvedPaths(true), cli.WithoutEnvironmentResolution)
|
||||
func runBuild(ctx context.Context, backend api.Service, opts buildOptions, services []string) error {
|
||||
project, err := opts.ToProject(services, cli.WithResolvedPaths(true))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := applyPlatforms(project, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
apiBuildOptions, err := opts.toAPIBuildOptions(services)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -34,15 +33,14 @@ func noCompletion() validArgsFn {
|
||||
}
|
||||
}
|
||||
|
||||
func completeServiceNames(dockerCli command.Cli, p *ProjectOptions) validArgsFn {
|
||||
func completeServiceNames(p *ProjectOptions) validArgsFn {
|
||||
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
p.Offline = true
|
||||
project, err := p.ToProject(dockerCli, nil)
|
||||
project, err := p.ToProject(nil)
|
||||
if err != nil {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
serviceNames := append(project.ServiceNames(), project.DisabledServiceNames()...)
|
||||
for _, s := range serviceNames {
|
||||
var serviceNames []string
|
||||
for _, s := range project.ServiceNames() {
|
||||
if toComplete == "" || strings.HasPrefix(s, toComplete) {
|
||||
serviceNames = append(serviceNames, s)
|
||||
}
|
||||
@@ -69,10 +67,9 @@ func completeProjectNames(backend api.Service) func(cmd *cobra.Command, args []s
|
||||
}
|
||||
}
|
||||
|
||||
func completeProfileNames(dockerCli command.Cli, p *ProjectOptions) validArgsFn {
|
||||
func completeProfileNames(p *ProjectOptions) validArgsFn {
|
||||
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
p.Offline = true
|
||||
project, err := p.ToProject(dockerCli, nil)
|
||||
project, err := p.ToProject(nil)
|
||||
if err != nil {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ package compose
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
@@ -27,17 +26,18 @@ import (
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/cli"
|
||||
"github.com/compose-spec/compose-go/v2/dotenv"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
composegoutils "github.com/compose-spec/compose-go/v2/utils"
|
||||
"github.com/compose-spec/compose-go/dotenv"
|
||||
buildx "github.com/docker/buildx/util/progress"
|
||||
"github.com/docker/cli/cli/command"
|
||||
|
||||
"github.com/compose-spec/compose-go/cli"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
composegoutils "github.com/compose-spec/compose-go/utils"
|
||||
"github.com/docker/buildx/util/logutil"
|
||||
dockercli "github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli-plugins/manager"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/compose/v2/pkg/remote"
|
||||
buildkit "github.com/moby/buildkit/util/progress/progressui"
|
||||
"github.com/morikuni/aec"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
@@ -60,8 +60,6 @@ const (
|
||||
ComposeRemoveOrphans = "COMPOSE_REMOVE_ORPHANS"
|
||||
// ComposeIgnoreOrphans ignore "orphaned" containers
|
||||
ComposeIgnoreOrphans = "COMPOSE_IGNORE_ORPHANS"
|
||||
// ComposeEnvFiles defines the env files to use if --env-file isn't used
|
||||
ComposeEnvFiles = "COMPOSE_ENV_FILES"
|
||||
)
|
||||
|
||||
// Command defines a compose CLI command as a func with args
|
||||
@@ -73,17 +71,18 @@ type CobraCommand func(context.Context, *cobra.Command, []string) error
|
||||
// AdaptCmd adapt a CobraCommand func to cobra library
|
||||
func AdaptCmd(fn CobraCommand) func(cmd *cobra.Command, args []string) error {
|
||||
return func(cmd *cobra.Command, args []string) error {
|
||||
ctx, cancel := context.WithCancel(cmd.Context())
|
||||
|
||||
s := make(chan os.Signal, 1)
|
||||
signal.Notify(s, syscall.SIGTERM, syscall.SIGINT)
|
||||
go func() {
|
||||
<-s
|
||||
cancel()
|
||||
signal.Stop(s)
|
||||
close(s)
|
||||
}()
|
||||
|
||||
ctx := cmd.Context()
|
||||
contextString := fmt.Sprintf("%s", ctx)
|
||||
if !strings.HasSuffix(contextString, ".WithCancel") { // need to handle cancel
|
||||
cancellableCtx, cancel := context.WithCancel(cmd.Context())
|
||||
ctx = cancellableCtx
|
||||
s := make(chan os.Signal, 1)
|
||||
signal.Notify(s, syscall.SIGTERM, syscall.SIGINT)
|
||||
go func() {
|
||||
<-s
|
||||
cancel()
|
||||
}()
|
||||
}
|
||||
err := fn(ctx, cmd, args)
|
||||
var composeErr compose.Error
|
||||
if api.IsErrCanceled(err) || errors.Is(ctx.Err(), context.Canceled) {
|
||||
@@ -117,8 +116,6 @@ type ProjectOptions struct {
|
||||
ProjectDir string
|
||||
EnvFiles []string
|
||||
Compatibility bool
|
||||
Progress string
|
||||
Offline bool
|
||||
}
|
||||
|
||||
// ProjectFunc does stuff within a types.Project
|
||||
@@ -128,22 +125,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, dockerCli command.Cli) func(cmd *cobra.Command, args []string) error {
|
||||
return o.WithServices(dockerCli, func(ctx context.Context, project *types.Project, services []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(dockerCli command.Cli, 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 {
|
||||
options := []cli.ProjectOptionsFn{
|
||||
cli.WithResolvedPaths(true),
|
||||
cli.WithDiscardEnvFile,
|
||||
cli.WithContext(ctx),
|
||||
}
|
||||
|
||||
project, err := o.ToProject(dockerCli, args, options...)
|
||||
project, err := o.ToProject(args, cli.WithResolvedPaths(true), cli.WithDiscardEnvFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -160,15 +151,14 @@ func (o *ProjectOptions) addProjectFlags(f *pflag.FlagSet) {
|
||||
f.StringVar(&o.ProjectDir, "project-directory", "", "Specify an alternate working directory\n(default: the path of the, first specified, Compose file)")
|
||||
f.StringVar(&o.WorkDir, "workdir", "", "DEPRECATED! USE --project-directory INSTEAD.\nSpecify an alternate working directory\n(default: the path of the, first specified, Compose file)")
|
||||
f.BoolVar(&o.Compatibility, "compatibility", false, "Run compose in backward compatibility mode")
|
||||
f.StringVar(&o.Progress, "progress", string(buildkit.AutoMode), fmt.Sprintf(`Set type of progress output (%s)`, strings.Join(printerModes, ", ")))
|
||||
_ = f.MarkHidden("workdir")
|
||||
}
|
||||
|
||||
func (o *ProjectOptions) projectOrName(dockerCli command.Cli, services ...string) (*types.Project, string, error) {
|
||||
func (o *ProjectOptions) projectOrName(services ...string) (*types.Project, string, error) {
|
||||
name := o.ProjectName
|
||||
var project *types.Project
|
||||
if len(o.ConfigPaths) > 0 || o.ProjectName == "" {
|
||||
p, err := o.ToProject(dockerCli, services, cli.WithDiscardEnvFile)
|
||||
p, err := o.ToProject(services, cli.WithDiscardEnvFile)
|
||||
if err != nil {
|
||||
envProjectName := os.Getenv(ComposeProjectName)
|
||||
if envProjectName != "" {
|
||||
@@ -182,7 +172,7 @@ func (o *ProjectOptions) projectOrName(dockerCli command.Cli, services ...string
|
||||
return project, name, nil
|
||||
}
|
||||
|
||||
func (o *ProjectOptions) toProjectName(dockerCli command.Cli) (string, error) {
|
||||
func (o *ProjectOptions) toProjectName() (string, error) {
|
||||
if o.ProjectName != "" {
|
||||
return o.ProjectName, nil
|
||||
}
|
||||
@@ -192,18 +182,14 @@ func (o *ProjectOptions) toProjectName(dockerCli command.Cli) (string, error) {
|
||||
return envProjectName, nil
|
||||
}
|
||||
|
||||
project, err := o.ToProject(dockerCli, nil)
|
||||
project, err := o.ToProject(nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return project.Name, nil
|
||||
}
|
||||
|
||||
func (o *ProjectOptions) ToProject(dockerCli command.Cli, services []string, po ...cli.ProjectOptionsFn) (*types.Project, error) {
|
||||
if !o.Offline {
|
||||
po = o.configureRemoteLoaders(dockerCli, po)
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -222,15 +208,15 @@ func (o *ProjectOptions) ToProject(dockerCli command.Cli, services []string, po
|
||||
return nil, errors.New("project name can't be empty. Use `--project-name` to set a valid name")
|
||||
}
|
||||
|
||||
project, err = project.WithServicesEnabled(services...)
|
||||
err = project.EnableServices(services...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for name, s := range project.Services {
|
||||
for i, s := range project.Services {
|
||||
s.CustomLabels = map[string]string{
|
||||
api.ProjectLabel: project.Name,
|
||||
api.ServiceLabel: name,
|
||||
api.ServiceLabel: s.Name,
|
||||
api.VersionLabel: api.ComposeVersion,
|
||||
api.WorkingDirLabel: project.WorkingDir,
|
||||
api.ConfigFilesLabel: strings.Join(project.ComposeFiles, ","),
|
||||
@@ -239,23 +225,15 @@ func (o *ProjectOptions) ToProject(dockerCli command.Cli, services []string, po
|
||||
if len(o.EnvFiles) != 0 {
|
||||
s.CustomLabels[api.EnvironmentFileLabel] = strings.Join(o.EnvFiles, ",")
|
||||
}
|
||||
project.Services[name] = s
|
||||
project.Services[i] = s
|
||||
}
|
||||
|
||||
project = project.WithoutUnnecessaryResources()
|
||||
project.WithoutUnnecessaryResources()
|
||||
|
||||
project, err = project.WithSelectedServices(services)
|
||||
err = project.ForServices(services)
|
||||
return project, err
|
||||
}
|
||||
|
||||
func (o *ProjectOptions) configureRemoteLoaders(dockerCli command.Cli, po []cli.ProjectOptionsFn) []cli.ProjectOptionsFn {
|
||||
git := remote.NewGitRemoteLoader(o.Offline)
|
||||
oci := remote.NewOCIRemoteLoader(dockerCli, o.Offline)
|
||||
|
||||
po = append(po, cli.WithResourceLoader(git), cli.WithResourceLoader(oci))
|
||||
return po
|
||||
}
|
||||
|
||||
func (o *ProjectOptions) toProjectOptions(po ...cli.ProjectOptionsFn) (*cli.ProjectOptions, error) {
|
||||
return cli.NewProjectOptions(o.ConfigPaths,
|
||||
append(po,
|
||||
@@ -265,7 +243,7 @@ func (o *ProjectOptions) toProjectOptions(po ...cli.ProjectOptionsFn) (*cli.Proj
|
||||
cli.WithDotEnv,
|
||||
cli.WithConfigFileEnv,
|
||||
cli.WithDefaultConfigPath,
|
||||
cli.WithDefaultProfiles(o.Profiles...),
|
||||
cli.WithProfiles(o.Profiles),
|
||||
cli.WithName(o.ProjectName))...)
|
||||
}
|
||||
|
||||
@@ -278,7 +256,7 @@ func RunningAsStandalone() bool {
|
||||
}
|
||||
|
||||
// RootCommand returns the compose command with its child commands
|
||||
func RootCommand(dockerCli command.Cli, backend api.Service) *cobra.Command { //nolint:gocyclo
|
||||
func RootCommand(streams 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
|
||||
@@ -297,6 +275,7 @@ func RootCommand(dockerCli command.Cli, backend api.Service) *cobra.Command { //
|
||||
version bool
|
||||
parallel int
|
||||
dryRun bool
|
||||
progress string
|
||||
)
|
||||
c := &cobra.Command{
|
||||
Short: "Docker Compose",
|
||||
@@ -309,7 +288,7 @@ func RootCommand(dockerCli command.Cli, backend api.Service) *cobra.Command { //
|
||||
return cmd.Help()
|
||||
}
|
||||
if version {
|
||||
return versionCommand(dockerCli).Execute()
|
||||
return versionCommand(streams).Execute()
|
||||
}
|
||||
_ = cmd.Help()
|
||||
return dockercli.StatusError{
|
||||
@@ -347,11 +326,11 @@ func RootCommand(dockerCli command.Cli, backend api.Service) *cobra.Command { //
|
||||
ansi = v
|
||||
}
|
||||
|
||||
formatter.SetANSIMode(dockerCli, ansi)
|
||||
formatter.SetANSIMode(streams, ansi)
|
||||
|
||||
if noColor, ok := os.LookupEnv("NO_COLOR"); ok && noColor != "" {
|
||||
ui.NoColor()
|
||||
formatter.SetANSIMode(dockerCli, formatter.Never)
|
||||
formatter.SetANSIMode(streams, formatter.Never)
|
||||
}
|
||||
|
||||
switch ansi {
|
||||
@@ -361,7 +340,7 @@ func RootCommand(dockerCli command.Cli, backend api.Service) *cobra.Command { //
|
||||
ui.Mode = ui.ModeTTY
|
||||
}
|
||||
|
||||
switch opts.Progress {
|
||||
switch progress {
|
||||
case ui.ModeAuto:
|
||||
ui.Mode = ui.ModeAuto
|
||||
case ui.ModeTTY:
|
||||
@@ -377,7 +356,7 @@ func RootCommand(dockerCli command.Cli, backend api.Service) *cobra.Command { //
|
||||
case ui.ModeQuiet, "none":
|
||||
ui.Mode = ui.ModeQuiet
|
||||
default:
|
||||
return fmt.Errorf("unsupported --progress value %q", opts.Progress)
|
||||
return fmt.Errorf("unsupported --progress value %q", progress)
|
||||
}
|
||||
|
||||
if opts.WorkDir != "" {
|
||||
@@ -428,37 +407,33 @@ func RootCommand(dockerCli command.Cli, backend api.Service) *cobra.Command { //
|
||||
}
|
||||
|
||||
c.AddCommand(
|
||||
upCommand(&opts, dockerCli, backend),
|
||||
downCommand(&opts, dockerCli, backend),
|
||||
startCommand(&opts, dockerCli, backend),
|
||||
restartCommand(&opts, dockerCli, backend),
|
||||
stopCommand(&opts, dockerCli, backend),
|
||||
psCommand(&opts, dockerCli, backend),
|
||||
listCommand(dockerCli, backend),
|
||||
logsCommand(&opts, dockerCli, backend),
|
||||
configCommand(&opts, dockerCli, backend),
|
||||
killCommand(&opts, dockerCli, backend),
|
||||
runCommand(&opts, dockerCli, backend),
|
||||
removeCommand(&opts, dockerCli, backend),
|
||||
execCommand(&opts, dockerCli, backend),
|
||||
attachCommand(&opts, dockerCli, backend),
|
||||
pauseCommand(&opts, dockerCli, backend),
|
||||
unpauseCommand(&opts, dockerCli, backend),
|
||||
topCommand(&opts, dockerCli, backend),
|
||||
eventsCommand(&opts, dockerCli, backend),
|
||||
portCommand(&opts, dockerCli, backend),
|
||||
imagesCommand(&opts, dockerCli, backend),
|
||||
versionCommand(dockerCli),
|
||||
buildCommand(&opts, dockerCli, backend),
|
||||
pushCommand(&opts, dockerCli, backend),
|
||||
pullCommand(&opts, dockerCli, backend),
|
||||
createCommand(&opts, dockerCli, backend),
|
||||
copyCommand(&opts, dockerCli, backend),
|
||||
waitCommand(&opts, dockerCli, backend),
|
||||
scaleCommand(&opts, dockerCli, backend),
|
||||
statsCommand(&opts, dockerCli),
|
||||
watchCommand(&opts, dockerCli, backend),
|
||||
alphaCommand(&opts, dockerCli, backend),
|
||||
upCommand(&opts, streams, backend),
|
||||
downCommand(&opts, backend),
|
||||
startCommand(&opts, backend),
|
||||
restartCommand(&opts, backend),
|
||||
stopCommand(&opts, backend),
|
||||
psCommand(&opts, streams, backend),
|
||||
listCommand(streams, backend),
|
||||
logsCommand(&opts, streams, backend),
|
||||
configCommand(&opts, streams, backend),
|
||||
killCommand(&opts, backend),
|
||||
runCommand(&opts, streams, backend),
|
||||
removeCommand(&opts, backend),
|
||||
execCommand(&opts, streams, backend),
|
||||
pauseCommand(&opts, backend),
|
||||
unpauseCommand(&opts, backend),
|
||||
topCommand(&opts, streams, backend),
|
||||
eventsCommand(&opts, streams, backend),
|
||||
portCommand(&opts, streams, backend),
|
||||
imagesCommand(&opts, streams, backend),
|
||||
versionCommand(streams),
|
||||
buildCommand(&opts, &progress, backend),
|
||||
pushCommand(&opts, backend),
|
||||
pullCommand(&opts, backend),
|
||||
createCommand(&opts, backend),
|
||||
copyCommand(&opts, backend),
|
||||
waitCommand(&opts, backend),
|
||||
alphaCommand(&opts, backend),
|
||||
)
|
||||
|
||||
c.Flags().SetInterspersed(false)
|
||||
@@ -481,9 +456,11 @@ func RootCommand(dockerCli command.Cli, backend api.Service) *cobra.Command { //
|
||||
)
|
||||
c.RegisterFlagCompletionFunc( //nolint:errcheck
|
||||
"profile",
|
||||
completeProfileNames(dockerCli, &opts),
|
||||
completeProfileNames(&opts),
|
||||
)
|
||||
|
||||
c.Flags().StringVar(&progress, "progress", buildx.PrinterModeAuto, fmt.Sprintf(`Set type of progress output (%s)`, strings.Join(printerModes, ", ")))
|
||||
|
||||
c.Flags().StringVar(&ansi, "ansi", "auto", `Control when to print ANSI control characters ("never"|"always"|"auto")`)
|
||||
c.Flags().IntVar(¶llel, "parallel", -1, `Control max parallelism, -1 for unlimited`)
|
||||
c.Flags().BoolVarP(&version, "version", "v", false, "Show the Docker Compose version information")
|
||||
@@ -497,17 +474,16 @@ func RootCommand(dockerCli command.Cli, backend api.Service) *cobra.Command { //
|
||||
}
|
||||
|
||||
func setEnvWithDotEnv(prjOpts *ProjectOptions) error {
|
||||
if len(prjOpts.EnvFiles) == 0 {
|
||||
if envFiles := os.Getenv(ComposeEnvFiles); envFiles != "" {
|
||||
prjOpts.EnvFiles = strings.Split(envFiles, ",")
|
||||
}
|
||||
}
|
||||
options, err := prjOpts.toProjectOptions()
|
||||
if err != nil {
|
||||
return compose.WrapComposeError(err)
|
||||
}
|
||||
workingDir, err := options.GetWorkingDir()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
envFromFile, err := dotenv.GetEnvFromFile(composegoutils.GetAsEqualsMap(os.Environ()), options.EnvFiles)
|
||||
envFromFile, err := dotenv.GetEnvFromFile(composegoutils.GetAsEqualsMap(os.Environ()), workingDir, options.EnvFiles)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -19,32 +19,32 @@ package compose
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestFilterServices(t *testing.T) {
|
||||
p := &types.Project{
|
||||
Services: types.Services{
|
||||
"foo": {
|
||||
Services: []types.ServiceConfig{
|
||||
{
|
||||
Name: "foo",
|
||||
Links: []string{"bar"},
|
||||
},
|
||||
"bar": {
|
||||
{
|
||||
Name: "bar",
|
||||
DependsOn: map[string]types.ServiceDependency{
|
||||
"zot": {},
|
||||
},
|
||||
},
|
||||
"zot": {
|
||||
{
|
||||
Name: "zot",
|
||||
},
|
||||
"qix": {
|
||||
{
|
||||
Name: "qix",
|
||||
},
|
||||
},
|
||||
}
|
||||
p, err := p.WithSelectedServices([]string{"bar"})
|
||||
err := p.ForServices([]string{"bar"})
|
||||
assert.NilError(t, err)
|
||||
|
||||
assert.Equal(t, len(p.Services), 2)
|
||||
|
||||
@@ -24,9 +24,8 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/cli"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/compose-spec/compose-go/cli"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
@@ -50,19 +49,17 @@ type configOptions struct {
|
||||
noConsistency bool
|
||||
}
|
||||
|
||||
func (o *configOptions) ToProject(ctx context.Context, dockerCli command.Cli, services []string, po ...cli.ProjectOptionsFn) (*types.Project, error) {
|
||||
po = append(po,
|
||||
func (o *configOptions) ToProject(services []string) (*types.Project, error) {
|
||||
return o.ProjectOptions.ToProject(services,
|
||||
cli.WithInterpolation(!o.noInterpolate),
|
||||
cli.WithResolvedPaths(!o.noResolvePath),
|
||||
cli.WithNormalization(!o.noNormalize),
|
||||
cli.WithConsistency(!o.noConsistency),
|
||||
cli.WithDefaultProfiles(o.Profiles...),
|
||||
cli.WithDiscardEnvFile,
|
||||
cli.WithContext(ctx))
|
||||
return o.ProjectOptions.ToProject(dockerCli, services, po...)
|
||||
cli.WithProfiles(o.Profiles),
|
||||
cli.WithDiscardEnvFile)
|
||||
}
|
||||
|
||||
func configCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func configCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *cobra.Command {
|
||||
opts := configOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -85,24 +82,24 @@ func configCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service
|
||||
}),
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
if opts.services {
|
||||
return runServices(ctx, dockerCli, opts)
|
||||
return runServices(streams, opts)
|
||||
}
|
||||
if opts.volumes {
|
||||
return runVolumes(ctx, dockerCli, opts)
|
||||
return runVolumes(streams, opts)
|
||||
}
|
||||
if opts.hash != "" {
|
||||
return runHash(ctx, dockerCli, opts)
|
||||
return runHash(streams, opts)
|
||||
}
|
||||
if opts.profiles {
|
||||
return runProfiles(ctx, dockerCli, opts, args)
|
||||
return runProfiles(streams, opts, args)
|
||||
}
|
||||
if opts.images {
|
||||
return runConfigImages(ctx, dockerCli, opts, args)
|
||||
return runConfigImages(streams, opts, args)
|
||||
}
|
||||
|
||||
return runConfig(ctx, dockerCli, backend, opts, args)
|
||||
return runConfig(ctx, streams, backend, opts, args)
|
||||
}),
|
||||
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||
ValidArgsFunction: completeServiceNames(p),
|
||||
}
|
||||
flags := cmd.Flags()
|
||||
flags.StringVar(&opts.Format, "format", "yaml", "Format the output. Values: [yaml | json]")
|
||||
@@ -123,9 +120,9 @@ func configCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runConfig(ctx context.Context, dockerCli command.Cli, backend api.Service, opts configOptions, services []string) error {
|
||||
func runConfig(ctx context.Context, streams api.Streams, backend api.Service, opts configOptions, services []string) error {
|
||||
var content []byte
|
||||
project, err := opts.ToProject(ctx, dockerCli, services)
|
||||
project, err := opts.ToProject(services)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -150,75 +147,67 @@ func runConfig(ctx context.Context, dockerCli command.Cli, backend api.Service,
|
||||
if opts.Output != "" && len(content) > 0 {
|
||||
return os.WriteFile(opts.Output, content, 0o666)
|
||||
}
|
||||
_, err = fmt.Fprint(dockerCli.Out(), string(content))
|
||||
_, err = fmt.Fprint(streams.Out(), string(content))
|
||||
return err
|
||||
}
|
||||
|
||||
func runServices(ctx context.Context, dockerCli command.Cli, opts configOptions) error {
|
||||
project, err := opts.ToProject(ctx, dockerCli, nil, cli.WithoutEnvironmentResolution)
|
||||
func runServices(streams api.Streams, opts configOptions) error {
|
||||
project, err := opts.ToProject(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = project.ForEachService(project.ServiceNames(), func(serviceName string, _ *types.ServiceConfig) error {
|
||||
fmt.Fprintln(dockerCli.Out(), serviceName)
|
||||
return project.WithServices(project.ServiceNames(), func(s types.ServiceConfig) error {
|
||||
fmt.Fprintln(streams.Out(), s.Name)
|
||||
return nil
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func runVolumes(ctx context.Context, dockerCli command.Cli, opts configOptions) error {
|
||||
project, err := opts.ToProject(ctx, dockerCli, nil, cli.WithoutEnvironmentResolution)
|
||||
func runVolumes(streams api.Streams, opts configOptions) error {
|
||||
project, err := opts.ToProject(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for n := range project.Volumes {
|
||||
fmt.Fprintln(dockerCli.Out(), n)
|
||||
fmt.Fprintln(streams.Out(), n)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func runHash(ctx context.Context, dockerCli command.Cli, opts configOptions) error {
|
||||
func runHash(streams api.Streams, opts configOptions) error {
|
||||
var services []string
|
||||
if opts.hash != "*" {
|
||||
services = append(services, strings.Split(opts.hash, ",")...)
|
||||
}
|
||||
project, err := opts.ToProject(ctx, dockerCli, nil, cli.WithoutEnvironmentResolution)
|
||||
project, err := opts.ToProject(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := applyPlatforms(project, true); err != nil {
|
||||
return err
|
||||
if len(services) > 0 {
|
||||
err = project.ForServices(services, types.IgnoreDependencies)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(services) == 0 {
|
||||
services = project.ServiceNames()
|
||||
}
|
||||
|
||||
sorted := services
|
||||
sorted := project.Services
|
||||
sort.Slice(sorted, func(i, j int) bool {
|
||||
return sorted[i] < sorted[j]
|
||||
return sorted[i].Name < sorted[j].Name
|
||||
})
|
||||
|
||||
for _, name := range sorted {
|
||||
s, err := project.GetService(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, s := range sorted {
|
||||
hash, err := compose.ServiceHash(s)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(dockerCli.Out(), "%s %s\n", name, hash)
|
||||
fmt.Fprintf(streams.Out(), "%s %s\n", s.Name, hash)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func runProfiles(ctx context.Context, dockerCli command.Cli, opts configOptions, services []string) error {
|
||||
func runProfiles(streams api.Streams, opts configOptions, services []string) error {
|
||||
set := map[string]struct{}{}
|
||||
project, err := opts.ToProject(ctx, dockerCli, services, cli.WithoutEnvironmentResolution)
|
||||
project, err := opts.ToProject(services)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -233,18 +222,18 @@ func runProfiles(ctx context.Context, dockerCli command.Cli, opts configOptions,
|
||||
}
|
||||
sort.Strings(profiles)
|
||||
for _, p := range profiles {
|
||||
fmt.Fprintln(dockerCli.Out(), p)
|
||||
fmt.Fprintln(streams.Out(), p)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func runConfigImages(ctx context.Context, dockerCli command.Cli, opts configOptions, services []string) error {
|
||||
project, err := opts.ToProject(ctx, dockerCli, services, cli.WithoutEnvironmentResolution)
|
||||
func runConfigImages(streams api.Streams, opts configOptions, services []string) error {
|
||||
project, err := opts.ToProject(services)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, s := range project.Services {
|
||||
fmt.Fprintln(dockerCli.Out(), api.GetImageNameOrDefault(s, project.Name))
|
||||
fmt.Fprintln(streams.Out(), api.GetImageNameOrDefault(s, project.Name))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
"errors"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
@@ -38,7 +37,7 @@ type copyOptions struct {
|
||||
copyUIDGID bool
|
||||
}
|
||||
|
||||
func copyCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func copyCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
|
||||
opts := copyOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -59,9 +58,9 @@ func copyCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
RunE: AdaptCmd(func(ctx context.Context, cmd *cobra.Command, args []string) error {
|
||||
opts.source = args[0]
|
||||
opts.destination = args[1]
|
||||
return runCopy(ctx, dockerCli, backend, opts)
|
||||
return runCopy(ctx, backend, opts)
|
||||
}),
|
||||
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||
ValidArgsFunction: completeServiceNames(p),
|
||||
}
|
||||
|
||||
flags := copyCmd.Flags()
|
||||
@@ -75,8 +74,8 @@ func copyCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
return copyCmd
|
||||
}
|
||||
|
||||
func runCopy(ctx context.Context, dockerCli command.Cli, backend api.Service, opts copyOptions) error {
|
||||
name, err := opts.toProjectName(dockerCli)
|
||||
func runCopy(ctx context.Context, backend api.Service, opts copyOptions) error {
|
||||
name, err := opts.toProjectName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -19,13 +19,11 @@ package compose
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
@@ -48,11 +46,8 @@ type createOptions struct {
|
||||
scale []string
|
||||
}
|
||||
|
||||
func createCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func createCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
|
||||
opts := createOptions{}
|
||||
buildOpts := buildOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
cmd := &cobra.Command{
|
||||
Use: "create [OPTIONS] [SERVICE...]",
|
||||
Short: "Creates containers for a service.",
|
||||
@@ -66,15 +61,26 @@ func createCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service
|
||||
}
|
||||
return nil
|
||||
}),
|
||||
RunE: p.WithServices(dockerCli, func(ctx context.Context, project *types.Project, services []string) error {
|
||||
return runCreate(ctx, dockerCli, backend, opts, buildOpts, project, services)
|
||||
RunE: p.WithProject(func(ctx context.Context, project *types.Project) error {
|
||||
if err := opts.Apply(project); err != nil {
|
||||
return err
|
||||
}
|
||||
return backend.Create(ctx, project, api.CreateOptions{
|
||||
RemoveOrphans: opts.removeOrphans,
|
||||
IgnoreOrphans: opts.ignoreOrphans,
|
||||
Recreate: opts.recreateStrategy(),
|
||||
RecreateDependencies: opts.dependenciesRecreateStrategy(),
|
||||
Inherit: !opts.noInherit,
|
||||
Timeout: opts.GetTimeout(),
|
||||
QuietPull: false,
|
||||
})
|
||||
}),
|
||||
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||
ValidArgsFunction: completeServiceNames(p),
|
||||
}
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVar(&opts.Build, "build", false, "Build images before starting containers.")
|
||||
flags.BoolVar(&opts.noBuild, "no-build", false, "Don't build an image, even if it's policy.")
|
||||
flags.StringVar(&opts.Pull, "pull", "policy", `Pull image before running ("always"|"missing"|"never"|"build")`)
|
||||
flags.BoolVar(&opts.noBuild, "no-build", false, "Don't build an image, even if it's missing.")
|
||||
flags.StringVar(&opts.Pull, "pull", "missing", `Pull image before running ("always"|"missing"|"never")`)
|
||||
flags.BoolVar(&opts.forceRecreate, "force-recreate", false, "Recreate containers even if their configuration and image haven't changed.")
|
||||
flags.BoolVar(&opts.noRecreate, "no-recreate", false, "If containers already exist, don't recreate them. Incompatible with --force-recreate.")
|
||||
flags.BoolVar(&opts.removeOrphans, "remove-orphans", false, "Remove containers for services not defined in the Compose file.")
|
||||
@@ -82,33 +88,6 @@ func createCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runCreate(ctx context.Context, _ command.Cli, backend api.Service, createOpts createOptions, buildOpts buildOptions, project *types.Project, services []string) error {
|
||||
if err := createOpts.Apply(project); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var build *api.BuildOptions
|
||||
if !createOpts.noBuild {
|
||||
bo, err := buildOpts.toAPIBuildOptions(services)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
build = &bo
|
||||
}
|
||||
|
||||
return backend.Create(ctx, project, api.CreateOptions{
|
||||
Build: build,
|
||||
Services: services,
|
||||
RemoveOrphans: createOpts.removeOrphans,
|
||||
IgnoreOrphans: createOpts.ignoreOrphans,
|
||||
Recreate: createOpts.recreateStrategy(),
|
||||
RecreateDependencies: createOpts.dependenciesRecreateStrategy(),
|
||||
Inherit: !createOpts.noInherit,
|
||||
Timeout: createOpts.GetTimeout(),
|
||||
QuietPull: false,
|
||||
})
|
||||
}
|
||||
|
||||
func (opts createOptions) recreateStrategy() string {
|
||||
if opts.noRecreate {
|
||||
return api.RecreateNever
|
||||
@@ -139,17 +118,11 @@ func (opts createOptions) GetTimeout() *time.Duration {
|
||||
|
||||
func (opts createOptions) Apply(project *types.Project) error {
|
||||
if opts.pullChanged {
|
||||
if !opts.isPullPolicyValid() {
|
||||
return fmt.Errorf("invalid --pull option %q", opts.Pull)
|
||||
}
|
||||
for i, service := range project.Services {
|
||||
service.PullPolicy = opts.Pull
|
||||
project.Services[i] = service
|
||||
}
|
||||
}
|
||||
// N.B. opts.Build means "force build all", but images can still be built
|
||||
// when this is false
|
||||
// e.g. if a service has pull_policy: build or its local image is policy
|
||||
if opts.Build {
|
||||
for i, service := range project.Services {
|
||||
if service.Build == nil {
|
||||
@@ -159,20 +132,16 @@ func (opts createOptions) Apply(project *types.Project) error {
|
||||
project.Services[i] = service
|
||||
}
|
||||
}
|
||||
|
||||
if err := applyPlatforms(project, true); err != nil {
|
||||
return err
|
||||
if opts.noBuild {
|
||||
for i, service := range project.Services {
|
||||
service.Build = nil
|
||||
if service.Image == "" {
|
||||
service.Image = api.GetImageNameOrDefault(service, project.Name)
|
||||
}
|
||||
project.Services[i] = service
|
||||
}
|
||||
}
|
||||
|
||||
err := applyScaleOpts(project, opts.scale)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func applyScaleOpts(project *types.Project, opts []string) error {
|
||||
for _, scale := range opts {
|
||||
for _, scale := range opts.scale {
|
||||
split := strings.Split(scale, "=")
|
||||
if len(split) != 2 {
|
||||
return fmt.Errorf("invalid --scale option %q. Should be SERVICE=NUM", scale)
|
||||
@@ -182,16 +151,10 @@ func applyScaleOpts(project *types.Project, opts []string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = setServiceScale(project, name, replicas)
|
||||
err = setServiceScale(project, name, uint64(replicas))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (opts createOptions) isPullPolicyValid() bool {
|
||||
pullPolicies := []string{types.PullPolicyAlways, types.PullPolicyNever, types.PullPolicyBuild,
|
||||
types.PullPolicyMissing, types.PullPolicyIfNotPresent}
|
||||
return slices.Contains(pullPolicies, opts.Pull)
|
||||
}
|
||||
|
||||
@@ -1,170 +0,0 @@
|
||||
/*
|
||||
Copyright 2023 Docker Compose CLI authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package compose
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/mocks"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
func TestRunCreate(t *testing.T) {
|
||||
ctrl, ctx := gomock.WithContext(context.Background(), t)
|
||||
backend := mocks.NewMockService(ctrl)
|
||||
backend.EXPECT().Create(
|
||||
gomock.Eq(ctx),
|
||||
pullPolicy(""),
|
||||
deepEqual(defaultCreateOptions(true)),
|
||||
)
|
||||
|
||||
createOpts := createOptions{}
|
||||
buildOpts := buildOptions{}
|
||||
project := sampleProject()
|
||||
err := runCreate(ctx, nil, backend, createOpts, buildOpts, project, nil)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestRunCreate_Build(t *testing.T) {
|
||||
ctrl, ctx := gomock.WithContext(context.Background(), t)
|
||||
backend := mocks.NewMockService(ctrl)
|
||||
backend.EXPECT().Create(
|
||||
gomock.Eq(ctx),
|
||||
pullPolicy("build"),
|
||||
deepEqual(defaultCreateOptions(true)),
|
||||
)
|
||||
|
||||
createOpts := createOptions{
|
||||
Build: true,
|
||||
}
|
||||
buildOpts := buildOptions{}
|
||||
project := sampleProject()
|
||||
err := runCreate(ctx, nil, backend, createOpts, buildOpts, project, nil)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestRunCreate_NoBuild(t *testing.T) {
|
||||
ctrl, ctx := gomock.WithContext(context.Background(), t)
|
||||
backend := mocks.NewMockService(ctrl)
|
||||
backend.EXPECT().Create(
|
||||
gomock.Eq(ctx),
|
||||
pullPolicy(""),
|
||||
deepEqual(defaultCreateOptions(false)),
|
||||
)
|
||||
|
||||
createOpts := createOptions{
|
||||
noBuild: true,
|
||||
}
|
||||
buildOpts := buildOptions{}
|
||||
project := sampleProject()
|
||||
err := runCreate(ctx, nil, backend, createOpts, buildOpts, project, nil)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func sampleProject() *types.Project {
|
||||
return &types.Project{
|
||||
Name: "test",
|
||||
Services: types.Services{
|
||||
"svc": {
|
||||
Name: "svc",
|
||||
Build: &types.BuildConfig{
|
||||
Context: ".",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func defaultCreateOptions(includeBuild bool) api.CreateOptions {
|
||||
var build *api.BuildOptions
|
||||
if includeBuild {
|
||||
bo := defaultBuildOptions()
|
||||
build = &bo
|
||||
}
|
||||
return api.CreateOptions{
|
||||
Build: build,
|
||||
Services: nil,
|
||||
RemoveOrphans: false,
|
||||
IgnoreOrphans: false,
|
||||
Recreate: "diverged",
|
||||
RecreateDependencies: "diverged",
|
||||
Inherit: true,
|
||||
Timeout: nil,
|
||||
QuietPull: false,
|
||||
}
|
||||
}
|
||||
|
||||
func defaultBuildOptions() api.BuildOptions {
|
||||
return api.BuildOptions{
|
||||
Args: make(types.MappingWithEquals),
|
||||
Progress: "auto",
|
||||
}
|
||||
}
|
||||
|
||||
// deepEqual returns a nice diff on failure vs gomock.Eq when used
|
||||
// on structs.
|
||||
func deepEqual(x interface{}) gomock.Matcher {
|
||||
return gomock.GotFormatterAdapter(
|
||||
gomock.GotFormatterFunc(func(got interface{}) string {
|
||||
return cmp.Diff(x, got)
|
||||
}),
|
||||
gomock.Eq(x),
|
||||
)
|
||||
}
|
||||
|
||||
func spewAdapter(m gomock.Matcher) gomock.Matcher {
|
||||
return gomock.GotFormatterAdapter(
|
||||
gomock.GotFormatterFunc(func(got interface{}) string {
|
||||
return spew.Sdump(got)
|
||||
}),
|
||||
m,
|
||||
)
|
||||
}
|
||||
|
||||
type withPullPolicy struct {
|
||||
policy string
|
||||
}
|
||||
|
||||
func pullPolicy(policy string) gomock.Matcher {
|
||||
return spewAdapter(withPullPolicy{policy: policy})
|
||||
}
|
||||
|
||||
func (w withPullPolicy) Matches(x interface{}) bool {
|
||||
proj, ok := x.(*types.Project)
|
||||
if !ok || proj == nil || len(proj.Services) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, svc := range proj.Services {
|
||||
if svc.PullPolicy != w.policy {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (w withPullPolicy) String() string {
|
||||
return fmt.Sprintf("has pull policy %q for all services", w.policy)
|
||||
}
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/compose/v2/pkg/utils"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -40,7 +39,7 @@ type downOptions struct {
|
||||
images string
|
||||
}
|
||||
|
||||
func downCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func downCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
|
||||
opts := downOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -57,7 +56,7 @@ func downCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
return nil
|
||||
}),
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
return runDown(ctx, dockerCli, backend, opts, args)
|
||||
return runDown(ctx, backend, opts, args)
|
||||
}),
|
||||
ValidArgsFunction: noCompletion(),
|
||||
}
|
||||
@@ -77,8 +76,8 @@ func downCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
return downCmd
|
||||
}
|
||||
|
||||
func runDown(ctx context.Context, dockerCli command.Cli, backend api.Service, opts downOptions, services []string) error {
|
||||
project, name, err := opts.projectOrName(dockerCli, services...)
|
||||
func runDown(ctx context.Context, backend api.Service, opts downOptions, services []string) error {
|
||||
project, name, err := opts.projectOrName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
@@ -32,7 +31,7 @@ type eventsOpts struct {
|
||||
json bool
|
||||
}
|
||||
|
||||
func eventsCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func eventsCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *cobra.Command {
|
||||
opts := eventsOpts{
|
||||
composeOptions: &composeOptions{
|
||||
ProjectOptions: p,
|
||||
@@ -42,17 +41,17 @@ func eventsCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service
|
||||
Use: "events [OPTIONS] [SERVICE...]",
|
||||
Short: "Receive real time events from containers.",
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
return runEvents(ctx, dockerCli, backend, opts, args)
|
||||
return runEvents(ctx, streams, backend, opts, args)
|
||||
}),
|
||||
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||
ValidArgsFunction: completeServiceNames(p),
|
||||
}
|
||||
|
||||
cmd.Flags().BoolVar(&opts.json, "json", false, "Output events as a stream of json objects")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runEvents(ctx context.Context, dockerCli command.Cli, backend api.Service, opts eventsOpts, services []string) error {
|
||||
name, err := opts.toProjectName(dockerCli)
|
||||
func runEvents(ctx context.Context, streams api.Streams, backend api.Service, opts eventsOpts, services []string) error {
|
||||
name, err := opts.toProjectName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -72,9 +71,9 @@ func runEvents(ctx context.Context, dockerCli command.Cli, backend api.Service,
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(dockerCli.Out(), string(marshal))
|
||||
fmt.Fprintln(streams.Out(), string(marshal))
|
||||
} else {
|
||||
fmt.Fprintln(dockerCli.Out(), event)
|
||||
fmt.Fprintln(streams.Out(), event)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
|
||||
@@ -19,9 +19,8 @@ package compose
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/compose"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -43,7 +42,7 @@ type execOpts struct {
|
||||
interactive bool
|
||||
}
|
||||
|
||||
func execCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func execCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *cobra.Command {
|
||||
opts := execOpts{
|
||||
composeOptions: &composeOptions{
|
||||
ProjectOptions: p,
|
||||
@@ -59,9 +58,9 @@ func execCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
return nil
|
||||
}),
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
return runExec(ctx, dockerCli, backend, opts)
|
||||
return runExec(ctx, backend, opts)
|
||||
}),
|
||||
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||
ValidArgsFunction: completeServiceNames(p),
|
||||
}
|
||||
|
||||
runCmd.Flags().BoolVarP(&opts.detach, "detach", "d", false, "Detached mode: Run command in the background.")
|
||||
@@ -69,7 +68,7 @@ func execCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
runCmd.Flags().IntVar(&opts.index, "index", 0, "index of the container if service has multiple replicas")
|
||||
runCmd.Flags().BoolVarP(&opts.privileged, "privileged", "", false, "Give extended privileges to the process.")
|
||||
runCmd.Flags().StringVarP(&opts.user, "user", "u", "", "Run the command as this user.")
|
||||
runCmd.Flags().BoolVarP(&opts.noTty, "no-TTY", "T", !dockerCli.Out().IsTerminal(), "Disable pseudo-TTY allocation. By default `docker compose exec` allocates a TTY.")
|
||||
runCmd.Flags().BoolVarP(&opts.noTty, "no-TTY", "T", !streams.Out().IsTerminal(), "Disable pseudo-TTY allocation. By default `docker compose exec` allocates a TTY.")
|
||||
runCmd.Flags().StringVarP(&opts.workingDir, "workdir", "w", "", "Path to workdir directory for this command.")
|
||||
|
||||
runCmd.Flags().BoolVarP(&opts.interactive, "interactive", "i", true, "Keep STDIN open even if not attached.")
|
||||
@@ -81,8 +80,8 @@ func execCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
return runCmd
|
||||
}
|
||||
|
||||
func runExec(ctx context.Context, dockerCli command.Cli, backend api.Service, opts execOpts) error {
|
||||
projectName, err := opts.toProjectName(dockerCli)
|
||||
func runExec(ctx context.Context, backend api.Service, opts execOpts) error {
|
||||
projectName, err := opts.toProjectName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -39,7 +38,7 @@ type imageOptions struct {
|
||||
Format string
|
||||
}
|
||||
|
||||
func imagesCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func imagesCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *cobra.Command {
|
||||
opts := imageOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -47,17 +46,17 @@ func imagesCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service
|
||||
Use: "images [OPTIONS] [SERVICE...]",
|
||||
Short: "List images used by the created containers",
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
return runImages(ctx, dockerCli, backend, opts, args)
|
||||
return runImages(ctx, streams, backend, opts, args)
|
||||
}),
|
||||
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||
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
|
||||
}
|
||||
|
||||
func runImages(ctx context.Context, dockerCli command.Cli, backend api.Service, opts imageOptions, services []string) error {
|
||||
projectName, err := opts.toProjectName(dockerCli)
|
||||
func runImages(ctx context.Context, streams api.Streams, backend api.Service, opts imageOptions, services []string) error {
|
||||
projectName, err := opts.toProjectName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -81,7 +80,7 @@ func runImages(ctx context.Context, dockerCli command.Cli, backend api.Service,
|
||||
}
|
||||
}
|
||||
for _, img := range ids {
|
||||
fmt.Fprintln(dockerCli.Out(), img)
|
||||
fmt.Fprintln(streams.Out(), img)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -90,7 +89,7 @@ func runImages(ctx context.Context, dockerCli command.Cli, backend api.Service,
|
||||
return images[i].ContainerName < images[j].ContainerName
|
||||
})
|
||||
|
||||
return formatter.Print(images, opts.Format, dockerCli.Out(),
|
||||
return formatter.Print(images, opts.Format, streams.Out(),
|
||||
func(w io.Writer) {
|
||||
for _, img := range images {
|
||||
id := stringid.TruncateID(img.ID)
|
||||
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
@@ -33,7 +32,7 @@ type killOptions struct {
|
||||
signal string
|
||||
}
|
||||
|
||||
func killCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func killCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
|
||||
opts := killOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -41,9 +40,9 @@ func killCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
Use: "kill [OPTIONS] [SERVICE...]",
|
||||
Short: "Force stop service containers.",
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
return runKill(ctx, dockerCli, backend, opts, args)
|
||||
return runKill(ctx, backend, opts, args)
|
||||
}),
|
||||
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||
ValidArgsFunction: completeServiceNames(p),
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
@@ -54,8 +53,8 @@ func killCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runKill(ctx context.Context, dockerCli command.Cli, backend api.Service, opts killOptions, services []string) error {
|
||||
project, name, err := opts.projectOrName(dockerCli, services...)
|
||||
func runKill(ctx context.Context, backend api.Service, opts killOptions, services []string) error {
|
||||
project, name, err := opts.projectOrName(services...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/compose/v2/cmd/formatter"
|
||||
|
||||
"github.com/docker/cli/opts"
|
||||
@@ -38,13 +37,13 @@ type lsOptions struct {
|
||||
Filter opts.FilterOpt
|
||||
}
|
||||
|
||||
func listCommand(dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func listCommand(streams api.Streams, backend api.Service) *cobra.Command {
|
||||
lsOpts := lsOptions{Filter: opts.NewFilterOpt()}
|
||||
lsCmd := &cobra.Command{
|
||||
Use: "ls [OPTIONS]",
|
||||
Short: "List running compose projects",
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
return runList(ctx, dockerCli, backend, lsOpts)
|
||||
return runList(ctx, streams, backend, lsOpts)
|
||||
}),
|
||||
Args: cobra.NoArgs,
|
||||
ValidArgsFunction: noCompletion(),
|
||||
@@ -61,7 +60,7 @@ var acceptedListFilters = map[string]bool{
|
||||
"name": true,
|
||||
}
|
||||
|
||||
func runList(ctx context.Context, dockerCli command.Cli, backend api.Service, lsOpts lsOptions) error {
|
||||
func runList(ctx context.Context, streams api.Streams, backend api.Service, lsOpts lsOptions) error {
|
||||
filters := lsOpts.Filter.Value()
|
||||
err := filters.Validate(acceptedListFilters)
|
||||
if err != nil {
|
||||
@@ -72,6 +71,12 @@ func runList(ctx context.Context, dockerCli command.Cli, backend api.Service, ls
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if lsOpts.Quiet {
|
||||
for _, s := range stackList {
|
||||
fmt.Fprintln(streams.Out(), s.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if filters.Len() > 0 {
|
||||
var filtered []api.Stack
|
||||
@@ -84,15 +89,8 @@ func runList(ctx context.Context, dockerCli command.Cli, backend api.Service, ls
|
||||
stackList = filtered
|
||||
}
|
||||
|
||||
if lsOpts.Quiet {
|
||||
for _, s := range stackList {
|
||||
fmt.Fprintln(dockerCli.Out(), s.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
view := viewFromStackList(stackList)
|
||||
return formatter.Print(view, lsOpts.Format, dockerCli.Out(), func(w io.Writer) {
|
||||
return formatter.Print(view, lsOpts.Format, streams.Out(), func(w io.Writer) {
|
||||
for _, stack := range view {
|
||||
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\n", stack.Name, stack.Status, stack.ConfigFiles)
|
||||
}
|
||||
|
||||
@@ -18,9 +18,7 @@ package compose
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose/v2/cmd/formatter"
|
||||
@@ -31,7 +29,6 @@ type logsOptions struct {
|
||||
*ProjectOptions
|
||||
composeOptions
|
||||
follow bool
|
||||
index int
|
||||
tail string
|
||||
since string
|
||||
until string
|
||||
@@ -40,7 +37,7 @@ type logsOptions struct {
|
||||
timestamps bool
|
||||
}
|
||||
|
||||
func logsCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func logsCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *cobra.Command {
|
||||
opts := logsOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -48,19 +45,12 @@ func logsCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
Use: "logs [OPTIONS] [SERVICE...]",
|
||||
Short: "View output from containers",
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
return runLogs(ctx, dockerCli, backend, opts, args)
|
||||
return runLogs(ctx, streams, backend, opts, args)
|
||||
}),
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
if opts.index > 0 && len(args) != 1 {
|
||||
return errors.New("--index requires one service to be selected")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||
ValidArgsFunction: completeServiceNames(p),
|
||||
}
|
||||
flags := logsCmd.Flags()
|
||||
flags.BoolVarP(&opts.follow, "follow", "f", false, "Follow log output.")
|
||||
flags.IntVar(&opts.index, "index", 0, "index of the container if service has multiple replicas")
|
||||
flags.StringVar(&opts.since, "since", "", "Show logs since timestamp (e.g. 2013-01-02T13:23:37Z) or relative (e.g. 42m for 42 minutes)")
|
||||
flags.StringVar(&opts.until, "until", "", "Show logs before a timestamp (e.g. 2013-01-02T13:23:37Z) or relative (e.g. 42m for 42 minutes)")
|
||||
flags.BoolVar(&opts.noColor, "no-color", false, "Produce monochrome output.")
|
||||
@@ -70,17 +60,16 @@ func logsCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
return logsCmd
|
||||
}
|
||||
|
||||
func runLogs(ctx context.Context, dockerCli command.Cli, backend api.Service, opts logsOptions, services []string) error {
|
||||
project, name, err := opts.projectOrName(dockerCli, services...)
|
||||
func runLogs(ctx context.Context, streams api.Streams, backend api.Service, opts logsOptions, services []string) error {
|
||||
project, name, err := opts.projectOrName(services...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
consumer := formatter.NewLogConsumer(ctx, dockerCli.Out(), dockerCli.Err(), !opts.noColor, !opts.noPrefix, false)
|
||||
consumer := formatter.NewLogConsumer(ctx, streams.Out(), streams.Err(), !opts.noColor, !opts.noPrefix, false)
|
||||
return backend.Logs(ctx, name, consumer, api.LogOptions{
|
||||
Project: project,
|
||||
Services: services,
|
||||
Follow: opts.follow,
|
||||
Index: opts.index,
|
||||
Tail: opts.tail,
|
||||
Since: opts.since,
|
||||
Until: opts.until,
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
Copyright 2023 Docker Compose CLI authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package compose
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/compose/v2/pkg/utils"
|
||||
)
|
||||
|
||||
func applyPlatforms(project *types.Project, buildForSinglePlatform bool) error {
|
||||
defaultPlatform := project.Environment["DOCKER_DEFAULT_PLATFORM"]
|
||||
for name, service := range project.Services {
|
||||
if service.Build == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// default platform only applies if the service doesn't specify
|
||||
if defaultPlatform != "" && service.Platform == "" {
|
||||
if len(service.Build.Platforms) > 0 && !utils.StringContains(service.Build.Platforms, defaultPlatform) {
|
||||
return fmt.Errorf("service %q build.platforms does not support value set by DOCKER_DEFAULT_PLATFORM: %s", name, defaultPlatform)
|
||||
}
|
||||
service.Platform = defaultPlatform
|
||||
}
|
||||
|
||||
if service.Platform != "" {
|
||||
if len(service.Build.Platforms) > 0 {
|
||||
if !utils.StringContains(service.Build.Platforms, service.Platform) {
|
||||
return fmt.Errorf("service %q build configuration does not support platform: %s", name, service.Platform)
|
||||
}
|
||||
}
|
||||
|
||||
if buildForSinglePlatform || len(service.Build.Platforms) == 0 {
|
||||
// if we're building for a single platform, we want to build for the platform we'll use to run the image
|
||||
// similarly, if no build platforms were explicitly specified, it makes sense to build for the platform
|
||||
// the image is designed for rather than allowing the builder to infer the platform
|
||||
service.Build.Platforms = []string{service.Platform}
|
||||
}
|
||||
}
|
||||
|
||||
// services can specify that they should be built for multiple platforms, which can be used
|
||||
// with `docker compose build` to produce a multi-arch image
|
||||
// other cases, such as `up` and `run`, need a single architecture to actually run
|
||||
// if there is only a single platform present (which might have been inferred
|
||||
// from service.Platform above), it will be used, even if it requires emulation.
|
||||
// if there's more than one platform, then the list is cleared so that the builder
|
||||
// can decide.
|
||||
// TODO(milas): there's no validation that the platform the builder will pick is actually one
|
||||
// of the supported platforms from the build definition
|
||||
// e.g. `build.platforms: [linux/arm64, linux/amd64]` on a `linux/ppc64` machine would build
|
||||
// for `linux/ppc64` instead of returning an error that it's not a valid platform for the service.
|
||||
if buildForSinglePlatform && len(service.Build.Platforms) > 1 {
|
||||
// empty indicates that the builder gets to decide
|
||||
service.Build.Platforms = nil
|
||||
}
|
||||
project.Services[name] = service
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
/*
|
||||
Copyright 2023 Docker Compose CLI authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package compose
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestApplyPlatforms_InferFromRuntime(t *testing.T) {
|
||||
makeProject := func() *types.Project {
|
||||
return &types.Project{
|
||||
Services: types.Services{
|
||||
"test": {
|
||||
Name: "test",
|
||||
Image: "foo",
|
||||
Build: &types.BuildConfig{
|
||||
Context: ".",
|
||||
Platforms: []string{
|
||||
"linux/amd64",
|
||||
"linux/arm64",
|
||||
"alice/32",
|
||||
},
|
||||
},
|
||||
Platform: "alice/32",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("SinglePlatform", func(t *testing.T) {
|
||||
project := makeProject()
|
||||
require.NoError(t, applyPlatforms(project, true))
|
||||
require.EqualValues(t, []string{"alice/32"}, project.Services["test"].Build.Platforms)
|
||||
})
|
||||
|
||||
t.Run("MultiPlatform", func(t *testing.T) {
|
||||
project := makeProject()
|
||||
require.NoError(t, applyPlatforms(project, false))
|
||||
require.EqualValues(t, []string{"linux/amd64", "linux/arm64", "alice/32"},
|
||||
project.Services["test"].Build.Platforms)
|
||||
})
|
||||
}
|
||||
|
||||
func TestApplyPlatforms_DockerDefaultPlatform(t *testing.T) {
|
||||
makeProject := func() *types.Project {
|
||||
return &types.Project{
|
||||
Environment: map[string]string{
|
||||
"DOCKER_DEFAULT_PLATFORM": "linux/amd64",
|
||||
},
|
||||
Services: types.Services{
|
||||
"test": {
|
||||
Name: "test",
|
||||
Image: "foo",
|
||||
Build: &types.BuildConfig{
|
||||
Context: ".",
|
||||
Platforms: []string{
|
||||
"linux/amd64",
|
||||
"linux/arm64",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("SinglePlatform", func(t *testing.T) {
|
||||
project := makeProject()
|
||||
require.NoError(t, applyPlatforms(project, true))
|
||||
require.EqualValues(t, []string{"linux/amd64"}, project.Services["test"].Build.Platforms)
|
||||
})
|
||||
|
||||
t.Run("MultiPlatform", func(t *testing.T) {
|
||||
project := makeProject()
|
||||
require.NoError(t, applyPlatforms(project, false))
|
||||
require.EqualValues(t, []string{"linux/amd64", "linux/arm64"},
|
||||
project.Services["test"].Build.Platforms)
|
||||
})
|
||||
}
|
||||
|
||||
func TestApplyPlatforms_UnsupportedPlatform(t *testing.T) {
|
||||
makeProject := func() *types.Project {
|
||||
return &types.Project{
|
||||
Environment: map[string]string{
|
||||
"DOCKER_DEFAULT_PLATFORM": "commodore/64",
|
||||
},
|
||||
Services: types.Services{
|
||||
"test": {
|
||||
Name: "test",
|
||||
Image: "foo",
|
||||
Build: &types.BuildConfig{
|
||||
Context: ".",
|
||||
Platforms: []string{
|
||||
"linux/amd64",
|
||||
"linux/arm64",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("SinglePlatform", func(t *testing.T) {
|
||||
project := makeProject()
|
||||
require.EqualError(t, applyPlatforms(project, true),
|
||||
`service "test" build.platforms does not support value set by DOCKER_DEFAULT_PLATFORM: commodore/64`)
|
||||
})
|
||||
|
||||
t.Run("MultiPlatform", func(t *testing.T) {
|
||||
project := makeProject()
|
||||
require.EqualError(t, applyPlatforms(project, false),
|
||||
`service "test" build.platforms does not support value set by DOCKER_DEFAULT_PLATFORM: commodore/64`)
|
||||
})
|
||||
}
|
||||
@@ -19,7 +19,6 @@ package compose
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
@@ -29,7 +28,7 @@ type pauseOptions struct {
|
||||
*ProjectOptions
|
||||
}
|
||||
|
||||
func pauseCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func pauseCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
|
||||
opts := pauseOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -37,15 +36,15 @@ func pauseCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
Use: "pause [SERVICE...]",
|
||||
Short: "Pause services",
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
return runPause(ctx, dockerCli, backend, opts, args)
|
||||
return runPause(ctx, backend, opts, args)
|
||||
}),
|
||||
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||
ValidArgsFunction: completeServiceNames(p),
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runPause(ctx context.Context, dockerCli command.Cli, backend api.Service, opts pauseOptions, services []string) error {
|
||||
project, name, err := opts.projectOrName(dockerCli, services...)
|
||||
func runPause(ctx context.Context, backend api.Service, opts pauseOptions, services []string) error {
|
||||
project, name, err := opts.projectOrName(services...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -60,7 +59,7 @@ type unpauseOptions struct {
|
||||
*ProjectOptions
|
||||
}
|
||||
|
||||
func unpauseCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func unpauseCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
|
||||
opts := unpauseOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -68,15 +67,15 @@ func unpauseCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Servic
|
||||
Use: "unpause [SERVICE...]",
|
||||
Short: "Unpause services",
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
return runUnPause(ctx, dockerCli, backend, opts, args)
|
||||
return runUnPause(ctx, backend, opts, args)
|
||||
}),
|
||||
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||
ValidArgsFunction: completeServiceNames(p),
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runUnPause(ctx context.Context, dockerCli command.Cli, backend api.Service, opts unpauseOptions, services []string) error {
|
||||
project, name, err := opts.projectOrName(dockerCli, services...)
|
||||
func runUnPause(ctx context.Context, backend api.Service, opts unpauseOptions, services []string) error {
|
||||
project, name, err := opts.projectOrName(services...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
@@ -35,7 +34,7 @@ type portOptions struct {
|
||||
index int
|
||||
}
|
||||
|
||||
func portCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func portCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *cobra.Command {
|
||||
opts := portOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -53,17 +52,17 @@ func portCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
return nil
|
||||
}),
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
return runPort(ctx, dockerCli, backend, opts, args[0])
|
||||
return runPort(ctx, streams, backend, opts, args[0])
|
||||
}),
|
||||
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||
ValidArgsFunction: completeServiceNames(p),
|
||||
}
|
||||
cmd.Flags().StringVar(&opts.protocol, "protocol", "tcp", "tcp or udp")
|
||||
cmd.Flags().IntVar(&opts.index, "index", 0, "index of the container if service has multiple replicas")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runPort(ctx context.Context, dockerCli command.Cli, backend api.Service, opts portOptions, service string) error {
|
||||
projectName, err := opts.toProjectName(dockerCli)
|
||||
func runPort(ctx context.Context, streams api.Streams, backend api.Service, opts portOptions, service string) error {
|
||||
projectName, err := opts.toProjectName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -75,6 +74,6 @@ func runPort(ctx context.Context, dockerCli command.Cli, backend api.Service, op
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(dockerCli.Out(), "%s:%d\n", ip, port)
|
||||
fmt.Fprintf(streams.Out(), "%s:%d\n", ip, port)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -18,19 +18,23 @@ package compose
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/compose/v2/cmd/formatter"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/utils"
|
||||
"github.com/docker/docker/api/types"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
cliformatter "github.com/docker/cli/cli/command/formatter"
|
||||
cliflags "github.com/docker/cli/cli/flags"
|
||||
formatter2 "github.com/docker/cli/cli/command/formatter"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
)
|
||||
|
||||
type psOptions struct {
|
||||
@@ -41,8 +45,6 @@ type psOptions struct {
|
||||
Services bool
|
||||
Filter string
|
||||
Status []string
|
||||
noTrunc bool
|
||||
Orphans bool
|
||||
}
|
||||
|
||||
func (p *psOptions) parseFilter() error {
|
||||
@@ -64,7 +66,7 @@ func (p *psOptions) parseFilter() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func psCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func psCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *cobra.Command {
|
||||
opts := psOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -75,39 +77,32 @@ func psCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *c
|
||||
return opts.parseFilter()
|
||||
},
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
return runPs(ctx, dockerCli, backend, args, opts)
|
||||
return runPs(ctx, streams, backend, args, opts)
|
||||
}),
|
||||
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||
ValidArgsFunction: completeServiceNames(p),
|
||||
}
|
||||
flags := psCmd.Flags()
|
||||
flags.StringVar(&opts.Format, "format", "table", cliflags.FormatHelp)
|
||||
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")
|
||||
flags.BoolVar(&opts.Services, "services", false, "Display services")
|
||||
flags.BoolVar(&opts.Orphans, "orphans", true, "Include orphaned services (not declared by project)")
|
||||
flags.BoolVarP(&opts.All, "all", "a", false, "Show all stopped containers (including those created by the run command)")
|
||||
flags.BoolVar(&opts.noTrunc, "no-trunc", false, "Don't truncate output")
|
||||
return psCmd
|
||||
}
|
||||
|
||||
func runPs(ctx context.Context, dockerCli command.Cli, backend api.Service, services []string, opts psOptions) error {
|
||||
project, name, err := opts.projectOrName(dockerCli, services...)
|
||||
func runPs(ctx context.Context, streams api.Streams, backend api.Service, services []string, opts psOptions) error {
|
||||
project, name, err := opts.projectOrName(services...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if project != nil {
|
||||
if project != nil && len(services) > 0 {
|
||||
names := project.ServiceNames()
|
||||
if len(services) > 0 {
|
||||
for _, service := range services {
|
||||
if !utils.StringContains(names, service) {
|
||||
return fmt.Errorf("no such service: %s", service)
|
||||
}
|
||||
for _, service := range services {
|
||||
if !utils.StringContains(names, service) {
|
||||
return fmt.Errorf("no such service: %s", service)
|
||||
}
|
||||
} else if !opts.Orphans {
|
||||
// until user asks to list orphaned services, we only include those declared in project
|
||||
services = names
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,33 +125,38 @@ func runPs(ctx context.Context, dockerCli command.Cli, backend api.Service, serv
|
||||
|
||||
if opts.Quiet {
|
||||
for _, c := range containers {
|
||||
fmt.Fprintln(dockerCli.Out(), c.ID)
|
||||
fmt.Fprintln(streams.Out(), c.ID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if opts.Services {
|
||||
services := []string{}
|
||||
for _, c := range containers {
|
||||
s := c.Service
|
||||
if !utils.StringContains(services, s) {
|
||||
services = append(services, s)
|
||||
for _, s := range containers {
|
||||
if !utils.StringContains(services, s.Service) {
|
||||
services = append(services, s.Service)
|
||||
}
|
||||
}
|
||||
fmt.Fprintln(dockerCli.Out(), strings.Join(services, "\n"))
|
||||
fmt.Fprintln(streams.Out(), strings.Join(services, "\n"))
|
||||
return nil
|
||||
}
|
||||
|
||||
if opts.Format == "" {
|
||||
opts.Format = dockerCli.ConfigFile().PsFormat
|
||||
}
|
||||
return formatter.Print(containers, opts.Format, streams.Out(),
|
||||
writer(containers),
|
||||
"NAME", "IMAGE", "COMMAND", "SERVICE", "CREATED", "STATUS", "PORTS")
|
||||
}
|
||||
|
||||
containerCtx := cliformatter.Context{
|
||||
Output: dockerCli.Out(),
|
||||
Format: formatter.NewContainerFormat(opts.Format, opts.Quiet, false),
|
||||
Trunc: !opts.noTrunc,
|
||||
func writer(containers []api.ContainerSummary) func(w io.Writer) {
|
||||
return func(w io.Writer) {
|
||||
for _, container := range containers {
|
||||
ports := displayablePorts(container)
|
||||
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\t%s\t%s\n", container.Name, container.Image, strconv.Quote(command), container.Service, created, status, ports)
|
||||
}
|
||||
}
|
||||
return formatter.ContainerWrite(containerCtx, containers)
|
||||
}
|
||||
|
||||
func filterByStatus(containers []api.ContainerSummary, statuses []string) []api.ContainerSummary {
|
||||
@@ -177,3 +177,21 @@ func hasStatus(c api.ContainerSummary, statuses []string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func displayablePorts(c api.ContainerSummary) string {
|
||||
if c.Publishers == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
ports := make([]types.Port, len(c.Publishers))
|
||||
for i, pub := range c.Publishers {
|
||||
ports[i] = types.Port{
|
||||
IP: pub.URL,
|
||||
PrivatePort: uint16(pub.TargetPort),
|
||||
PublicPort: uint16(pub.PublishedPort),
|
||||
Type: pub.Protocol,
|
||||
}
|
||||
}
|
||||
|
||||
return formatter2.DisplayablePorts(ports)
|
||||
}
|
||||
|
||||
@@ -18,16 +18,16 @@ package compose
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/docker/cli/cli/streams"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/mocks"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
func TestPsTable(t *testing.T) {
|
||||
@@ -69,11 +69,7 @@ func TestPsTable(t *testing.T) {
|
||||
}).AnyTimes()
|
||||
|
||||
opts := psOptions{ProjectOptions: &ProjectOptions{ProjectName: "test"}}
|
||||
stdout := streams.NewOut(f)
|
||||
cli := mocks.NewMockCli(ctrl)
|
||||
cli.EXPECT().Out().Return(stdout).AnyTimes()
|
||||
cli.EXPECT().ConfigFile().Return(&configfile.ConfigFile{}).AnyTimes()
|
||||
err = runPs(ctx, cli, backend, nil, opts)
|
||||
err = runPs(ctx, stream{out: streams.NewOut(f)}, backend, nil, opts)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = f.Seek(0, 0)
|
||||
@@ -84,3 +80,21 @@ func TestPsTable(t *testing.T) {
|
||||
|
||||
assert.Contains(t, string(output), "8080/tcp, 8443/tcp")
|
||||
}
|
||||
|
||||
type stream struct {
|
||||
out *streams.Out
|
||||
err io.Writer
|
||||
in *streams.In
|
||||
}
|
||||
|
||||
func (s stream) Out() *streams.Out {
|
||||
return s.out
|
||||
}
|
||||
|
||||
func (s stream) Err() io.Writer {
|
||||
return s.err
|
||||
}
|
||||
|
||||
func (s stream) In() *streams.In {
|
||||
return s.in
|
||||
}
|
||||
|
||||
@@ -19,44 +19,31 @@ package compose
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
)
|
||||
|
||||
type publishOptions struct {
|
||||
*ProjectOptions
|
||||
resolveImageDigests bool
|
||||
ociVersion string
|
||||
}
|
||||
|
||||
func publishCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
opts := publishOptions{
|
||||
func publishCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
|
||||
opts := pushOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
cmd := &cobra.Command{
|
||||
publishCmd := &cobra.Command{
|
||||
Use: "publish [OPTIONS] [REPOSITORY]",
|
||||
Short: "Publish compose application",
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
return runPublish(ctx, dockerCli, backend, opts, args[0])
|
||||
return runPublish(ctx, backend, opts, args[0])
|
||||
}),
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVar(&opts.resolveImageDigests, "resolve-image-digests", false, "Pin image tags to digests.")
|
||||
flags.StringVar(&opts.ociVersion, "oci-version", "", "OCI Image/Artifact specification version (automatically determined by default)")
|
||||
return cmd
|
||||
return publishCmd
|
||||
}
|
||||
|
||||
func runPublish(ctx context.Context, dockerCli command.Cli, backend api.Service, opts publishOptions, repository string) error {
|
||||
project, err := opts.ToProject(dockerCli, nil)
|
||||
func runPublish(ctx context.Context, backend api.Service, opts pushOptions, repository string) error {
|
||||
project, err := opts.ToProject(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return backend.Publish(ctx, project, repository, api.PublishOptions{
|
||||
ResolveImageDigests: opts.resolveImageDigests,
|
||||
OCIVersion: api.OCIVersion(opts.ociVersion),
|
||||
})
|
||||
return backend.Publish(ctx, project, repository)
|
||||
}
|
||||
|
||||
@@ -21,8 +21,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/morikuni/aec"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
@@ -38,10 +37,9 @@ type pullOptions struct {
|
||||
includeDeps bool
|
||||
ignorePullFailures bool
|
||||
noBuildable bool
|
||||
policy string
|
||||
}
|
||||
|
||||
func pullCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func pullCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
|
||||
opts := pullOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -55,9 +53,9 @@ func pullCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
return nil
|
||||
}),
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
return runPull(ctx, dockerCli, backend, opts, args)
|
||||
return runPull(ctx, backend, opts, args)
|
||||
}),
|
||||
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||
ValidArgsFunction: completeServiceNames(p),
|
||||
}
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Pull without printing progress information.")
|
||||
@@ -68,42 +66,22 @@ func pullCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
flags.MarkHidden("no-parallel") //nolint:errcheck
|
||||
cmd.Flags().BoolVar(&opts.ignorePullFailures, "ignore-pull-failures", false, "Pull what it can and ignores images with pull failures.")
|
||||
cmd.Flags().BoolVar(&opts.noBuildable, "ignore-buildable", false, "Ignore images that can be built.")
|
||||
cmd.Flags().StringVar(&opts.policy, "policy", "", `Apply pull policy ("missing"|"always").`)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (opts pullOptions) apply(project *types.Project, services []string) (*types.Project, error) {
|
||||
func runPull(ctx context.Context, backend api.Service, opts pullOptions, services []string) error {
|
||||
project, err := opts.ToProject(services)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !opts.includeDeps {
|
||||
var err error
|
||||
project, err = project.WithSelectedServices(services, types.IgnoreDependencies)
|
||||
err := project.ForServices(services, types.IgnoreDependencies)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if opts.policy != "" {
|
||||
for i, service := range project.Services {
|
||||
if service.Image == "" {
|
||||
continue
|
||||
}
|
||||
service.PullPolicy = opts.policy
|
||||
project.Services[i] = service
|
||||
}
|
||||
}
|
||||
return project, nil
|
||||
}
|
||||
|
||||
func runPull(ctx context.Context, dockerCli command.Cli, backend api.Service, opts pullOptions, services []string) error {
|
||||
project, err := opts.ToProject(dockerCli, services)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
project, err = opts.apply(project, services)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return backend.Pull(ctx, project, api.PullOptions{
|
||||
Quiet: opts.quiet,
|
||||
IgnoreFailures: opts.ignorePullFailures,
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
Copyright 2023 Docker Compose CLI authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package compose
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestApplyPullOptions(t *testing.T) {
|
||||
project := &types.Project{
|
||||
Services: types.Services{
|
||||
"must-build": {
|
||||
Name: "must-build",
|
||||
// No image, local build only
|
||||
Build: &types.BuildConfig{
|
||||
Context: ".",
|
||||
},
|
||||
},
|
||||
"has-build": {
|
||||
Name: "has-build",
|
||||
Image: "registry.example.com/myservice",
|
||||
Build: &types.BuildConfig{
|
||||
Context: ".",
|
||||
},
|
||||
},
|
||||
"must-pull": {
|
||||
Name: "must-pull",
|
||||
Image: "registry.example.com/another-service",
|
||||
},
|
||||
},
|
||||
}
|
||||
project, err := pullOptions{
|
||||
policy: types.PullPolicyMissing,
|
||||
}.apply(project, nil)
|
||||
assert.NilError(t, err)
|
||||
|
||||
assert.Equal(t, project.Services["must-build"].PullPolicy, "") // still default
|
||||
assert.Equal(t, project.Services["has-build"].PullPolicy, types.PullPolicyMissing)
|
||||
assert.Equal(t, project.Services["must-pull"].PullPolicy, types.PullPolicyMissing)
|
||||
}
|
||||
@@ -19,8 +19,7 @@ package compose
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
@@ -34,7 +33,7 @@ type pushOptions struct {
|
||||
Quiet bool
|
||||
}
|
||||
|
||||
func pushCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func pushCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
|
||||
opts := pushOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -42,9 +41,9 @@ func pushCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
Use: "push [OPTIONS] [SERVICE...]",
|
||||
Short: "Push service images",
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
return runPush(ctx, dockerCli, backend, opts, args)
|
||||
return runPush(ctx, backend, opts, args)
|
||||
}),
|
||||
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||
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")
|
||||
@@ -53,14 +52,14 @@ func pushCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
return pushCmd
|
||||
}
|
||||
|
||||
func runPush(ctx context.Context, dockerCli command.Cli, backend api.Service, opts pushOptions, services []string) error {
|
||||
project, err := opts.ToProject(dockerCli, services)
|
||||
func runPush(ctx context.Context, backend api.Service, opts pushOptions, services []string) error {
|
||||
project, err := opts.ToProject(services)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !opts.IncludeDeps {
|
||||
project, err = project.WithSelectedServices(services, types.IgnoreDependencies)
|
||||
err := project.ForServices(services, types.IgnoreDependencies)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ package compose
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -31,7 +30,7 @@ type removeOptions struct {
|
||||
volumes bool
|
||||
}
|
||||
|
||||
func removeCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func removeCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
|
||||
opts := removeOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -45,9 +44,9 @@ can override this with -v. To list all volumes, use "docker volume ls".
|
||||
|
||||
Any data which is not in a volume will be lost.`,
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
return runRemove(ctx, dockerCli, backend, opts, args)
|
||||
return runRemove(ctx, backend, opts, args)
|
||||
}),
|
||||
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||
ValidArgsFunction: completeServiceNames(p),
|
||||
}
|
||||
f := cmd.Flags()
|
||||
f.BoolVarP(&opts.force, "force", "f", false, "Don't ask to confirm removal")
|
||||
@@ -59,8 +58,8 @@ Any data which is not in a volume will be lost.`,
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runRemove(ctx context.Context, dockerCli command.Cli, backend api.Service, opts removeOptions, services []string) error {
|
||||
project, name, err := opts.projectOrName(dockerCli, services...)
|
||||
func runRemove(ctx context.Context, backend api.Service, opts removeOptions, services []string) error {
|
||||
project, name, err := opts.projectOrName(services...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
@@ -33,7 +33,7 @@ type restartOptions struct {
|
||||
noDeps bool
|
||||
}
|
||||
|
||||
func restartCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func restartCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
|
||||
opts := restartOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -44,9 +44,9 @@ func restartCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Servic
|
||||
opts.timeChanged = cmd.Flags().Changed("timeout")
|
||||
},
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
return runRestart(ctx, dockerCli, backend, opts, args)
|
||||
return runRestart(ctx, backend, opts, args)
|
||||
}),
|
||||
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||
ValidArgsFunction: completeServiceNames(p),
|
||||
}
|
||||
flags := restartCmd.Flags()
|
||||
flags.IntVarP(&opts.timeout, "timeout", "t", 0, "Specify a shutdown timeout in seconds")
|
||||
@@ -55,29 +55,28 @@ func restartCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Servic
|
||||
return restartCmd
|
||||
}
|
||||
|
||||
func runRestart(ctx context.Context, dockerCli command.Cli, backend api.Service, opts restartOptions, services []string) error {
|
||||
project, name, err := opts.projectOrName(dockerCli)
|
||||
func runRestart(ctx context.Context, backend api.Service, opts restartOptions, services []string) error {
|
||||
project, name, err := opts.projectOrName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if project != nil && len(services) > 0 {
|
||||
project, err = project.WithServicesEnabled(services...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var timeout *time.Duration
|
||||
if opts.timeChanged {
|
||||
timeoutValue := time.Duration(opts.timeout) * time.Second
|
||||
timeout = &timeoutValue
|
||||
}
|
||||
|
||||
if opts.noDeps {
|
||||
err := project.ForServices(services, types.IgnoreDependencies)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return backend.Restart(ctx, name, api.RestartOptions{
|
||||
Timeout: timeout,
|
||||
Services: services,
|
||||
Project: project,
|
||||
NoDeps: opts.noDeps,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -21,13 +21,9 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/format"
|
||||
xprogress "github.com/moby/buildkit/util/progress/progressui"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
cgo "github.com/compose-spec/compose-go/v2/cli"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/cli/cli/command"
|
||||
cgo "github.com/compose-spec/compose-go/cli"
|
||||
"github.com/compose-spec/compose-go/loader"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/docker/cli/opts"
|
||||
"github.com/mattn/go-shellwords"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -66,56 +62,54 @@ type runOptions struct {
|
||||
quietPull bool
|
||||
}
|
||||
|
||||
func (options runOptions) apply(project *types.Project) (*types.Project, error) {
|
||||
func (options runOptions) apply(project *types.Project) error {
|
||||
if options.noDeps {
|
||||
var err error
|
||||
project, err = project.WithSelectedServices([]string{options.Service}, types.IgnoreDependencies)
|
||||
err := project.ForServices([]string{options.Service}, types.IgnoreDependencies)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
target, err := project.GetService(options.Service)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
target.Tty = !options.noTty
|
||||
target.StdinOpen = options.interactive
|
||||
|
||||
// --service-ports and --publish are incompatible
|
||||
if !options.servicePorts {
|
||||
if len(target.Ports) > 0 {
|
||||
logrus.Debug("Running service without ports exposed as --service-ports=false")
|
||||
}
|
||||
target.Ports = []types.ServicePortConfig{}
|
||||
}
|
||||
if len(options.publish) > 0 {
|
||||
target.Ports = []types.ServicePortConfig{}
|
||||
for _, p := range options.publish {
|
||||
config, err := types.ParsePortConfig(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
target.Ports = append(target.Ports, config...)
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range options.volumes {
|
||||
volume, err := format.ParseVolume(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if len(options.volumes) > 0 {
|
||||
for _, v := range options.volumes {
|
||||
volume, err := loader.ParseVolume(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
target.Volumes = append(target.Volumes, volume)
|
||||
}
|
||||
target.Volumes = append(target.Volumes, volume)
|
||||
}
|
||||
|
||||
for name := range project.Services {
|
||||
if name == options.Service {
|
||||
project.Services[name] = target
|
||||
for i, s := range project.Services {
|
||||
if s.Name == options.Service {
|
||||
project.Services[i] = target
|
||||
break
|
||||
}
|
||||
}
|
||||
return project, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func runCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func runCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *cobra.Command {
|
||||
options := runOptions{
|
||||
composeOptions: &composeOptions{
|
||||
ProjectOptions: p,
|
||||
@@ -124,9 +118,6 @@ func runCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *
|
||||
capDrop: opts.NewListOpts(nil),
|
||||
}
|
||||
createOpts := createOptions{}
|
||||
buildOpts := buildOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
cmd := &cobra.Command{
|
||||
Use: "run [OPTIONS] SERVICE [COMMAND] [ARGS...]",
|
||||
Short: "Run a one-off command on a service.",
|
||||
@@ -156,26 +147,22 @@ 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(dockerCli, []string{options.Service}, cgo.WithResolvedPaths(true), cgo.WithDiscardEnvFile)
|
||||
project, err := p.ToProject([]string{options.Service}, cgo.WithResolvedPaths(true), cgo.WithDiscardEnvFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if createOpts.quietPull {
|
||||
buildOpts.Progress = string(xprogress.QuietMode)
|
||||
}
|
||||
|
||||
options.ignoreOrphans = utils.StringToBool(project.Environment[ComposeIgnoreOrphans])
|
||||
return runRun(ctx, backend, project, options, createOpts, buildOpts, dockerCli)
|
||||
return runRun(ctx, backend, project, options, createOpts, streams)
|
||||
}),
|
||||
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||
ValidArgsFunction: completeServiceNames(p),
|
||||
}
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVarP(&options.Detach, "detach", "d", false, "Run container in background and print container ID")
|
||||
flags.StringArrayVarP(&options.environment, "env", "e", []string{}, "Set environment variables")
|
||||
flags.StringArrayVarP(&options.labels, "label", "l", []string{}, "Add or override a label")
|
||||
flags.BoolVar(&options.Remove, "rm", false, "Automatically remove the container when it exits")
|
||||
flags.BoolVarP(&options.noTty, "no-TTY", "T", !dockerCli.Out().IsTerminal(), "Disable pseudo-TTY allocation (default: auto-detected).")
|
||||
flags.BoolVarP(&options.noTty, "no-TTY", "T", !streams.Out().IsTerminal(), "Disable pseudo-TTY allocation (default: auto-detected).")
|
||||
flags.StringVar(&options.name, "name", "", "Assign a name to the container")
|
||||
flags.StringVarP(&options.user, "user", "u", "", "Run as specified username or uid")
|
||||
flags.StringVarP(&options.workdir, "workdir", "w", "", "Working directory inside the container")
|
||||
@@ -186,7 +173,7 @@ func runCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *
|
||||
flags.StringArrayVarP(&options.volumes, "volume", "v", []string{}, "Bind mount a volume.")
|
||||
flags.StringArrayVarP(&options.publish, "publish", "p", []string{}, "Publish a container's port(s) to the host.")
|
||||
flags.BoolVar(&options.useAliases, "use-aliases", false, "Use the service's network useAliases in the network(s) the container connects to.")
|
||||
flags.BoolVarP(&options.servicePorts, "service-ports", "P", false, "Run command with all service's ports enabled and mapped to the host.")
|
||||
flags.BoolVar(&options.servicePorts, "service-ports", false, "Run command with the service's ports enabled and mapped to the host.")
|
||||
flags.BoolVar(&options.quietPull, "quiet-pull", false, "Pull without printing progress information.")
|
||||
flags.BoolVar(&createOpts.Build, "build", false, "Build image before starting container.")
|
||||
flags.BoolVar(&createOpts.removeOrphans, "remove-orphans", false, "Remove containers for services not defined in the Compose file.")
|
||||
@@ -210,8 +197,8 @@ func normalizeRunFlags(f *pflag.FlagSet, name string) pflag.NormalizedName {
|
||||
return pflag.NormalizedName(name)
|
||||
}
|
||||
|
||||
func runRun(ctx context.Context, backend api.Service, project *types.Project, options runOptions, createOpts createOptions, buildOpts buildOptions, dockerCli command.Cli) error {
|
||||
project, err := options.apply(project)
|
||||
func runRun(ctx context.Context, backend api.Service, project *types.Project, options runOptions, createOpts createOptions, streams api.Streams) error {
|
||||
err := options.apply(project)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -222,17 +209,8 @@ func runRun(ctx context.Context, backend api.Service, project *types.Project, op
|
||||
}
|
||||
|
||||
err = progress.Run(ctx, func(ctx context.Context) error {
|
||||
var buildForDeps *api.BuildOptions
|
||||
if !createOpts.noBuild {
|
||||
// allow dependencies needing build to be implicitly selected
|
||||
bo, err := buildOpts.toAPIBuildOptions(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buildForDeps = &bo
|
||||
}
|
||||
return startDependencies(ctx, backend, *project, buildForDeps, options.Service, options.ignoreOrphans)
|
||||
}, dockerCli.Err())
|
||||
return startDependencies(ctx, backend, *project, options.Service, options.ignoreOrphans)
|
||||
}, streams.Err())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -246,20 +224,8 @@ func runRun(ctx context.Context, backend api.Service, project *types.Project, op
|
||||
labels[parts[0]] = parts[1]
|
||||
}
|
||||
|
||||
var buildForRun *api.BuildOptions
|
||||
if !createOpts.noBuild {
|
||||
// dependencies have already been started above, so only the service
|
||||
// being run might need to be built at this point
|
||||
bo, err := buildOpts.toAPIBuildOptions([]string{options.Service})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buildForRun = &bo
|
||||
}
|
||||
|
||||
// start container and attach to container streams
|
||||
runOpts := api.RunOptions{
|
||||
Build: buildForRun,
|
||||
Name: options.name,
|
||||
Service: options.Service,
|
||||
Command: options.Command,
|
||||
@@ -280,10 +246,10 @@ func runRun(ctx context.Context, backend api.Service, project *types.Project, op
|
||||
QuietPull: options.quietPull,
|
||||
}
|
||||
|
||||
for name, service := range project.Services {
|
||||
if name == options.Service {
|
||||
for i, service := range project.Services {
|
||||
if service.Name == options.Service {
|
||||
service.StdinOpen = options.interactive
|
||||
project.Services[name] = service
|
||||
project.Services[i] = service
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,21 +264,20 @@ func runRun(ctx context.Context, backend api.Service, project *types.Project, op
|
||||
return err
|
||||
}
|
||||
|
||||
func startDependencies(ctx context.Context, backend api.Service, project types.Project, buildOpts *api.BuildOptions, requestedServiceName string, ignoreOrphans bool) error {
|
||||
func startDependencies(ctx context.Context, backend api.Service, project types.Project, requestedServiceName string, ignoreOrphans bool) error {
|
||||
dependencies := types.Services{}
|
||||
var requestedService types.ServiceConfig
|
||||
for name, service := range project.Services {
|
||||
if name != requestedServiceName {
|
||||
dependencies[name] = service
|
||||
for _, service := range project.Services {
|
||||
if service.Name != requestedServiceName {
|
||||
dependencies = append(dependencies, service)
|
||||
} else {
|
||||
requestedService = service
|
||||
}
|
||||
}
|
||||
|
||||
project.Services = dependencies
|
||||
project.DisabledServices[requestedServiceName] = requestedService
|
||||
project.DisabledServices = append(project.DisabledServices, requestedService)
|
||||
err := backend.Create(ctx, &project, api.CreateOptions{
|
||||
Build: buildOpts,
|
||||
IgnoreOrphans: ignoreOrphans,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
Copyright 2020 Docker Compose CLI authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package compose
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"golang.org/x/exp/maps"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type scaleOptions struct {
|
||||
*ProjectOptions
|
||||
noDeps bool
|
||||
}
|
||||
|
||||
func scaleCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
opts := scaleOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
scaleCmd := &cobra.Command{
|
||||
Use: "scale [SERVICE=REPLICAS...]",
|
||||
Short: "Scale services ",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
serviceTuples, err := parseServicesReplicasArgs(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return runScale(ctx, dockerCli, backend, opts, serviceTuples)
|
||||
}),
|
||||
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||
}
|
||||
flags := scaleCmd.Flags()
|
||||
flags.BoolVar(&opts.noDeps, "no-deps", false, "Don't start linked services.")
|
||||
|
||||
return scaleCmd
|
||||
}
|
||||
|
||||
func runScale(ctx context.Context, dockerCli command.Cli, backend api.Service, opts scaleOptions, serviceReplicaTuples map[string]int) error {
|
||||
services := maps.Keys(serviceReplicaTuples)
|
||||
project, err := opts.ToProject(dockerCli, services)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if opts.noDeps {
|
||||
if project, err = project.WithSelectedServices(services, types.IgnoreDependencies); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for key, value := range serviceReplicaTuples {
|
||||
service, err := project.GetService(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
service.SetScale(value)
|
||||
project.Services[key] = service
|
||||
}
|
||||
|
||||
return backend.Scale(ctx, project, api.ScaleOptions{Services: services})
|
||||
}
|
||||
|
||||
func parseServicesReplicasArgs(args []string) (map[string]int, error) {
|
||||
serviceReplicaTuples := map[string]int{}
|
||||
for _, arg := range args {
|
||||
key, val, ok := strings.Cut(arg, "=")
|
||||
if !ok || key == "" || val == "" {
|
||||
return nil, fmt.Errorf("invalid scale specifier: %s", arg)
|
||||
}
|
||||
intValue, err := strconv.Atoi(val)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid scale specifier: can't parse replica value as int: %v", arg)
|
||||
}
|
||||
serviceReplicaTuples[key] = intValue
|
||||
}
|
||||
return serviceReplicaTuples, nil
|
||||
}
|
||||
@@ -19,7 +19,6 @@ package compose
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -28,7 +27,7 @@ type startOptions struct {
|
||||
*ProjectOptions
|
||||
}
|
||||
|
||||
func startCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func startCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
|
||||
opts := startOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -36,15 +35,15 @@ func startCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
Use: "start [SERVICE...]",
|
||||
Short: "Start services",
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
return runStart(ctx, dockerCli, backend, opts, args)
|
||||
return runStart(ctx, backend, opts, args)
|
||||
}),
|
||||
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||
ValidArgsFunction: completeServiceNames(p),
|
||||
}
|
||||
return startCmd
|
||||
}
|
||||
|
||||
func runStart(ctx context.Context, dockerCli command.Cli, backend api.Service, opts startOptions, services []string) error {
|
||||
project, name, err := opts.projectOrName(dockerCli, services...)
|
||||
func runStart(ctx context.Context, backend api.Service, opts startOptions, services []string) error {
|
||||
project, name, err := opts.projectOrName(services...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
Copyright 2020 Docker Compose CLI authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package compose
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
)
|
||||
|
||||
type statsOptions struct {
|
||||
ProjectOptions *ProjectOptions
|
||||
all bool
|
||||
format string
|
||||
noStream bool
|
||||
noTrunc bool
|
||||
}
|
||||
|
||||
func statsCommand(p *ProjectOptions, dockerCli command.Cli) *cobra.Command {
|
||||
opts := statsOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
cmd := &cobra.Command{
|
||||
Use: "stats [OPTIONS] [SERVICE]",
|
||||
Short: "Display a live stream of container(s) resource usage statistics",
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
return runStats(ctx, dockerCli, opts, args)
|
||||
}),
|
||||
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||
}
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVarP(&opts.all, "all", "a", false, "Show all containers (default shows just running)")
|
||||
flags.StringVar(&opts.format, "format", "", `Format output using a custom template:
|
||||
'table': Print output in table format with column headers (default)
|
||||
'table TEMPLATE': Print output in table format using the given Go template
|
||||
'json': Print in JSON format
|
||||
'TEMPLATE': Print output using the given Go template.
|
||||
Refer to https://docs.docker.com/go/formatting/ for more information about formatting output with templates`)
|
||||
flags.BoolVar(&opts.noStream, "no-stream", false, "Disable streaming stats and only pull the first result")
|
||||
flags.BoolVar(&opts.noTrunc, "no-trunc", false, "Do not truncate output")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runStats(ctx context.Context, dockerCli command.Cli, opts statsOptions, service []string) error {
|
||||
name, err := opts.ProjectOptions.toProjectName(dockerCli)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
filter := []filters.KeyValuePair{
|
||||
filters.Arg("label", fmt.Sprintf("%s=%s", api.ProjectLabel, name)),
|
||||
}
|
||||
if len(service) > 0 {
|
||||
filter = append(filter, filters.Arg("label", fmt.Sprintf("%s=%s", api.ServiceLabel, service[0])))
|
||||
}
|
||||
args := filters.NewArgs(filter...)
|
||||
return container.RunStats(ctx, dockerCli, &container.StatsOptions{
|
||||
All: opts.all,
|
||||
NoStream: opts.noStream,
|
||||
NoTrunc: opts.noTrunc,
|
||||
Format: opts.format,
|
||||
Filters: &args,
|
||||
})
|
||||
}
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
@@ -32,7 +31,7 @@ type stopOptions struct {
|
||||
timeout int
|
||||
}
|
||||
|
||||
func stopCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func stopCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
|
||||
opts := stopOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -43,9 +42,9 @@ func stopCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
opts.timeChanged = cmd.Flags().Changed("timeout")
|
||||
},
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
return runStop(ctx, dockerCli, backend, opts, args)
|
||||
return runStop(ctx, backend, opts, args)
|
||||
}),
|
||||
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||
ValidArgsFunction: completeServiceNames(p),
|
||||
}
|
||||
flags := cmd.Flags()
|
||||
flags.IntVarP(&opts.timeout, "timeout", "t", 0, "Specify a shutdown timeout in seconds")
|
||||
@@ -53,8 +52,8 @@ func stopCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runStop(ctx context.Context, dockerCli command.Cli, backend api.Service, opts stopOptions, services []string) error {
|
||||
project, name, err := opts.projectOrName(dockerCli, services...)
|
||||
func runStop(ctx context.Context, backend api.Service, opts stopOptions, services []string) error {
|
||||
project, name, err := opts.projectOrName(services...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import (
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
@@ -34,7 +33,7 @@ type topOptions struct {
|
||||
*ProjectOptions
|
||||
}
|
||||
|
||||
func topCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func topCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *cobra.Command {
|
||||
opts := topOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -42,15 +41,15 @@ func topCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *
|
||||
Use: "top [SERVICES...]",
|
||||
Short: "Display the running processes",
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
return runTop(ctx, dockerCli, backend, opts, args)
|
||||
return runTop(ctx, streams, backend, opts, args)
|
||||
}),
|
||||
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||
ValidArgsFunction: completeServiceNames(p),
|
||||
}
|
||||
return topCmd
|
||||
}
|
||||
|
||||
func runTop(ctx context.Context, dockerCli command.Cli, backend api.Service, opts topOptions, services []string) error {
|
||||
projectName, err := opts.toProjectName(dockerCli)
|
||||
func runTop(ctx context.Context, streams api.Streams, backend api.Service, opts topOptions, services []string) error {
|
||||
projectName, err := opts.toProjectName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -64,8 +63,8 @@ func runTop(ctx context.Context, dockerCli command.Cli, backend api.Service, opt
|
||||
})
|
||||
|
||||
for _, container := range containers {
|
||||
fmt.Fprintf(dockerCli.Out(), "%s\n", container.Name)
|
||||
err := psPrinter(dockerCli.Out(), func(w io.Writer) {
|
||||
fmt.Fprintf(streams.Out(), "%s\n", container.Name)
|
||||
err := psPrinter(streams.Out(), func(w io.Writer) {
|
||||
for _, proc := range container.Processes {
|
||||
info := []interface{}{}
|
||||
for _, p := range proc {
|
||||
|
||||
@@ -18,15 +18,10 @@ package compose
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
xprogress "github.com/moby/buildkit/util/progress/progressui"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/docker/compose/v2/cmd/formatter"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
@@ -56,29 +51,27 @@ type upOptions struct {
|
||||
waitTimeout int
|
||||
}
|
||||
|
||||
func (opts upOptions) apply(project *types.Project, services []string) (*types.Project, error) {
|
||||
func (opts upOptions) apply(project *types.Project, services []string) error {
|
||||
if opts.noDeps {
|
||||
var err error
|
||||
project, err = project.WithSelectedServices(services, types.IgnoreDependencies)
|
||||
err := project.ForServices(services, types.IgnoreDependencies)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if opts.exitCodeFrom != "" {
|
||||
_, err := project.GetService(opts.exitCodeFrom)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return project, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func upCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func upCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *cobra.Command {
|
||||
up := upOptions{}
|
||||
create := createOptions{}
|
||||
build := buildOptions{ProjectOptions: p}
|
||||
upCmd := &cobra.Command{
|
||||
Use: "up [OPTIONS] [SERVICE...]",
|
||||
Short: "Create and start containers",
|
||||
@@ -87,23 +80,20 @@ func upCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *c
|
||||
create.timeChanged = cmd.Flags().Changed("timeout")
|
||||
return validateFlags(&up, &create)
|
||||
}),
|
||||
RunE: p.WithServices(dockerCli, func(ctx context.Context, project *types.Project, services []string) error {
|
||||
RunE: p.WithServices(func(ctx context.Context, project *types.Project, services []string) error {
|
||||
create.ignoreOrphans = utils.StringToBool(project.Environment[ComposeIgnoreOrphans])
|
||||
if create.ignoreOrphans && create.removeOrphans {
|
||||
return fmt.Errorf("cannot combine %s and --remove-orphans", ComposeIgnoreOrphans)
|
||||
return fmt.Errorf("%s and --remove-orphans cannot be combined", ComposeIgnoreOrphans)
|
||||
}
|
||||
if len(up.attach) != 0 && up.attachDependencies {
|
||||
return errors.New("cannot combine --attach and --attach-dependencies")
|
||||
}
|
||||
return runUp(ctx, dockerCli, backend, create, up, build, project, services)
|
||||
return runUp(ctx, streams, backend, create, up, project, services)
|
||||
}),
|
||||
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||
ValidArgsFunction: completeServiceNames(p),
|
||||
}
|
||||
flags := upCmd.Flags()
|
||||
flags.BoolVarP(&up.Detach, "detach", "d", false, "Detached mode: Run containers in the background")
|
||||
flags.BoolVar(&create.Build, "build", false, "Build images before starting containers.")
|
||||
flags.BoolVar(&create.noBuild, "no-build", false, "Don't build an image, even if it's policy.")
|
||||
flags.StringVar(&create.Pull, "pull", "policy", `Pull image before running ("always"|"missing"|"never")`)
|
||||
flags.BoolVar(&create.noBuild, "no-build", false, "Don't build an image, even if it's missing.")
|
||||
flags.StringVar(&create.Pull, "pull", "missing", `Pull image before running ("always"|"missing"|"never")`)
|
||||
flags.BoolVar(&create.removeOrphans, "remove-orphans", false, "Remove containers for services not defined in the Compose file.")
|
||||
flags.StringArrayVar(&create.scale, "scale", []string{}, "Scale SERVICE to NUM instances. Overrides the `scale` setting in the Compose file if present.")
|
||||
flags.BoolVar(&up.noColor, "no-color", false, "Produce monochrome output.")
|
||||
@@ -118,12 +108,12 @@ func upCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *c
|
||||
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.")
|
||||
flags.BoolVar(&up.attachDependencies, "attach-dependencies", false, "Attach to dependent containers.")
|
||||
flags.BoolVar(&create.quietPull, "quiet-pull", false, "Pull without printing progress information.")
|
||||
flags.StringArrayVar(&up.attach, "attach", []string{}, "Restrict attaching to the specified services. Incompatible with --attach-dependencies.")
|
||||
flags.StringArrayVar(&up.noAttach, "no-attach", []string{}, "Do not attach (stream logs) to the specified services.")
|
||||
flags.BoolVar(&up.attachDependencies, "attach-dependencies", false, "Automatically attach to log output of dependent services.")
|
||||
flags.StringArrayVar(&up.attach, "attach", []string{}, "Attach to service output.")
|
||||
flags.StringArrayVar(&up.noAttach, "no-attach", []string{}, "Don't attach to specified service.")
|
||||
flags.BoolVar(&up.wait, "wait", false, "Wait for services to be running|healthy. Implies detached mode.")
|
||||
flags.IntVar(&up.waitTimeout, "wait-timeout", 0, "Maximum duration to wait for the project to be running|healthy.")
|
||||
flags.IntVar(&up.waitTimeout, "wait-timeout", 0, "timeout waiting for application to be running|healthy.")
|
||||
|
||||
return upCmd
|
||||
}
|
||||
@@ -153,16 +143,7 @@ func validateFlags(up *upOptions, create *createOptions) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func runUp(
|
||||
ctx context.Context,
|
||||
dockerCli command.Cli,
|
||||
backend api.Service,
|
||||
createOptions createOptions,
|
||||
upOptions upOptions,
|
||||
buildOptions buildOptions,
|
||||
project *types.Project,
|
||||
services []string,
|
||||
) error {
|
||||
func runUp(ctx context.Context, streams api.Streams, backend api.Service, createOptions createOptions, upOptions upOptions, project *types.Project, services []string) error {
|
||||
if len(project.Services) == 0 {
|
||||
return fmt.Errorf("no service selected")
|
||||
}
|
||||
@@ -172,29 +153,43 @@ func runUp(
|
||||
return err
|
||||
}
|
||||
|
||||
project, err = upOptions.apply(project, services)
|
||||
err = upOptions.apply(project, services)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var build *api.BuildOptions
|
||||
if !createOptions.noBuild {
|
||||
if createOptions.quietPull {
|
||||
buildOptions.Progress = string(xprogress.QuietMode)
|
||||
}
|
||||
// BuildOptions here is nested inside CreateOptions, so
|
||||
// no service list is passed, it will implicitly pick all
|
||||
// services being created, which includes any explicitly
|
||||
// specified via "services" arg here as well as deps
|
||||
bo, err := buildOptions.toAPIBuildOptions(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
build = &bo
|
||||
var consumer api.LogConsumer
|
||||
if !upOptions.Detach {
|
||||
consumer = formatter.NewLogConsumer(ctx, streams.Out(), streams.Err(), !upOptions.noColor, !upOptions.noPrefix, upOptions.timestamp)
|
||||
}
|
||||
|
||||
attachTo := utils.Set[string]{}
|
||||
if len(upOptions.attach) > 0 {
|
||||
attachTo.AddAll(upOptions.attach...)
|
||||
}
|
||||
if upOptions.attachDependencies {
|
||||
if err := project.WithServices(attachTo.Elements(), func(s types.ServiceConfig) error {
|
||||
if s.Attach == nil || *s.Attach {
|
||||
attachTo.Add(s.Name)
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(attachTo) == 0 {
|
||||
if err := project.WithServices(services, func(s types.ServiceConfig) error {
|
||||
if s.Attach == nil || *s.Attach {
|
||||
attachTo.Add(s.Name)
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
attachTo.RemoveAll(upOptions.noAttach...)
|
||||
|
||||
create := api.CreateOptions{
|
||||
Build: build,
|
||||
Services: services,
|
||||
RemoveOrphans: createOptions.removeOrphans,
|
||||
IgnoreOrphans: createOptions.ignoreOrphans,
|
||||
@@ -209,48 +204,14 @@ func runUp(
|
||||
return backend.Create(ctx, project, create)
|
||||
}
|
||||
|
||||
var consumer api.LogConsumer
|
||||
var attach []string
|
||||
if !upOptions.Detach {
|
||||
consumer = formatter.NewLogConsumer(ctx, dockerCli.Out(), dockerCli.Err(), !upOptions.noColor, !upOptions.noPrefix, upOptions.timestamp)
|
||||
|
||||
var attachSet utils.Set[string]
|
||||
if len(upOptions.attach) != 0 {
|
||||
// services are passed explicitly with --attach, verify they're valid and then use them as-is
|
||||
attachSet = utils.NewSet(upOptions.attach...)
|
||||
unexpectedSvcs := attachSet.Diff(utils.NewSet(project.ServiceNames()...))
|
||||
if len(unexpectedSvcs) != 0 {
|
||||
return fmt.Errorf("cannot attach to services not included in up: %s", strings.Join(unexpectedSvcs.Elements(), ", "))
|
||||
}
|
||||
} else {
|
||||
// mark services being launched (and potentially their deps) for attach
|
||||
// if they didn't opt-out via Compose YAML
|
||||
attachSet = utils.NewSet[string]()
|
||||
var dependencyOpt types.DependencyOption = types.IgnoreDependencies
|
||||
if upOptions.attachDependencies {
|
||||
dependencyOpt = types.IncludeDependencies
|
||||
}
|
||||
if err := project.ForEachService(services, func(serviceName string, s *types.ServiceConfig) error {
|
||||
if s.Attach == nil || *s.Attach {
|
||||
attachSet.Add(serviceName)
|
||||
}
|
||||
return nil
|
||||
}, dependencyOpt); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// filter out any services that have been explicitly marked for ignore with `--no-attach`
|
||||
attachSet.RemoveAll(upOptions.noAttach...)
|
||||
attach = attachSet.Elements()
|
||||
}
|
||||
|
||||
timeout := time.Duration(upOptions.waitTimeout) * time.Second
|
||||
|
||||
return backend.Up(ctx, project, api.UpOptions{
|
||||
Create: create,
|
||||
Start: api.StartOptions{
|
||||
Project: project,
|
||||
Attach: consumer,
|
||||
AttachTo: attach,
|
||||
AttachTo: attachTo.Elements(),
|
||||
ExitCodeFrom: upOptions.exitCodeFrom,
|
||||
CascadeStop: upOptions.cascadeStop,
|
||||
Wait: upOptions.wait,
|
||||
@@ -260,12 +221,22 @@ func runUp(
|
||||
})
|
||||
}
|
||||
|
||||
func setServiceScale(project *types.Project, name string, replicas int) error {
|
||||
service, err := project.GetService(name)
|
||||
if err != nil {
|
||||
return err
|
||||
func setServiceScale(project *types.Project, name string, replicas uint64) error {
|
||||
for i, s := range project.Services {
|
||||
if s.Name != name {
|
||||
continue
|
||||
}
|
||||
|
||||
service, err := project.GetService(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if service.Deploy == nil {
|
||||
service.Deploy = &types.DeployConfig{}
|
||||
}
|
||||
service.Deploy.Replicas = &replicas
|
||||
project.Services[i] = service
|
||||
return nil
|
||||
}
|
||||
service.SetScale(replicas)
|
||||
project.Services[name] = service
|
||||
return nil
|
||||
return fmt.Errorf("unknown service %q", name)
|
||||
}
|
||||
|
||||
@@ -19,33 +19,25 @@ package compose
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestApplyScaleOpt(t *testing.T) {
|
||||
p := types.Project{
|
||||
Services: types.Services{
|
||||
"foo": {
|
||||
Services: []types.ServiceConfig{
|
||||
{
|
||||
Name: "foo",
|
||||
},
|
||||
"bar": {
|
||||
{
|
||||
Name: "bar",
|
||||
Deploy: &types.DeployConfig{
|
||||
Mode: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
err := applyScaleOpts(&p, []string{"foo=2", "bar=3"})
|
||||
opt := createOptions{scale: []string{"foo=2"}}
|
||||
err := opt.Apply(&p)
|
||||
assert.NilError(t, err)
|
||||
foo, err := p.GetService("foo")
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, *foo.Scale, 2)
|
||||
|
||||
bar, err := p.GetService("bar")
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, *bar.Scale, 3)
|
||||
assert.Equal(t, *bar.Deploy.Replicas, 3)
|
||||
|
||||
assert.Equal(t, *foo.Deploy.Replicas, uint64(2))
|
||||
}
|
||||
|
||||
@@ -33,14 +33,14 @@ type versionOptions struct {
|
||||
short bool
|
||||
}
|
||||
|
||||
func versionCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func versionCommand(streams command.Cli) *cobra.Command {
|
||||
opts := versionOptions{}
|
||||
cmd := &cobra.Command{
|
||||
Use: "version [OPTIONS]",
|
||||
Short: "Show the Docker Compose version information",
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
runVersion(opts, dockerCli)
|
||||
runVersion(opts, streams)
|
||||
return nil
|
||||
},
|
||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
@@ -57,14 +57,14 @@ func versionCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runVersion(opts versionOptions, dockerCli command.Cli) {
|
||||
func runVersion(opts versionOptions, streams command.Cli) {
|
||||
if opts.short {
|
||||
fmt.Fprintln(dockerCli.Out(), strings.TrimPrefix(internal.Version, "v"))
|
||||
fmt.Fprintln(streams.Out(), strings.TrimPrefix(internal.Version, "v"))
|
||||
return
|
||||
}
|
||||
if opts.format == formatter.JSON {
|
||||
fmt.Fprintf(dockerCli.Out(), "{\"version\":%q}\n", internal.Version)
|
||||
fmt.Fprintf(streams.Out(), "{\"version\":%q}\n", internal.Version)
|
||||
return
|
||||
}
|
||||
fmt.Fprintln(dockerCli.Out(), "Docker Compose version", internal.Version)
|
||||
fmt.Fprintln(streams.Out(), "Docker Compose version", internal.Version)
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -35,7 +34,7 @@ type vizOptions struct {
|
||||
indentationStr string
|
||||
}
|
||||
|
||||
func vizCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func vizCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
|
||||
opts := vizOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -51,7 +50,7 @@ func vizCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *
|
||||
return err
|
||||
}),
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
return runViz(ctx, dockerCli, backend, &opts)
|
||||
return runViz(ctx, backend, &opts)
|
||||
}),
|
||||
}
|
||||
|
||||
@@ -63,9 +62,9 @@ func vizCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runViz(ctx context.Context, dockerCli command.Cli, backend api.Service, opts *vizOptions) error {
|
||||
func runViz(ctx context.Context, backend api.Service, opts *vizOptions) error {
|
||||
_, _ = fmt.Fprintln(os.Stderr, "viz command is EXPERIMENTAL")
|
||||
project, err := opts.ToProject(dockerCli, nil)
|
||||
project, err := opts.ToProject(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -34,7 +33,7 @@ type waitOptions struct {
|
||||
downProject bool
|
||||
}
|
||||
|
||||
func waitCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func waitCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
|
||||
opts := waitOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -47,7 +46,7 @@ func waitCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: Adapt(func(ctx context.Context, services []string) error {
|
||||
opts.services = services
|
||||
statusCode, err = runWait(ctx, dockerCli, backend, &opts)
|
||||
statusCode, err = runWait(ctx, backend, &opts)
|
||||
return err
|
||||
}),
|
||||
PostRun: func(cmd *cobra.Command, args []string) {
|
||||
@@ -60,8 +59,8 @@ func waitCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runWait(ctx context.Context, dockerCli command.Cli, backend api.Service, opts *waitOptions) (int64, error) {
|
||||
_, name, err := opts.projectOrName(dockerCli)
|
||||
func runWait(ctx context.Context, backend api.Service, opts *waitOptions) (int64, error) {
|
||||
_, name, err := opts.projectOrName()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@@ -19,102 +19,43 @@ package compose
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/compose/v2/internal/locker"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type watchOptions struct {
|
||||
*ProjectOptions
|
||||
quiet bool
|
||||
noUp bool
|
||||
}
|
||||
|
||||
func watchCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
watchOpts := watchOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
buildOpts := buildOptions{
|
||||
func watchCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
|
||||
opts := watchOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
cmd := &cobra.Command{
|
||||
Use: "watch [SERVICE...]",
|
||||
Short: "Watch build context for service and rebuild/refresh containers when files are updated",
|
||||
Short: "EXPERIMENTAL - Watch build context for service and rebuild/refresh containers when files are updated",
|
||||
PreRunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
return nil
|
||||
}),
|
||||
RunE: AdaptCmd(func(ctx context.Context, cmd *cobra.Command, args []string) error {
|
||||
if cmd.Parent().Name() == "alpha" {
|
||||
logrus.Warn("watch command is now available as a top level command")
|
||||
}
|
||||
return runWatch(ctx, dockerCli, backend, watchOpts, buildOpts, args)
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
return runWatch(ctx, backend, opts, args)
|
||||
}),
|
||||
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||
ValidArgsFunction: completeServiceNames(p),
|
||||
}
|
||||
|
||||
cmd.Flags().BoolVar(&watchOpts.quiet, "quiet", false, "hide build output")
|
||||
cmd.Flags().BoolVar(&watchOpts.noUp, "no-up", false, "Do not build & start services before watching")
|
||||
cmd.Flags().BoolVar(&opts.quiet, "quiet", false, "hide build output")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runWatch(ctx context.Context, dockerCli command.Cli, backend api.Service, watchOpts watchOptions, buildOpts buildOptions, services []string) error {
|
||||
project, err := watchOpts.ToProject(dockerCli, nil)
|
||||
func runWatch(ctx context.Context, backend api.Service, opts watchOptions, services []string) error {
|
||||
fmt.Fprintln(os.Stderr, "watch command is EXPERIMENTAL")
|
||||
project, err := opts.ToProject(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := applyPlatforms(project, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
build, err := buildOpts.toAPIBuildOptions(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// validation done -- ensure we have the lockfile for this project before doing work
|
||||
l, err := locker.NewPidfile(project.Name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot take exclusive lock for project %q: %w", project.Name, err)
|
||||
}
|
||||
if err := l.Lock(); err != nil {
|
||||
return fmt.Errorf("cannot take exclusive lock for project %q: %w", project.Name, err)
|
||||
}
|
||||
|
||||
if !watchOpts.noUp {
|
||||
for index, service := range project.Services {
|
||||
if service.Build != nil && service.Develop != nil {
|
||||
service.PullPolicy = types.PullPolicyBuild
|
||||
}
|
||||
project.Services[index] = service
|
||||
}
|
||||
upOpts := api.UpOptions{
|
||||
Create: api.CreateOptions{
|
||||
Build: &build,
|
||||
Services: services,
|
||||
RemoveOrphans: false,
|
||||
Recreate: api.RecreateDiverged,
|
||||
RecreateDependencies: api.RecreateNever,
|
||||
Inherit: true,
|
||||
QuietPull: watchOpts.quiet,
|
||||
},
|
||||
Start: api.StartOptions{
|
||||
Project: project,
|
||||
Attach: nil,
|
||||
CascadeStop: false,
|
||||
Services: services,
|
||||
},
|
||||
}
|
||||
if err := backend.Up(ctx, project, upOpts); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return backend.Watch(ctx, project, services, api.WatchOptions{
|
||||
Build: build,
|
||||
})
|
||||
return backend.Watch(ctx, project, services, api.WatchOptions{})
|
||||
}
|
||||
|
||||
@@ -1,287 +0,0 @@
|
||||
/*
|
||||
Copyright 2020 Docker Compose CLI authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package formatter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/cli/cli/command/formatter"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/go-units"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultContainerTableFormat = "table {{.Name}}\t{{.Image}}\t{{.Command}}\t{{.Service}}\t{{.RunningFor}}\t{{.Status}}\t{{.Ports}}"
|
||||
|
||||
nameHeader = "NAME"
|
||||
projectHeader = "PROJECT"
|
||||
serviceHeader = "SERVICE"
|
||||
commandHeader = "COMMAND"
|
||||
runningForHeader = "CREATED"
|
||||
mountsHeader = "MOUNTS"
|
||||
localVolumes = "LOCAL VOLUMES"
|
||||
networksHeader = "NETWORKS"
|
||||
)
|
||||
|
||||
// NewContainerFormat returns a Format for rendering using a Context
|
||||
func NewContainerFormat(source string, quiet bool, size bool) formatter.Format {
|
||||
switch source {
|
||||
case formatter.TableFormatKey, "": // table formatting is the default if none is set.
|
||||
if quiet {
|
||||
return formatter.DefaultQuietFormat
|
||||
}
|
||||
format := defaultContainerTableFormat
|
||||
if size {
|
||||
format += `\t{{.Size}}`
|
||||
}
|
||||
return formatter.Format(format)
|
||||
case formatter.RawFormatKey:
|
||||
if quiet {
|
||||
return `container_id: {{.ID}}`
|
||||
}
|
||||
format := `container_id: {{.ID}}
|
||||
image: {{.Image}}
|
||||
command: {{.Command}}
|
||||
created_at: {{.CreatedAt}}
|
||||
state: {{- pad .State 1 0}}
|
||||
status: {{- pad .Status 1 0}}
|
||||
names: {{.Names}}
|
||||
labels: {{- pad .Labels 1 0}}
|
||||
ports: {{- pad .Ports 1 0}}
|
||||
`
|
||||
if size {
|
||||
format += `size: {{.Size}}\n`
|
||||
}
|
||||
return formatter.Format(format)
|
||||
default: // custom format
|
||||
if quiet {
|
||||
return formatter.DefaultQuietFormat
|
||||
}
|
||||
return formatter.Format(source)
|
||||
}
|
||||
}
|
||||
|
||||
// ContainerWrite renders the context for a list of containers
|
||||
func ContainerWrite(ctx formatter.Context, containers []api.ContainerSummary) error {
|
||||
render := func(format func(subContext formatter.SubContext) error) error {
|
||||
for _, container := range containers {
|
||||
err := format(&ContainerContext{trunc: ctx.Trunc, c: container})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return ctx.Write(NewContainerContext(), render)
|
||||
}
|
||||
|
||||
// ContainerContext is a struct used for rendering a list of containers in a Go template.
|
||||
type ContainerContext struct {
|
||||
formatter.HeaderContext
|
||||
trunc bool
|
||||
c api.ContainerSummary
|
||||
|
||||
// FieldsUsed is used in the pre-processing step to detect which fields are
|
||||
// used in the template. It's currently only used to detect use of the .Size
|
||||
// field which (if used) automatically sets the '--size' option when making
|
||||
// the API call.
|
||||
FieldsUsed map[string]interface{}
|
||||
}
|
||||
|
||||
// NewContainerContext creates a new context for rendering containers
|
||||
func NewContainerContext() *ContainerContext {
|
||||
containerCtx := ContainerContext{}
|
||||
containerCtx.Header = formatter.SubHeaderContext{
|
||||
"ID": formatter.ContainerIDHeader,
|
||||
"Name": nameHeader,
|
||||
"Project": projectHeader,
|
||||
"Service": serviceHeader,
|
||||
"Image": formatter.ImageHeader,
|
||||
"Command": commandHeader,
|
||||
"CreatedAt": formatter.CreatedAtHeader,
|
||||
"RunningFor": runningForHeader,
|
||||
"Ports": formatter.PortsHeader,
|
||||
"State": formatter.StateHeader,
|
||||
"Status": formatter.StatusHeader,
|
||||
"Size": formatter.SizeHeader,
|
||||
"Labels": formatter.LabelsHeader,
|
||||
}
|
||||
return &containerCtx
|
||||
}
|
||||
|
||||
// MarshalJSON makes ContainerContext implement json.Marshaler
|
||||
func (c *ContainerContext) MarshalJSON() ([]byte, error) {
|
||||
return formatter.MarshalJSON(c)
|
||||
}
|
||||
|
||||
// ID returns the container's ID as a string. Depending on the `--no-trunc`
|
||||
// option being set, the full or truncated ID is returned.
|
||||
func (c *ContainerContext) ID() string {
|
||||
if c.trunc {
|
||||
return stringid.TruncateID(c.c.ID)
|
||||
}
|
||||
return c.c.ID
|
||||
}
|
||||
|
||||
func (c *ContainerContext) Name() string {
|
||||
return c.c.Name
|
||||
}
|
||||
|
||||
// Names returns a comma-separated string of the container's names, with their
|
||||
// slash (/) prefix stripped. Additional names for the container (related to the
|
||||
// legacy `--link` feature) are omitted.
|
||||
func (c *ContainerContext) Names() string {
|
||||
names := formatter.StripNamePrefix(c.c.Names)
|
||||
if c.trunc {
|
||||
for _, name := range names {
|
||||
if len(strings.Split(name, "/")) == 1 {
|
||||
names = []string{name}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return strings.Join(names, ",")
|
||||
}
|
||||
|
||||
func (c *ContainerContext) Service() string {
|
||||
return c.c.Service
|
||||
}
|
||||
|
||||
func (c *ContainerContext) Project() string {
|
||||
return c.c.Project
|
||||
}
|
||||
|
||||
func (c *ContainerContext) Image() string {
|
||||
return c.c.Image
|
||||
}
|
||||
|
||||
func (c *ContainerContext) Command() string {
|
||||
command := c.c.Command
|
||||
if c.trunc {
|
||||
command = formatter.Ellipsis(command, 20)
|
||||
}
|
||||
return strconv.Quote(command)
|
||||
}
|
||||
|
||||
func (c *ContainerContext) CreatedAt() string {
|
||||
return time.Unix(c.c.Created, 0).String()
|
||||
}
|
||||
|
||||
func (c *ContainerContext) RunningFor() string {
|
||||
createdAt := time.Unix(c.c.Created, 0)
|
||||
return units.HumanDuration(time.Now().UTC().Sub(createdAt)) + " ago"
|
||||
}
|
||||
|
||||
func (c *ContainerContext) ExitCode() int {
|
||||
return c.c.ExitCode
|
||||
}
|
||||
|
||||
func (c *ContainerContext) State() string {
|
||||
return c.c.State
|
||||
}
|
||||
|
||||
func (c *ContainerContext) Status() string {
|
||||
return c.c.Status
|
||||
}
|
||||
|
||||
func (c *ContainerContext) Health() string {
|
||||
return c.c.Health
|
||||
}
|
||||
|
||||
func (c *ContainerContext) Publishers() api.PortPublishers {
|
||||
return c.c.Publishers
|
||||
}
|
||||
|
||||
func (c *ContainerContext) Ports() string {
|
||||
var ports []types.Port
|
||||
for _, publisher := range c.c.Publishers {
|
||||
ports = append(ports, types.Port{
|
||||
IP: publisher.URL,
|
||||
PrivatePort: uint16(publisher.TargetPort),
|
||||
PublicPort: uint16(publisher.PublishedPort),
|
||||
Type: publisher.Protocol,
|
||||
})
|
||||
}
|
||||
return formatter.DisplayablePorts(ports)
|
||||
}
|
||||
|
||||
// Labels returns a comma-separated string of labels present on the container.
|
||||
func (c *ContainerContext) Labels() string {
|
||||
if c.c.Labels == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
var joinLabels []string
|
||||
for k, v := range c.c.Labels {
|
||||
joinLabels = append(joinLabels, fmt.Sprintf("%s=%s", k, v))
|
||||
}
|
||||
return strings.Join(joinLabels, ",")
|
||||
}
|
||||
|
||||
// Label returns the value of the label with the given name or an empty string
|
||||
// if the given label does not exist.
|
||||
func (c *ContainerContext) Label(name string) string {
|
||||
if c.c.Labels == nil {
|
||||
return ""
|
||||
}
|
||||
return c.c.Labels[name]
|
||||
}
|
||||
|
||||
// Mounts returns a comma-separated string of mount names present on the container.
|
||||
// If the trunc option is set, names can be truncated (ellipsized).
|
||||
func (c *ContainerContext) Mounts() string {
|
||||
var mounts []string
|
||||
for _, name := range c.c.Mounts {
|
||||
if c.trunc {
|
||||
name = formatter.Ellipsis(name, 15)
|
||||
}
|
||||
mounts = append(mounts, name)
|
||||
}
|
||||
return strings.Join(mounts, ",")
|
||||
}
|
||||
|
||||
// LocalVolumes returns the number of volumes using the "local" volume driver.
|
||||
func (c *ContainerContext) LocalVolumes() string {
|
||||
return fmt.Sprintf("%d", c.c.LocalVolumes)
|
||||
}
|
||||
|
||||
// Networks returns a comma-separated string of networks that the container is
|
||||
// attached to.
|
||||
func (c *ContainerContext) Networks() string {
|
||||
return strings.Join(c.c.Networks, ",")
|
||||
}
|
||||
|
||||
// Size returns the container's size and virtual size (e.g. "2B (virtual 21.5MB)")
|
||||
func (c *ContainerContext) Size() string {
|
||||
if c.FieldsUsed == nil {
|
||||
c.FieldsUsed = map[string]interface{}{}
|
||||
}
|
||||
c.FieldsUsed["Size"] = struct{}{}
|
||||
srw := units.HumanSizeWithPrecision(float64(c.c.SizeRw), 3)
|
||||
sv := units.HumanSizeWithPrecision(float64(c.c.SizeRootFs), 3)
|
||||
|
||||
sf := srw
|
||||
if c.c.SizeRootFs > 0 {
|
||||
sf = fmt.Sprintf("%s (virtual %s)", srw, sv)
|
||||
}
|
||||
return sf
|
||||
}
|
||||
@@ -23,6 +23,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Print prints formatted lists in different formats
|
||||
@@ -65,7 +67,7 @@ func Print(toJSON interface{}, format string, outWriter io.Writer, writerFn func
|
||||
_, _ = fmt.Fprintln(outWriter, outJSON)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("format value %q could not be parsed: %w", format, api.ErrParsingFailed)
|
||||
return errors.Wrapf(api.ErrParsingFailed, "format value %q could not be parsed", format)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
27
cmd/main.go
27
cmd/main.go
@@ -24,31 +24,28 @@ import (
|
||||
"github.com/docker/cli/cli-plugins/plugin"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/compose/v2/cmd/cmdtrace"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose/v2/cmd/compatibility"
|
||||
commands "github.com/docker/compose/v2/cmd/compose"
|
||||
"github.com/docker/compose/v2/internal"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/compose"
|
||||
)
|
||||
|
||||
func pluginMain() {
|
||||
plugin.Run(func(dockerCli command.Cli) *cobra.Command {
|
||||
backend := compose.NewComposeService(dockerCli)
|
||||
cmd := commands.RootCommand(dockerCli, backend)
|
||||
serviceProxy := api.NewServiceProxy().WithService(compose.NewComposeService(dockerCli))
|
||||
cmd := commands.RootCommand(dockerCli, serviceProxy)
|
||||
originalPreRun := cmd.PersistentPreRunE
|
||||
cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
|
||||
// initialize the dockerCli instance
|
||||
if err := plugin.PersistentPreRunE(cmd, args); err != nil {
|
||||
return err
|
||||
}
|
||||
// compose-specific initialization
|
||||
dockerCliPostInitialize(dockerCli)
|
||||
|
||||
// TODO(milas): add an env var to enable logging from the
|
||||
// OTel components for debugging purposes
|
||||
_ = cmdtrace.Setup(cmd, dockerCli, os.Args[1:])
|
||||
_ = cmdtrace.Setup(cmd, dockerCli)
|
||||
|
||||
if originalPreRun != nil {
|
||||
return originalPreRun(cmd, args)
|
||||
@@ -71,22 +68,6 @@ func pluginMain() {
|
||||
})
|
||||
}
|
||||
|
||||
// dockerCliPostInitialize performs Compose-specific configuration for the
|
||||
// command.Cli instance provided by the plugin.Run() initialization.
|
||||
//
|
||||
// NOTE: This must be called AFTER plugin.PersistentPreRunE.
|
||||
func dockerCliPostInitialize(dockerCli command.Cli) {
|
||||
// HACK(milas): remove once docker/cli#4574 is merged; for now,
|
||||
// set it in a rather roundabout way by grabbing the underlying
|
||||
// concrete client and manually invoking an option on it
|
||||
_ = dockerCli.Apply(func(cli *command.DockerCli) error {
|
||||
if mobyClient, ok := cli.Client().(*client.Client); ok {
|
||||
_ = client.WithUserAgent("compose/" + internal.Version)(mobyClient)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func main() {
|
||||
if plugin.RunningStandalone() {
|
||||
os.Args = append([]string{"docker"}, compatibility.Convert(os.Args[1:])...)
|
||||
|
||||
@@ -5,38 +5,34 @@ Define and run multi-container applications with Docker.
|
||||
|
||||
### Subcommands
|
||||
|
||||
| Name | Description |
|
||||
|:--------------------------------|:-----------------------------------------------------------------------------------------|
|
||||
| [`attach`](compose_attach.md) | Attach local standard input, output, and error streams to a service's running container. |
|
||||
| [`build`](compose_build.md) | Build or rebuild services |
|
||||
| [`config`](compose_config.md) | Parse, resolve and render compose file in canonical format |
|
||||
| [`cp`](compose_cp.md) | Copy files/folders between a service container and the local filesystem |
|
||||
| [`create`](compose_create.md) | Creates containers for a service. |
|
||||
| [`down`](compose_down.md) | Stop and remove containers, networks |
|
||||
| [`events`](compose_events.md) | Receive real time events from containers. |
|
||||
| [`exec`](compose_exec.md) | Execute a command in a running container. |
|
||||
| [`images`](compose_images.md) | List images used by the created containers |
|
||||
| [`kill`](compose_kill.md) | Force stop service containers. |
|
||||
| [`logs`](compose_logs.md) | View output from containers |
|
||||
| [`ls`](compose_ls.md) | List running compose projects |
|
||||
| [`pause`](compose_pause.md) | Pause services |
|
||||
| [`port`](compose_port.md) | Print the public port for a port binding. |
|
||||
| [`ps`](compose_ps.md) | List containers |
|
||||
| [`pull`](compose_pull.md) | Pull service images |
|
||||
| [`push`](compose_push.md) | Push service images |
|
||||
| [`restart`](compose_restart.md) | Restart service containers |
|
||||
| [`rm`](compose_rm.md) | Removes stopped service containers |
|
||||
| [`run`](compose_run.md) | Run a one-off command on a service. |
|
||||
| [`scale`](compose_scale.md) | Scale services |
|
||||
| [`start`](compose_start.md) | Start services |
|
||||
| [`stats`](compose_stats.md) | Display a live stream of container(s) resource usage statistics |
|
||||
| [`stop`](compose_stop.md) | Stop services |
|
||||
| [`top`](compose_top.md) | Display the running processes |
|
||||
| [`unpause`](compose_unpause.md) | Unpause services |
|
||||
| [`up`](compose_up.md) | Create and start containers |
|
||||
| [`version`](compose_version.md) | Show the Docker Compose version information |
|
||||
| [`wait`](compose_wait.md) | Block until the first service container stops |
|
||||
| [`watch`](compose_watch.md) | Watch build context for service and rebuild/refresh containers when files are updated |
|
||||
| Name | Description |
|
||||
|:--------------------------------|:------------------------------------------------------------------------|
|
||||
| [`build`](compose_build.md) | Build or rebuild services |
|
||||
| [`config`](compose_config.md) | Parse, resolve and render compose file in canonical format |
|
||||
| [`cp`](compose_cp.md) | Copy files/folders between a service container and the local filesystem |
|
||||
| [`create`](compose_create.md) | Creates containers for a service. |
|
||||
| [`down`](compose_down.md) | Stop and remove containers, networks |
|
||||
| [`events`](compose_events.md) | Receive real time events from containers. |
|
||||
| [`exec`](compose_exec.md) | Execute a command in a running container. |
|
||||
| [`images`](compose_images.md) | List images used by the created containers |
|
||||
| [`kill`](compose_kill.md) | Force stop service containers. |
|
||||
| [`logs`](compose_logs.md) | View output from containers |
|
||||
| [`ls`](compose_ls.md) | List running compose projects |
|
||||
| [`pause`](compose_pause.md) | Pause services |
|
||||
| [`port`](compose_port.md) | Print the public port for a port binding. |
|
||||
| [`ps`](compose_ps.md) | List containers |
|
||||
| [`pull`](compose_pull.md) | Pull service images |
|
||||
| [`push`](compose_push.md) | Push service images |
|
||||
| [`restart`](compose_restart.md) | Restart service containers |
|
||||
| [`rm`](compose_rm.md) | Removes stopped service containers |
|
||||
| [`run`](compose_run.md) | Run a one-off command on a service. |
|
||||
| [`start`](compose_start.md) | Start services |
|
||||
| [`stop`](compose_stop.md) | Stop services |
|
||||
| [`top`](compose_top.md) | Display the running processes |
|
||||
| [`unpause`](compose_unpause.md) | Unpause services |
|
||||
| [`up`](compose_up.md) | Create and start containers |
|
||||
| [`version`](compose_version.md) | Show the Docker Compose version information |
|
||||
| [`wait`](compose_wait.md) | Block until the first service container stops |
|
||||
|
||||
|
||||
### Options
|
||||
@@ -59,7 +55,7 @@ Define and run multi-container applications with Docker.
|
||||
|
||||
## Description
|
||||
|
||||
You can use the compose subcommand, `docker compose [-f <arg>...] [options] [COMMAND] [ARGS...]`, to build and manage
|
||||
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 the name and path of one or more Compose files
|
||||
@@ -148,16 +144,16 @@ demo_1 | 64 bytes from 127.0.0.1: seq=0 ttl=64 time=0.095 ms
|
||||
### Use profiles to enable optional services
|
||||
|
||||
Use `--profile` to specify one or more active profiles
|
||||
Calling `docker compose --profile frontend up` starts the services with the profile `frontend` and services
|
||||
Calling `docker compose --profile frontend up` will start the services with the profile `frontend` and services
|
||||
without any specified profiles.
|
||||
You can also enable multiple profiles, e.g. with `docker compose --profile frontend --profile debug up` the profiles `frontend` and `debug` is enabled.
|
||||
You can also enable multiple profiles, e.g. with `docker compose --profile frontend --profile debug up` the profiles `frontend` and `debug` will be enabled.
|
||||
|
||||
Profiles can also be set by `COMPOSE_PROFILES` environment variable.
|
||||
|
||||
### Configuring parallelism
|
||||
|
||||
Use `--parallel` to specify the maximum level of parallelism for concurrent engine calls.
|
||||
Calling `docker compose --parallel 1 pull` pulls the pullable images defined in the Compose file
|
||||
Calling `docker compose --parallel 1 pull` will pull the pullable images defined in the Compose file
|
||||
one at a time. This can also be used to control build concurrency.
|
||||
|
||||
Parallelism can also be set by the `COMPOSE_PARALLEL_LIMIT` environment variable.
|
||||
@@ -173,7 +169,7 @@ and `COMPOSE_PARALLEL_LIMIT` does the same as the `--parallel` flag.
|
||||
|
||||
If flags are explicitly set on the command line, the associated environment variable is ignored.
|
||||
|
||||
Setting the `COMPOSE_IGNORE_ORPHANS` environment variable to `true` stops docker compose from detecting orphaned
|
||||
Setting the `COMPOSE_IGNORE_ORPHANS` environment variable to `true` will stop docker compose from detecting orphaned
|
||||
containers for the project.
|
||||
|
||||
### Use Dry Run mode to test your command
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# docker compose alpha dry-run
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
Dry run command allows you to test a command without applying changes.
|
||||
EXPERIMENTAL - Dry run command allow you to test a command without applying changes
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
@@ -5,11 +5,9 @@ Publish compose application
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:--------------------------|:---------|:--------|:-------------------------------------------------------------------------------|
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| `--oci-version` | `string` | | OCI Image/Artifact specification version (automatically determined by default) |
|
||||
| `--resolve-image-digests` | | | Pin image tags to digests. |
|
||||
| Name | Type | Default | Description |
|
||||
|:------------|:-----|:--------|:--------------------------------|
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
# docker compose alpha scale
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
Scale services.
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:------------|:-----|:--------|:--------------------------------|
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| `--no-deps` | | | Don't start linked services |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
# docker compose alpha watch
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
Watch build context for service and rebuild/refresh containers when files are updated
|
||||
EXPERIMENTAL - Watch build context for service and rebuild/refresh containers when files are updated
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:------------|:-----|:--------|:----------------------------------------------|
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| `--no-up` | | | Do not build & start services before watching |
|
||||
| `--quiet` | | | hide build output |
|
||||
| Name | Type | Default | Description |
|
||||
|:------------|:-----|:--------|:--------------------------------|
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| `--quiet` | | | hide build output |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
# docker compose attach
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
Attach local standard input, output, and error streams to a service's running container.
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:----------------|:---------|:--------|:----------------------------------------------------------|
|
||||
| `--detach-keys` | `string` | | Override the key sequence for detaching from a container. |
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| `--index` | `int` | `0` | index of the container if service has multiple replicas. |
|
||||
| `--no-stdin` | | | Do not attach STDIN |
|
||||
| `--sig-proxy` | | | Proxy all received signals to the process |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
@@ -5,18 +5,17 @@ Build or rebuild services
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:----------------------|:--------------|:--------|:------------------------------------------------------------------------------------------------------------|
|
||||
| `--build-arg` | `stringArray` | | Set build-time variables for services. |
|
||||
| `--builder` | `string` | | Set builder to use. |
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| `-m`, `--memory` | `bytes` | `0` | Set memory limit for the build container. Not supported by BuildKit. |
|
||||
| `--no-cache` | | | Do not use cache when building the image |
|
||||
| `--pull` | | | Always attempt to pull a newer version of the image. |
|
||||
| `--push` | | | Push service images. |
|
||||
| `-q`, `--quiet` | | | Don't print anything to STDOUT |
|
||||
| `--ssh` | `string` | | Set SSH authentications used when building service images. (use 'default' for using your default SSH Agent) |
|
||||
| `--with-dependencies` | | | Also build dependencies (transitively). |
|
||||
| Name | Type | Default | Description |
|
||||
|:-----------------|:--------------|:--------|:------------------------------------------------------------------------------------------------------------|
|
||||
| `--build-arg` | `stringArray` | | Set build-time variables for services. |
|
||||
| `--builder` | `string` | | Set builder to use. |
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| `-m`, `--memory` | `bytes` | `0` | Set memory limit for the build container. Not supported by BuildKit. |
|
||||
| `--no-cache` | | | Do not use cache when building the image |
|
||||
| `--pull` | | | Always attempt to pull a newer version of the image. |
|
||||
| `--push` | | | Push service images. |
|
||||
| `-q`, `--quiet` | | | Don't print anything to STDOUT |
|
||||
| `--ssh` | `string` | | Set SSH authentications used when building service images. (use 'default' for using your default SSH Agent) |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
@@ -31,6 +31,6 @@ Parse, resolve and render compose file in canonical format
|
||||
|
||||
## Description
|
||||
|
||||
`docker compose config` renders the actual data model to be applied on the Docker Engine.
|
||||
It merges the Compose files set by `-f` flags, resolves variables in the Compose file, and expands short-notation into
|
||||
`docker compose config` renders the actual data model to be applied on 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.
|
||||
|
||||
@@ -5,16 +5,16 @@ Creates containers for a service.
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:-------------------|:--------------|:---------|:----------------------------------------------------------------------------------------------|
|
||||
| `--build` | | | Build images before starting containers. |
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| `--force-recreate` | | | Recreate containers even if their configuration and image haven't changed. |
|
||||
| `--no-build` | | | Don't build an image, even if it's policy. |
|
||||
| `--no-recreate` | | | If containers already exist, don't recreate them. Incompatible with --force-recreate. |
|
||||
| `--pull` | `string` | `policy` | Pull image before running ("always"\|"missing"\|"never"\|"build") |
|
||||
| `--remove-orphans` | | | Remove containers for services not defined in the Compose file. |
|
||||
| `--scale` | `stringArray` | | Scale SERVICE to NUM instances. Overrides the `scale` setting in the Compose file if present. |
|
||||
| Name | Type | Default | Description |
|
||||
|:-------------------|:--------------|:----------|:----------------------------------------------------------------------------------------------|
|
||||
| `--build` | | | Build images before starting containers. |
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| `--force-recreate` | | | Recreate containers even if their configuration and image haven't changed. |
|
||||
| `--no-build` | | | Don't build an image, even if it's missing. |
|
||||
| `--no-recreate` | | | If containers already exist, don't recreate them. Incompatible with --force-recreate. |
|
||||
| `--pull` | `string` | `missing` | Pull image before running ("always"\|"missing"\|"never") |
|
||||
| `--remove-orphans` | | | Remove containers for services not defined in the Compose file. |
|
||||
| `--scale` | `stringArray` | | Scale SERVICE to NUM instances. Overrides the `scale` setting in the Compose file if present. |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
@@ -22,12 +22,12 @@ Stops containers and removes containers, networks, volumes, and images created b
|
||||
|
||||
By default, the only things removed are:
|
||||
|
||||
- Containers for services defined in the Compose file.
|
||||
- Networks defined in the networks section of the Compose file.
|
||||
- The default network, if one is used.
|
||||
- Containers for services defined in the Compose file
|
||||
- Networks defined in the networks section of the Compose file
|
||||
- The default network, if one is used
|
||||
|
||||
Networks and volumes defined as external are never removed.
|
||||
|
||||
Anonymous volumes are not removed by default. However, as they don’t have a stable name, they are not automatically
|
||||
Anonymous volumes are not removed by default. However, as they don’t have a stable name, they will not be automatically
|
||||
mounted by a subsequent `up`. For data that needs to persist between updates, use explicit paths as bind mounts or
|
||||
named volumes.
|
||||
|
||||
@@ -33,4 +33,4 @@ With the `--json` flag, a json object is printed one per line with the format:
|
||||
}
|
||||
```
|
||||
|
||||
The events that can be received using this can be seen [here](https://docs.docker.com/engine/reference/commandline/system_events/#object-types).
|
||||
The events that can be received using this can be seen [here](https://docs.docker.com/engine/reference/commandline/events/#object-types).
|
||||
|
||||
@@ -9,7 +9,6 @@ View output from containers
|
||||
|:---------------------|:---------|:--------|:-----------------------------------------------------------------------------------------------|
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| `-f`, `--follow` | | | Follow log output. |
|
||||
| `--index` | `int` | `0` | index of the container if service has multiple replicas |
|
||||
| `--no-color` | | | Produce monochrome output. |
|
||||
| `--no-log-prefix` | | | Don't print prefix in logs. |
|
||||
| `--since` | `string` | | Show logs since timestamp (e.g. 2013-01-02T13:23:37Z) or relative (e.g. 42m for 42 minutes) |
|
||||
|
||||
@@ -5,17 +5,15 @@ List containers
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:----------------------|:--------------|:--------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `-a`, `--all` | | | Show all stopped containers (including those created by the run command) |
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| [`--filter`](#filter) | `string` | | Filter services by a property (supported filters: status). |
|
||||
| [`--format`](#format) | `string` | `table` | Format output using a custom template:<br>'table': Print output in table format with column headers (default)<br>'table TEMPLATE': Print output in table format using the given Go template<br>'json': Print in JSON format<br>'TEMPLATE': Print output using the given Go template.<br>Refer to https://docs.docker.com/go/formatting/ for more information about formatting output with templates |
|
||||
| `--no-trunc` | | | Don't truncate output |
|
||||
| `--orphans` | | | Include orphaned services (not declared by project) |
|
||||
| `-q`, `--quiet` | | | Only display IDs |
|
||||
| `--services` | | | Display services |
|
||||
| [`--status`](#status) | `stringArray` | | Filter services by status. Values: [paused \| restarting \| removing \| running \| dead \| created \| exited] |
|
||||
| Name | Type | Default | Description |
|
||||
|:----------------------|:--------------|:--------|:--------------------------------------------------------------------------------------------------------------|
|
||||
| `-a`, `--all` | | | Show all stopped containers (including those created by the run command) |
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| [`--filter`](#filter) | `string` | | Filter services by a property (supported filters: status). |
|
||||
| [`--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] |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
@@ -30,7 +28,7 @@ NAME IMAGE COMMAND SERVICE CREATED STATUS
|
||||
example-foo-1 alpine "/entrypoint.…" foo 4 seconds ago Up 2 seconds 0.0.0.0:8080->80/tcp
|
||||
```
|
||||
|
||||
By default, only running containers are shown. `--all` flag can be used to include stopped containers.
|
||||
By default, only running containers are shown. `--all` flag can be used to include stopped containers
|
||||
|
||||
```console
|
||||
$ docker compose ps --all
|
||||
@@ -54,7 +52,7 @@ $ docker compose ps --format json
|
||||
```
|
||||
|
||||
The JSON output allows you to use the information in other tools for further
|
||||
processing, for example, using the [`jq` utility](https://stedolan.github.io/jq/)
|
||||
processing, for example, using the [`jq` utility](https://stedolan.github.io/jq/){:target="_blank" rel="noopener" class="_"}
|
||||
to pretty-print the JSON:
|
||||
|
||||
```console
|
||||
|
||||
@@ -5,14 +5,13 @@ Pull service images
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:-------------------------|:---------|:--------|:--------------------------------------------------------|
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| `--ignore-buildable` | | | Ignore images that can be built. |
|
||||
| `--ignore-pull-failures` | | | Pull what it can and ignores images with pull failures. |
|
||||
| `--include-deps` | | | Also pull services declared as dependencies. |
|
||||
| `--policy` | `string` | | Apply pull policy ("missing"\|"always"). |
|
||||
| `-q`, `--quiet` | | | Pull without printing progress information. |
|
||||
| Name | Type | Default | Description |
|
||||
|:-------------------------|:-----|:--------|:--------------------------------------------------------|
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| `--ignore-buildable` | | | Ignore images that can be built. |
|
||||
| `--ignore-pull-failures` | | | Pull what it can and ignores images with pull failures. |
|
||||
| `--include-deps` | | | Also pull services declared as dependencies. |
|
||||
| `-q`, `--quiet` | | | Pull without printing progress information. |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
@@ -25,7 +24,7 @@ those images.
|
||||
|
||||
## Examples
|
||||
|
||||
Consider the following `compose.yaml`:
|
||||
suppose you have this `compose.yaml`:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
@@ -66,4 +65,5 @@ $ docker compose pull db
|
||||
⠹ c8752d5b785c Waiting 9.3s
|
||||
```
|
||||
|
||||
`docker compose pull` tries to pull image for services with a build section. If pull fails, it lets you know this service image must be built. You can skip this by setting `--ignore-buildable` flag.
|
||||
`docker compose pull` will try to pull image for services with a build section. If pull fails, it will let
|
||||
user know this service image MUST be built. You can skip this by setting `--ignore-buildable` flag
|
||||
|
||||
@@ -23,6 +23,6 @@ after running this command. For example, changes to environment variables (which
|
||||
after a container is built, but before the container's command is executed) are not updated
|
||||
after restarting.
|
||||
|
||||
If you are looking to configure a service's restart policy, refer to
|
||||
If you are looking to configure a service's restart policy, please refer to
|
||||
[restart](https://github.com/compose-spec/compose-spec/blob/master/spec.md#restart)
|
||||
or [restart_policy](https://github.com/compose-spec/compose-spec/blob/master/deploy.md#restart_policy).
|
||||
|
||||
@@ -5,29 +5,29 @@ Run a one-off command on a service.
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:------------------------|:--------------|:--------|:----------------------------------------------------------------------------------|
|
||||
| `--build` | | | Build image before starting container. |
|
||||
| `--cap-add` | `list` | | Add Linux capabilities |
|
||||
| `--cap-drop` | `list` | | Drop Linux capabilities |
|
||||
| `-d`, `--detach` | | | Run container in background and print container ID |
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| `--entrypoint` | `string` | | Override the entrypoint of the image |
|
||||
| `-e`, `--env` | `stringArray` | | Set environment variables |
|
||||
| `-i`, `--interactive` | | | Keep STDIN open even if not attached. |
|
||||
| `-l`, `--label` | `stringArray` | | Add or override a label |
|
||||
| `--name` | `string` | | Assign a name to the container |
|
||||
| `-T`, `--no-TTY` | | | Disable pseudo-TTY allocation (default: auto-detected). |
|
||||
| `--no-deps` | | | Don't start linked services. |
|
||||
| `-p`, `--publish` | `stringArray` | | Publish a container's port(s) to the host. |
|
||||
| `--quiet-pull` | | | Pull without printing progress information. |
|
||||
| `--remove-orphans` | | | Remove containers for services not defined in the Compose file. |
|
||||
| `--rm` | | | Automatically remove the container when it exits |
|
||||
| `-P`, `--service-ports` | | | Run command with all service's ports enabled and mapped to the host. |
|
||||
| `--use-aliases` | | | Use the service's network useAliases in the network(s) the container connects to. |
|
||||
| `-u`, `--user` | `string` | | Run as specified username or uid |
|
||||
| `-v`, `--volume` | `stringArray` | | Bind mount a volume. |
|
||||
| `-w`, `--workdir` | `string` | | Working directory inside the container |
|
||||
| Name | Type | Default | Description |
|
||||
|:----------------------|:--------------|:--------|:----------------------------------------------------------------------------------|
|
||||
| `--build` | | | Build image before starting container. |
|
||||
| `--cap-add` | `list` | | Add Linux capabilities |
|
||||
| `--cap-drop` | `list` | | Drop Linux capabilities |
|
||||
| `-d`, `--detach` | | | Run container in background and print container ID |
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| `--entrypoint` | `string` | | Override the entrypoint of the image |
|
||||
| `-e`, `--env` | `stringArray` | | Set environment variables |
|
||||
| `-i`, `--interactive` | | | Keep STDIN open even if not attached. |
|
||||
| `-l`, `--label` | `stringArray` | | Add or override a label |
|
||||
| `--name` | `string` | | Assign a name to the container |
|
||||
| `-T`, `--no-TTY` | | | Disable pseudo-TTY allocation (default: auto-detected). |
|
||||
| `--no-deps` | | | Don't start linked services. |
|
||||
| `-p`, `--publish` | `stringArray` | | Publish a container's port(s) to the host. |
|
||||
| `--quiet-pull` | | | Pull without printing progress information. |
|
||||
| `--remove-orphans` | | | Remove containers for services not defined in the Compose file. |
|
||||
| `--rm` | | | Automatically remove the container when it exits |
|
||||
| `--service-ports` | | | Run command with the service's ports enabled and mapped to the host. |
|
||||
| `--use-aliases` | | | Use the service's network useAliases in the network(s) the container connects to. |
|
||||
| `-u`, `--user` | `string` | | Run as specified username or uid |
|
||||
| `-v`, `--volume` | `stringArray` | | Bind mount a volume. |
|
||||
| `-w`, `--workdir` | `string` | | Working directory inside the container |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
# docker compose scale
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
Scale services
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:------------|:-----|:--------|:--------------------------------|
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| `--no-deps` | | | Don't start linked services. |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
# docker compose stats
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
Display a live stream of container(s) resource usage statistics
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:--------------|:---------|:--------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `-a`, `--all` | | | Show all containers (default shows just running) |
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| `--format` | `string` | | Format output using a custom template:<br>'table': Print output in table format with column headers (default)<br>'table TEMPLATE': Print output in table format using the given Go template<br>'json': Print in JSON format<br>'TEMPLATE': Print output using the given Go template.<br>Refer to https://docs.docker.com/go/formatting/ for more information about formatting output with templates |
|
||||
| `--no-stream` | | | Disable streaming stats and only pull the first result |
|
||||
| `--no-trunc` | | | Do not truncate output |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
@@ -5,33 +5,33 @@ Create and start containers
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:-----------------------------|:--------------|:---------|:---------------------------------------------------------------------------------------------------------|
|
||||
| `--abort-on-container-exit` | | | Stops all containers if any container was stopped. Incompatible with -d |
|
||||
| `--always-recreate-deps` | | | Recreate dependent containers. Incompatible with --no-recreate. |
|
||||
| `--attach` | `stringArray` | | Restrict attaching to the specified services. Incompatible with --attach-dependencies. |
|
||||
| `--attach-dependencies` | | | Automatically attach to log output of dependent services. |
|
||||
| `--build` | | | Build images before starting containers. |
|
||||
| `-d`, `--detach` | | | Detached mode: Run containers in the background |
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| `--exit-code-from` | `string` | | Return the exit code of the selected service container. Implies --abort-on-container-exit |
|
||||
| `--force-recreate` | | | Recreate containers even if their configuration and image haven't changed. |
|
||||
| `--no-attach` | `stringArray` | | Do not attach (stream logs) to the specified services. |
|
||||
| `--no-build` | | | Don't build an image, even if it's policy. |
|
||||
| `--no-color` | | | Produce monochrome output. |
|
||||
| `--no-deps` | | | Don't start linked services. |
|
||||
| `--no-log-prefix` | | | Don't print prefix in logs. |
|
||||
| `--no-recreate` | | | If containers already exist, don't recreate them. Incompatible with --force-recreate. |
|
||||
| `--no-start` | | | Don't start the services after creating them. |
|
||||
| `--pull` | `string` | `policy` | Pull image before running ("always"\|"missing"\|"never") |
|
||||
| `--quiet-pull` | | | Pull without printing progress information. |
|
||||
| `--remove-orphans` | | | Remove containers for services not defined in the Compose file. |
|
||||
| `-V`, `--renew-anon-volumes` | | | Recreate anonymous volumes instead of retrieving data from the previous containers. |
|
||||
| `--scale` | `stringArray` | | Scale SERVICE to NUM instances. Overrides the `scale` setting in the Compose file if present. |
|
||||
| `-t`, `--timeout` | `int` | `0` | 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. |
|
||||
| `--wait-timeout` | `int` | `0` | Maximum duration to wait for the project to be running\|healthy. |
|
||||
| Name | Type | Default | Description |
|
||||
|:-----------------------------|:--------------|:----------|:---------------------------------------------------------------------------------------------------------|
|
||||
| `--abort-on-container-exit` | | | Stops all containers if any container was stopped. Incompatible with -d |
|
||||
| `--always-recreate-deps` | | | Recreate dependent containers. Incompatible with --no-recreate. |
|
||||
| `--attach` | `stringArray` | | Attach to service output. |
|
||||
| `--attach-dependencies` | | | Attach to dependent containers. |
|
||||
| `--build` | | | Build images before starting containers. |
|
||||
| `-d`, `--detach` | | | Detached mode: Run containers in the background |
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| `--exit-code-from` | `string` | | Return the exit code of the selected service container. Implies --abort-on-container-exit |
|
||||
| `--force-recreate` | | | Recreate containers even if their configuration and image haven't changed. |
|
||||
| `--no-attach` | `stringArray` | | Don't attach to specified service. |
|
||||
| `--no-build` | | | Don't build an image, even if it's missing. |
|
||||
| `--no-color` | | | Produce monochrome output. |
|
||||
| `--no-deps` | | | Don't start linked services. |
|
||||
| `--no-log-prefix` | | | Don't print prefix in logs. |
|
||||
| `--no-recreate` | | | If containers already exist, don't recreate them. Incompatible with --force-recreate. |
|
||||
| `--no-start` | | | Don't start the services after creating them. |
|
||||
| `--pull` | `string` | `missing` | Pull image before running ("always"\|"missing"\|"never") |
|
||||
| `--quiet-pull` | | | Pull without printing progress information. |
|
||||
| `--remove-orphans` | | | Remove containers for services not defined in the Compose file. |
|
||||
| `-V`, `--renew-anon-volumes` | | | Recreate anonymous volumes instead of retrieving data from the previous containers. |
|
||||
| `--scale` | `stringArray` | | Scale SERVICE to NUM instances. Overrides the `scale` setting in the Compose file if present. |
|
||||
| `-t`, `--timeout` | `int` | `0` | 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. |
|
||||
| `--wait-timeout` | `int` | `0` | timeout waiting for application to be running\|healthy. |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
# docker compose watch
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
Watch build context for service and rebuild/refresh containers when files are updated
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:------------|:-----|:--------|:----------------------------------------------|
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| `--no-up` | | | Do not build & start services before watching |
|
||||
| `--quiet` | | | hide build output |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
command: docker compose
|
||||
short: Docker Compose
|
||||
long: |-
|
||||
You can use the compose subcommand, `docker compose [-f <arg>...] [options] [COMMAND] [ARGS...]`, to build and manage
|
||||
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 the name and path of one or more Compose files
|
||||
@@ -90,16 +90,16 @@ long: |-
|
||||
### Use profiles to enable optional services
|
||||
|
||||
Use `--profile` to specify one or more active profiles
|
||||
Calling `docker compose --profile frontend up` starts the services with the profile `frontend` and services
|
||||
Calling `docker compose --profile frontend up` will start the services with the profile `frontend` and services
|
||||
without any specified profiles.
|
||||
You can also enable multiple profiles, e.g. with `docker compose --profile frontend --profile debug up` the profiles `frontend` and `debug` is enabled.
|
||||
You can also enable multiple profiles, e.g. with `docker compose --profile frontend --profile debug up` the profiles `frontend` and `debug` will be enabled.
|
||||
|
||||
Profiles can also be set by `COMPOSE_PROFILES` environment variable.
|
||||
|
||||
### Configuring parallelism
|
||||
|
||||
Use `--parallel` to specify the maximum level of parallelism for concurrent engine calls.
|
||||
Calling `docker compose --parallel 1 pull` pulls the pullable images defined in the Compose file
|
||||
Calling `docker compose --parallel 1 pull` will pull the pullable images defined in the Compose file
|
||||
one at a time. This can also be used to control build concurrency.
|
||||
|
||||
Parallelism can also be set by the `COMPOSE_PARALLEL_LIMIT` environment variable.
|
||||
@@ -115,7 +115,7 @@ long: |-
|
||||
|
||||
If flags are explicitly set on the command line, the associated environment variable is ignored.
|
||||
|
||||
Setting the `COMPOSE_IGNORE_ORPHANS` environment variable to `true` stops docker compose from detecting orphaned
|
||||
Setting the `COMPOSE_IGNORE_ORPHANS` environment variable to `true` will stop docker compose from detecting orphaned
|
||||
containers for the project.
|
||||
|
||||
### Use Dry Run mode to test your command
|
||||
@@ -146,7 +146,6 @@ usage: docker compose
|
||||
pname: docker
|
||||
plink: docker.yaml
|
||||
cname:
|
||||
- docker compose attach
|
||||
- docker compose build
|
||||
- docker compose config
|
||||
- docker compose cp
|
||||
@@ -166,18 +165,14 @@ cname:
|
||||
- docker compose restart
|
||||
- docker compose rm
|
||||
- docker compose run
|
||||
- docker compose scale
|
||||
- docker compose start
|
||||
- docker compose stats
|
||||
- docker compose stop
|
||||
- docker compose top
|
||||
- docker compose unpause
|
||||
- docker compose up
|
||||
- docker compose version
|
||||
- docker compose wait
|
||||
- docker compose watch
|
||||
clink:
|
||||
- docker_compose_attach.yaml
|
||||
- docker_compose_build.yaml
|
||||
- docker_compose_config.yaml
|
||||
- docker_compose_cp.yaml
|
||||
@@ -197,16 +192,13 @@ clink:
|
||||
- docker_compose_restart.yaml
|
||||
- docker_compose_rm.yaml
|
||||
- docker_compose_run.yaml
|
||||
- docker_compose_scale.yaml
|
||||
- docker_compose_start.yaml
|
||||
- docker_compose_stats.yaml
|
||||
- docker_compose_stop.yaml
|
||||
- docker_compose_top.yaml
|
||||
- docker_compose_unpause.yaml
|
||||
- docker_compose_up.yaml
|
||||
- docker_compose_version.yaml
|
||||
- docker_compose_wait.yaml
|
||||
- docker_compose_watch.yaml
|
||||
options:
|
||||
- option: ansi
|
||||
value_type: string
|
||||
|
||||
@@ -6,9 +6,11 @@ plink: docker_compose.yaml
|
||||
cname:
|
||||
- docker compose alpha publish
|
||||
- docker compose alpha viz
|
||||
- docker compose alpha watch
|
||||
clink:
|
||||
- docker_compose_alpha_publish.yaml
|
||||
- docker_compose_alpha_viz.yaml
|
||||
- docker_compose_alpha_watch.yaml
|
||||
inherited_options:
|
||||
- option: dry-run
|
||||
value_type: bool
|
||||
|
||||
@@ -4,27 +4,6 @@ long: Publish compose application
|
||||
usage: docker compose alpha publish [OPTIONS] [REPOSITORY]
|
||||
pname: docker compose alpha
|
||||
plink: docker_compose_alpha.yaml
|
||||
options:
|
||||
- option: oci-version
|
||||
value_type: string
|
||||
description: |
|
||||
OCI Image/Artifact specification version (automatically determined by default)
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: resolve-image-digests
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Pin image tags to digests.
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
inherited_options:
|
||||
- option: dry-run
|
||||
value_type: bool
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
command: docker compose alpha scale
|
||||
short: Scale services
|
||||
long: Scale services
|
||||
usage: docker compose alpha scale [SERVICE=REPLICAS...]
|
||||
pname: docker compose alpha
|
||||
plink: docker_compose_alpha.yaml
|
||||
options:
|
||||
- option: no-deps
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Don't start linked services.
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
inherited_options:
|
||||
- option: dry-run
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Execute command in dry run mode
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: true
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
|
||||
@@ -1,22 +1,12 @@
|
||||
command: docker compose alpha watch
|
||||
short: |
|
||||
Watch build context for service and rebuild/refresh containers when files are updated
|
||||
EXPERIMENTAL - Watch build context for service and rebuild/refresh containers when files are updated
|
||||
long: |
|
||||
Watch build context for service and rebuild/refresh containers when files are updated
|
||||
EXPERIMENTAL - Watch build context for service and rebuild/refresh containers when files are updated
|
||||
usage: docker compose alpha watch [SERVICE...]
|
||||
pname: docker compose alpha
|
||||
plink: docker_compose_alpha.yaml
|
||||
options:
|
||||
- option: no-up
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Do not build & start services before watching
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: quiet
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
command: docker compose attach
|
||||
short: |
|
||||
Attach local standard input, output, and error streams to a service's running container.
|
||||
long: |
|
||||
Attach local standard input, output, and error streams to a service's running container.
|
||||
usage: docker compose attach [OPTIONS] SERVICE
|
||||
pname: docker compose
|
||||
plink: docker_compose.yaml
|
||||
options:
|
||||
- option: detach-keys
|
||||
value_type: string
|
||||
description: Override the key sequence for detaching from a container.
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: index
|
||||
value_type: int
|
||||
default_value: "0"
|
||||
description: index of the container if service has multiple replicas.
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: no-stdin
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Do not attach STDIN
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: sig-proxy
|
||||
value_type: bool
|
||||
default_value: "true"
|
||||
description: Proxy all received signals to the process
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
inherited_options:
|
||||
- option: dry-run
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Execute command in dry run mode
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
|
||||
@@ -147,16 +147,6 @@ options:
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: with-dependencies
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Also build dependencies (transitively).
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
inherited_options:
|
||||
- option: dry-run
|
||||
value_type: bool
|
||||
|
||||
@@ -2,8 +2,8 @@ command: docker compose config
|
||||
aliases: docker compose config, docker compose convert
|
||||
short: Parse, resolve and render compose file in canonical format
|
||||
long: |-
|
||||
`docker compose config` renders the actual data model to be applied on the Docker Engine.
|
||||
It merges the Compose files set by `-f` flags, resolves variables in the Compose file, and expands short-notation into
|
||||
`docker compose config` renders the actual data model to be applied on 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.
|
||||
usage: docker compose config [OPTIONS] [SERVICE...]
|
||||
pname: docker compose
|
||||
|
||||
@@ -29,7 +29,7 @@ options:
|
||||
- option: no-build
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Don't build an image, even if it's policy.
|
||||
description: Don't build an image, even if it's missing.
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
@@ -49,8 +49,8 @@ options:
|
||||
swarm: false
|
||||
- option: pull
|
||||
value_type: string
|
||||
default_value: policy
|
||||
description: Pull image before running ("always"|"missing"|"never"|"build")
|
||||
default_value: missing
|
||||
description: Pull image before running ("always"|"missing"|"never")
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
|
||||
@@ -5,13 +5,13 @@ long: |-
|
||||
|
||||
By default, the only things removed are:
|
||||
|
||||
- Containers for services defined in the Compose file.
|
||||
- Networks defined in the networks section of the Compose file.
|
||||
- The default network, if one is used.
|
||||
- Containers for services defined in the Compose file
|
||||
- Networks defined in the networks section of the Compose file
|
||||
- The default network, if one is used
|
||||
|
||||
Networks and volumes defined as external are never removed.
|
||||
|
||||
Anonymous volumes are not removed by default. However, as they don’t have a stable name, they are not automatically
|
||||
Anonymous volumes are not removed by default. However, as they don’t have a stable name, they will not be automatically
|
||||
mounted by a subsequent `up`. For data that needs to persist between updates, use explicit paths as bind mounts or
|
||||
named volumes.
|
||||
usage: docker compose down [OPTIONS] [SERVICES]
|
||||
|
||||
@@ -19,7 +19,7 @@ long: |-
|
||||
}
|
||||
```
|
||||
|
||||
The events that can be received using this can be seen [here](/engine/reference/commandline/system_events/#object-types).
|
||||
The events that can be received using this can be seen [here](/engine/reference/commandline/events/#object-types).
|
||||
usage: docker compose events [OPTIONS] [SERVICE...]
|
||||
pname: docker compose
|
||||
plink: docker_compose.yaml
|
||||
|
||||
@@ -16,16 +16,6 @@ options:
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: index
|
||||
value_type: int
|
||||
default_value: "0"
|
||||
description: index of the container if service has multiple replicas
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: no-color
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
|
||||
@@ -9,7 +9,7 @@ long: |-
|
||||
example-foo-1 alpine "/entrypoint.…" foo 4 seconds ago Up 2 seconds 0.0.0.0:8080->80/tcp
|
||||
```
|
||||
|
||||
By default, only running containers are shown. `--all` flag can be used to include stopped containers.
|
||||
By default, only running containers are shown. `--all` flag can be used to include stopped containers
|
||||
|
||||
```console
|
||||
$ docker compose ps --all
|
||||
@@ -46,13 +46,7 @@ options:
|
||||
- option: format
|
||||
value_type: string
|
||||
default_value: table
|
||||
description: |-
|
||||
Format output using a custom template:
|
||||
'table': Print output in table format with column headers (default)
|
||||
'table TEMPLATE': Print output in table format using the given Go template
|
||||
'json': Print in JSON format
|
||||
'TEMPLATE': Print output using the given Go template.
|
||||
Refer to https://docs.docker.com/go/formatting/ for more information about formatting output with templates
|
||||
description: 'Format the output. Values: [table | json]'
|
||||
details_url: '#format'
|
||||
deprecated: false
|
||||
hidden: false
|
||||
@@ -60,26 +54,6 @@ options:
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: no-trunc
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Don't truncate output
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: orphans
|
||||
value_type: bool
|
||||
default_value: "true"
|
||||
description: Include orphaned services (not declared by project)
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: quiet
|
||||
shorthand: q
|
||||
value_type: bool
|
||||
@@ -138,7 +112,7 @@ examples: |-
|
||||
```
|
||||
|
||||
The JSON output allows you to use the information in other tools for further
|
||||
processing, for example, using the [`jq` utility](https://stedolan.github.io/jq/)
|
||||
processing, for example, using the [`jq` utility](https://stedolan.github.io/jq/){:target="_blank" rel="noopener" class="_"}
|
||||
to pretty-print the JSON:
|
||||
|
||||
```console
|
||||
|
||||
@@ -57,15 +57,6 @@ options:
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: policy
|
||||
value_type: string
|
||||
description: Apply pull policy ("missing"|"always").
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: quiet
|
||||
shorthand: q
|
||||
value_type: bool
|
||||
@@ -89,7 +80,7 @@ inherited_options:
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
examples: |-
|
||||
Consider the following `compose.yaml`:
|
||||
suppose you have this `compose.yaml`:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
@@ -130,7 +121,8 @@ examples: |-
|
||||
⠹ c8752d5b785c Waiting 9.3s
|
||||
```
|
||||
|
||||
`docker compose pull` tries to pull image for services with a build section. If pull fails, it lets you know this service image must be built. You can skip this by setting `--ignore-buildable` flag.
|
||||
`docker compose pull` will try to pull image for services with a build section. If pull fails, it will let
|
||||
user know this service image MUST be built. You can skip this by setting `--ignore-buildable` flag
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
|
||||
@@ -8,7 +8,7 @@ long: |-
|
||||
after a container is built, but before the container's command is executed) are not updated
|
||||
after restarting.
|
||||
|
||||
If you are looking to configure a service's restart policy, refer to
|
||||
If you are looking to configure a service's restart policy, please refer to
|
||||
[restart](https://github.com/compose-spec/compose-spec/blob/master/spec.md#restart)
|
||||
or [restart_policy](https://github.com/compose-spec/compose-spec/blob/master/deploy.md#restart_policy).
|
||||
usage: docker compose restart [OPTIONS] [SERVICE...]
|
||||
|
||||
@@ -211,11 +211,10 @@ options:
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: service-ports
|
||||
shorthand: P
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: |
|
||||
Run command with all service's ports enabled and mapped to the host.
|
||||
Run command with the service's ports enabled and mapped to the host.
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
command: docker compose scale
|
||||
short: Scale services
|
||||
long: Scale services
|
||||
usage: docker compose scale [SERVICE=REPLICAS...]
|
||||
pname: docker compose
|
||||
plink: docker_compose.yaml
|
||||
options:
|
||||
- option: no-deps
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Don't start linked services.
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
inherited_options:
|
||||
- option: dry-run
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Execute command in dry run mode
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
command: docker compose stats
|
||||
short: Display a live stream of container(s) resource usage statistics
|
||||
long: Display a live stream of container(s) resource usage statistics
|
||||
usage: docker compose stats [OPTIONS] [SERVICE]
|
||||
pname: docker compose
|
||||
plink: docker_compose.yaml
|
||||
options:
|
||||
- option: all
|
||||
shorthand: a
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Show all containers (default shows just running)
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: format
|
||||
value_type: string
|
||||
description: |-
|
||||
Format output using a custom template:
|
||||
'table': Print output in table format with column headers (default)
|
||||
'table TEMPLATE': Print output in table format using the given Go template
|
||||
'json': Print in JSON format
|
||||
'TEMPLATE': Print output using the given Go template.
|
||||
Refer to https://docs.docker.com/go/formatting/ for more information about formatting output with templates
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: no-stream
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Disable streaming stats and only pull the first result
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: no-trunc
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Do not truncate output
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
inherited_options:
|
||||
- option: dry-run
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Execute command in dry run mode
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
|
||||
@@ -48,8 +48,7 @@ options:
|
||||
- option: attach
|
||||
value_type: stringArray
|
||||
default_value: '[]'
|
||||
description: |
|
||||
Restrict attaching to the specified services. Incompatible with --attach-dependencies.
|
||||
description: Attach to service output.
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
@@ -59,7 +58,7 @@ options:
|
||||
- option: attach-dependencies
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Automatically attach to log output of dependent services.
|
||||
description: Attach to dependent containers.
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
@@ -111,7 +110,7 @@ options:
|
||||
- option: no-attach
|
||||
value_type: stringArray
|
||||
default_value: '[]'
|
||||
description: Do not attach (stream logs) to the specified services.
|
||||
description: Don't attach to specified service.
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
@@ -121,7 +120,7 @@ options:
|
||||
- option: no-build
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Don't build an image, even if it's policy.
|
||||
description: Don't build an image, even if it's missing.
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
@@ -181,7 +180,7 @@ options:
|
||||
swarm: false
|
||||
- option: pull
|
||||
value_type: string
|
||||
default_value: policy
|
||||
default_value: missing
|
||||
description: Pull image before running ("always"|"missing"|"never")
|
||||
deprecated: false
|
||||
hidden: false
|
||||
@@ -267,7 +266,7 @@ options:
|
||||
- option: wait-timeout
|
||||
value_type: int
|
||||
default_value: "0"
|
||||
description: Maximum duration to wait for the project to be running|healthy.
|
||||
description: timeout waiting for application to be running|healthy.
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
command: docker compose watch
|
||||
short: |
|
||||
Watch build context for service and rebuild/refresh containers when files are updated
|
||||
long: |
|
||||
Watch build context for service and rebuild/refresh containers when files are updated
|
||||
usage: docker compose watch [SERVICE...]
|
||||
pname: docker compose
|
||||
plink: docker_compose.yaml
|
||||
options:
|
||||
- option: no-up
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Do not build & start services before watching
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: quiet
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: hide build output
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
inherited_options:
|
||||
- option: dry-run
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Execute command in dry run mode
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
|
||||
21
e2e/cucumber-features/build-contexts.feature
Normal file
21
e2e/cucumber-features/build-contexts.feature
Normal file
@@ -0,0 +1,21 @@
|
||||
Feature: Build Contexts
|
||||
|
||||
Background:
|
||||
Given a compose file
|
||||
"""
|
||||
services:
|
||||
a:
|
||||
build:
|
||||
context: .
|
||||
dockerfile_inline: |
|
||||
# syntax=docker/dockerfile:1
|
||||
FROM alpine:latest
|
||||
COPY --from=dep /etc/hostname /
|
||||
additional_contexts:
|
||||
- dep=docker-image://ubuntu:latest
|
||||
"""
|
||||
|
||||
Scenario: Build w/ build context
|
||||
When I run "compose build"
|
||||
Then the exit code is 0
|
||||
|
||||
5
e2e/cucumber-features/down.feature
Normal file
5
e2e/cucumber-features/down.feature
Normal 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""
|
||||
28
e2e/cucumber-features/port-conflict.feature
Normal file
28
e2e/cucumber-features/port-conflict.feature
Normal file
@@ -0,0 +1,28 @@
|
||||
Feature: Report port conflicts
|
||||
|
||||
Background:
|
||||
Given a compose file
|
||||
"""
|
||||
services:
|
||||
web:
|
||||
image: nginx
|
||||
ports:
|
||||
- 31415:80
|
||||
"""
|
||||
And I run "docker rm -f nginx-pi-31415"
|
||||
|
||||
Scenario: Reports a port allocation conflict with another container
|
||||
Given I run "docker run -d -p 31415:80 --name nginx-pi-31415 nginx"
|
||||
When I run "compose up -d"
|
||||
Then the output contains "port is already allocated"
|
||||
And the exit code is 1
|
||||
|
||||
Scenario: Reports a port conflict with some other process
|
||||
Given a process listening on port 31415
|
||||
When I run "compose up -d"
|
||||
Then the output contains "address already in use"
|
||||
And the exit code is 1
|
||||
|
||||
Scenario: Cleanup
|
||||
Given I run "docker rm -f nginx-pi-31415"
|
||||
|
||||
29
e2e/cucumber-features/ps.feature
Normal file
29
e2e/cucumber-features/ps.feature
Normal file
@@ -0,0 +1,29 @@
|
||||
Feature: PS
|
||||
|
||||
Background:
|
||||
Given a compose file
|
||||
"""
|
||||
services:
|
||||
build:
|
||||
image: test:latest
|
||||
build:
|
||||
context: ./
|
||||
pull:
|
||||
image: alpine
|
||||
command: top
|
||||
"""
|
||||
And a dockerfile
|
||||
"""
|
||||
FROM golang:1.19-alpine
|
||||
"""
|
||||
And I run "docker rm -f external-test"
|
||||
|
||||
Scenario: external container from compose image exists
|
||||
When I run "compose build"
|
||||
Then the exit code is 0
|
||||
And I run "docker run --name external-test test:latest ls"
|
||||
Then the exit code is 0
|
||||
And I run "compose ps -a"
|
||||
Then the output does not contain "external-test"
|
||||
And I run "docker rm -f external-test"
|
||||
|
||||
15
e2e/cucumber-features/simple.feature
Normal file
15
e2e/cucumber-features/simple.feature
Normal 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 "Up"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user