Merge remote-tracking branch 'upstream/v2' into down-image-rm

This commit is contained in:
Milas Bowman 2022-09-13 18:00:41 +01:00
commit b49b9ffe7e
46 changed files with 1368 additions and 130 deletions

View File

@ -19,7 +19,6 @@ on:
default: "false" default: "false"
env: env:
GO_VERSION: "1.18.5" # for non sandboxed e2e tests
DESTDIR: "./bin" DESTDIR: "./bin"
DOCKER_CLI_VERSION: "20.10.17" DOCKER_CLI_VERSION: "20.10.17"
@ -143,7 +142,8 @@ jobs:
name: Set up Go name: Set up Go
uses: actions/setup-go@v3 uses: actions/setup-go@v3
with: with:
go-version: ${{ env.GO_VERSION }} go-version-file: 'go.mod'
check-latest: true
cache: true cache: true
- -
name: Setup docker CLI name: Setup docker CLI

View File

@ -5,7 +5,6 @@ linters:
enable-all: false enable-all: false
disable-all: true disable-all: true
enable: enable:
- deadcode
- depguard - depguard
- errcheck - errcheck
- gocritic - gocritic
@ -21,13 +20,15 @@ linters:
- nakedret - nakedret
- nolintlint - nolintlint
- staticcheck - staticcheck
- structcheck
- typecheck - typecheck
- unconvert - unconvert
- unparam - unparam
- unused - unused
- varcheck
linters-settings: linters-settings:
revive:
rules:
- name: package-comments
disabled: true
depguard: depguard:
list-type: denylist list-type: denylist
include-go-root: true include-go-root: true

View File

@ -15,9 +15,9 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
ARG GO_VERSION=1.18.5 ARG GO_VERSION=1.19.1
ARG XX_VERSION=1.1.2 ARG XX_VERSION=1.1.2
ARG GOLANGCI_LINT_VERSION=v1.47.3 ARG GOLANGCI_LINT_VERSION=v1.49.0
ARG ADDLICENSE_VERSION=v1.0.0 ARG ADDLICENSE_VERSION=v1.0.0
ARG BUILD_TAGS="e2e,kube" ARG BUILD_TAGS="e2e,kube"

View File

@ -35,12 +35,12 @@ You can download Docker Compose binaries from the
Rename the relevant binary for your OS to `docker-compose` and copy it to `$HOME/.docker/cli-plugins` Rename the relevant binary for your OS to `docker-compose` and copy it to `$HOME/.docker/cli-plugins`
Or copy it into one of these folders for installing it system-wide: Or copy it into one of these folders to install it system-wide:
* `/usr/local/lib/docker/cli-plugins` OR `/usr/local/libexec/docker/cli-plugins` * `/usr/local/lib/docker/cli-plugins` OR `/usr/local/libexec/docker/cli-plugins`
* `/usr/lib/docker/cli-plugins` OR `/usr/libexec/docker/cli-plugins` * `/usr/lib/docker/cli-plugins` OR `/usr/libexec/docker/cli-plugins`
(might require to make the downloaded file executable with `chmod +x`) (might require making the downloaded file executable with `chmod +x`)
Quick Start Quick Start

View File

@ -25,6 +25,7 @@ import (
"strings" "strings"
"syscall" "syscall"
cnabgodocker "github.com/cnabio/cnab-go/driver/docker"
"github.com/compose-spec/compose-go/cli" "github.com/compose-spec/compose-go/cli"
"github.com/compose-spec/compose-go/types" "github.com/compose-spec/compose-go/types"
composegoutils "github.com/compose-spec/compose-go/utils" composegoutils "github.com/compose-spec/compose-go/utils"
@ -32,6 +33,7 @@ import (
dockercli "github.com/docker/cli/cli" dockercli "github.com/docker/cli/cli"
"github.com/docker/cli/cli-plugins/manager" "github.com/docker/cli/cli-plugins/manager"
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
"github.com/docker/docker/client"
"github.com/morikuni/aec" "github.com/morikuni/aec"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -291,6 +293,16 @@ func RootCommand(dockerCli command.Cli, backend api.Service) *cobra.Command {
if err != nil { if err != nil {
return err return err
} }
// Reset DockerCli and APIClient to get possible `DOCKER_HOST` and/or `DOCKER_CONTEXT` loaded from environment file.
err = dockerCli.Apply(func(cli *command.DockerCli) error {
return cli.Initialize(cnabgodocker.BuildDockerClientOptions(),
command.WithInitializeClient(func(_ *command.DockerCli) (client.APIClient, error) {
return nil, nil
}))
})
if err != nil {
return err
}
parent := cmd.Root() parent := cmd.Root()
if parent != nil { if parent != nil {
parentPrerun := parent.PersistentPreRunE parentPrerun := parent.PersistentPreRunE

View File

@ -18,6 +18,7 @@ package compose
import ( import (
"bufio" "bufio"
"bytes"
"context" "context"
"fmt" "fmt"
"io" "io"
@ -112,7 +113,7 @@ func convertCommand(p *projectOptions, backend api.Service) *cobra.Command {
} }
func runConvert(ctx context.Context, backend api.Service, opts convertOptions, services []string) error { func runConvert(ctx context.Context, backend api.Service, opts convertOptions, services []string) error {
var json []byte var content []byte
project, err := opts.toProject(services, project, err := opts.toProject(services,
cli.WithInterpolation(!opts.noInterpolate), cli.WithInterpolation(!opts.noInterpolate),
cli.WithResolvedPaths(true), cli.WithResolvedPaths(true),
@ -136,7 +137,7 @@ func runConvert(ctx context.Context, backend api.Service, opts convertOptions, s
} }
} }
json, err = backend.Convert(ctx, project, api.ConvertOptions{ content, err = backend.Convert(ctx, project, api.ConvertOptions{
Format: opts.Format, Format: opts.Format,
Output: opts.Output, Output: opts.Output,
}) })
@ -144,19 +145,23 @@ func runConvert(ctx context.Context, backend api.Service, opts convertOptions, s
return err return err
} }
if !opts.noInterpolate {
content = escapeDollarSign(content)
}
if opts.quiet { if opts.quiet {
return nil return nil
} }
var out io.Writer = os.Stdout var out io.Writer = os.Stdout
if opts.Output != "" && len(json) > 0 { if opts.Output != "" && len(content) > 0 {
file, err := os.Create(opts.Output) file, err := os.Create(opts.Output)
if err != nil { if err != nil {
return err return err
} }
out = bufio.NewWriter(file) out = bufio.NewWriter(file)
} }
_, err = fmt.Fprint(out, string(json)) _, err = fmt.Fprint(out, string(content))
return err return err
} }
@ -237,3 +242,9 @@ func runConfigImages(opts convertOptions, services []string) error {
} }
return nil return nil
} }
func escapeDollarSign(marshal []byte) []byte {
dollar := []byte{'$'}
escDollar := []byte{'$', '$'}
return bytes.ReplaceAll(marshal, dollar, escDollar)
}

View File

@ -63,12 +63,13 @@ func logsCommand(p *projectOptions, backend api.Service) *cobra.Command {
} }
func runLogs(ctx context.Context, backend api.Service, opts logsOptions, services []string) error { func runLogs(ctx context.Context, backend api.Service, opts logsOptions, services []string) error {
projectName, err := opts.toProjectName() project, name, err := opts.projectOrName()
if err != nil { if err != nil {
return err return err
} }
consumer := formatter.NewLogConsumer(ctx, os.Stdout, !opts.noColor, !opts.noPrefix) consumer := formatter.NewLogConsumer(ctx, os.Stdout, !opts.noColor, !opts.noPrefix)
return backend.Logs(ctx, projectName, consumer, api.LogOptions{ return backend.Logs(ctx, name, consumer, api.LogOptions{
Project: project,
Services: services, Services: services,
Follow: opts.follow, Follow: opts.follow,
Tail: opts.tail, Tail: opts.tail,

View File

@ -51,5 +51,6 @@ func runStart(ctx context.Context, backend api.Service, opts startOptions, servi
return backend.Start(ctx, name, api.StartOptions{ return backend.Start(ctx, name, api.StartOptions{
AttachTo: services, AttachTo: services,
Project: project, Project: project,
Services: services,
}) })
} }

35
cmd/compose/tracing.go Normal file
View File

@ -0,0 +1,35 @@
/*
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 (
"github.com/moby/buildkit/util/tracing/detect"
"go.opentelemetry.io/otel"
_ "github.com/moby/buildkit/util/tracing/detect/delegated" //nolint:blank-imports
_ "github.com/moby/buildkit/util/tracing/env" //nolint:blank-imports
)
func init() {
detect.ServiceName = "compose"
// do not log tracing errors to stdio
otel.SetErrorHandler(skipErrors{})
}
type skipErrors struct{}
func (skipErrors) Handle(err error) {}

View File

@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
variable "GO_VERSION" { variable "GO_VERSION" {
default = "1.18.5" default = "1.19.1"
} }
variable "BUILD_TAGS" { variable "BUILD_TAGS" {

32
go.mod
View File

@ -1,15 +1,16 @@
module github.com/docker/compose/v2 module github.com/docker/compose/v2
go 1.18 go 1.19
require ( require (
github.com/AlecAivazis/survey/v2 v2.3.5 github.com/AlecAivazis/survey/v2 v2.3.6
github.com/buger/goterm v1.0.4 github.com/buger/goterm v1.0.4
github.com/cnabio/cnab-go v0.24.1-0.20220907172316-1ca5c8721bf7
github.com/cnabio/cnab-to-oci v0.3.7 github.com/cnabio/cnab-to-oci v0.3.7
github.com/compose-spec/compose-go v1.5.0 github.com/compose-spec/compose-go v1.5.0
github.com/containerd/console v1.0.3 github.com/containerd/console v1.0.3
github.com/containerd/containerd v1.6.8 github.com/containerd/containerd v1.6.8
github.com/distribution/distribution/v3 v3.0.0-20220729163034-26163d82560f github.com/distribution/distribution/v3 v3.0.0-20220902125104-0122d7ddaec0
github.com/docker/buildx v0.8.2 // when updating, also update the replace rules accordingly github.com/docker/buildx v0.8.2 // when updating, also update the replace rules accordingly
github.com/docker/cli v20.10.17+incompatible github.com/docker/cli v20.10.17+incompatible
github.com/docker/cli-docs-tool v0.5.0 github.com/docker/cli-docs-tool v0.5.0
@ -22,7 +23,7 @@ require (
github.com/mattn/go-isatty v0.0.16 github.com/mattn/go-isatty v0.0.16
github.com/mattn/go-shellwords v1.0.12 github.com/mattn/go-shellwords v1.0.12
github.com/moby/buildkit v0.10.4 github.com/moby/buildkit v0.10.4
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae
github.com/morikuni/aec v1.0.0 github.com/morikuni/aec v1.0.0
github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799
@ -32,7 +33,7 @@ require (
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.8.0 github.com/stretchr/testify v1.8.0
github.com/theupdateframework/notary v0.7.0 github.com/theupdateframework/notary v0.7.0
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
gotest.tools v2.2.0+incompatible gotest.tools v2.2.0+incompatible
gotest.tools/v3 v3.3.0 gotest.tools/v3 v3.3.0
@ -44,7 +45,6 @@ require (
github.com/Microsoft/go-winio v0.5.2 // indirect github.com/Microsoft/go-winio v0.5.2 // indirect
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cnabio/cnab-go v0.23.4 // indirect
github.com/containerd/continuity v0.2.3-0.20220330195504-d132b287edc8 // indirect github.com/containerd/continuity v0.2.3-0.20220330195504-d132b287edc8 // indirect
github.com/containerd/ttrpc v1.1.0 // indirect github.com/containerd/ttrpc v1.1.0 // indirect
github.com/containerd/typeurl v1.0.2 // indirect github.com/containerd/typeurl v1.0.2 // indirect
@ -55,7 +55,7 @@ require (
github.com/docker/go-metrics v0.0.1 // indirect github.com/docker/go-metrics v0.0.1 // indirect
github.com/felixge/httpsnoop v1.0.2 // indirect github.com/felixge/httpsnoop v1.0.2 // indirect
github.com/fvbommel/sortorder v1.0.2 // indirect github.com/fvbommel/sortorder v1.0.2 // indirect
github.com/go-logr/logr v1.2.2 // indirect github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect
github.com/gofrs/flock v0.8.0 // indirect github.com/gofrs/flock v0.8.0 // indirect
github.com/gogo/googleapis v1.4.1 // indirect github.com/gogo/googleapis v1.4.1 // indirect
@ -101,15 +101,15 @@ require (
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.29.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.29.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.29.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.29.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.29.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.29.0 // indirect
go.opentelemetry.io/otel v1.4.1 // indirect go.opentelemetry.io/otel v1.10.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.4.1 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.4.1 // indirect
go.opentelemetry.io/otel/internal/metric v0.27.0 // indirect go.opentelemetry.io/otel/internal/metric v0.27.0 // indirect
go.opentelemetry.io/otel/metric v0.27.0 // indirect go.opentelemetry.io/otel/metric v0.27.0 // indirect
go.opentelemetry.io/otel/sdk v1.4.1 // indirect go.opentelemetry.io/otel/sdk v1.4.1 // indirect
go.opentelemetry.io/otel/trace v1.4.1 // indirect go.opentelemetry.io/otel/trace v1.10.0 // indirect
go.opentelemetry.io/proto/otlp v0.12.0 // indirect go.opentelemetry.io/proto/otlp v0.12.0 // indirect
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect
golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect golang.org/x/net v0.0.0-20220906165146-f3363e06e74c // indirect
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
@ -122,7 +122,7 @@ require (
gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apimachinery v0.24.1 // indirect; see replace for the actual version used k8s.io/apimachinery v0.24.1 // indirect; see replace for the actual version used
k8s.io/client-go v0.24.1 // indirect; see replace for the actual version used k8s.io/client-go v0.24.1 // see replace for the actual version used
k8s.io/klog/v2 v2.60.1 // indirect k8s.io/klog/v2 v2.60.1 // indirect
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect
@ -130,9 +130,19 @@ require (
) )
require ( require (
github.com/cenkalti/backoff/v4 v4.1.2 // indirect
github.com/cyberphone/json-canonicalization v0.0.0-20210303052042-6bc126869bf4 // indirect github.com/cyberphone/json-canonicalization v0.0.0-20210303052042-6bc126869bf4 // indirect
github.com/googleapis/gnostic v0.5.5 // indirect
github.com/mitchellh/copystructure v1.0.0 // indirect
github.com/mitchellh/reflectwalk v1.0.0 // indirect
github.com/moby/spdystream v0.2.0 // indirect
github.com/serialx/hashring v0.0.0-20190422032157-8b2912629002 // indirect
github.com/zmap/zcrypto v0.0.0-20220605182715-4dfcec6e9a8c // indirect github.com/zmap/zcrypto v0.0.0-20220605182715-4dfcec6e9a8c // indirect
github.com/zmap/zlint v1.1.0 // indirect github.com/zmap/zlint v1.1.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.4.1 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.4.1 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.4.1 // indirect
k8s.io/api v0.24.1 // indirect
) )
replace ( replace (

45
go.sum
View File

@ -56,8 +56,8 @@ git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGy
git.apache.org/thrift.git v0.12.0/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= git.apache.org/thrift.git v0.12.0/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8/go.mod h1:CzsSbkDixRphAF5hS6wbMKq0eI6ccJRb7/A0M6JBnwg= github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8/go.mod h1:CzsSbkDixRphAF5hS6wbMKq0eI6ccJRb7/A0M6JBnwg=
github.com/AkihiroSuda/containerd-fuse-overlayfs v1.0.0/go.mod h1:0mMDvQFeLbbn1Wy8P2j3hwFhqBq+FKn8OZPno8WLmp8= github.com/AkihiroSuda/containerd-fuse-overlayfs v1.0.0/go.mod h1:0mMDvQFeLbbn1Wy8P2j3hwFhqBq+FKn8OZPno8WLmp8=
github.com/AlecAivazis/survey/v2 v2.3.5 h1:A8cYupsAZkjaUmhtTYv3sSqc7LO5mp1XDfqe5E/9wRQ= github.com/AlecAivazis/survey/v2 v2.3.6 h1:NvTuVHISgTHEHeBFqt6BHOe4Ny/NwGZr7w+F8S9ziyw=
github.com/AlecAivazis/survey/v2 v2.3.5/go.mod h1:4AuI9b7RjAR+G7v9+C4YSlX/YL3K3cWNXgWXOhllqvI= github.com/AlecAivazis/survey/v2 v2.3.6/go.mod h1:4AuI9b7RjAR+G7v9+C4YSlX/YL3K3cWNXgWXOhllqvI=
github.com/Azure/azure-amqp-common-go/v2 v2.1.0/go.mod h1:R8rea+gJRuJR6QxTir/XuEd+YuKoUiazDC/N96FiDEU= github.com/Azure/azure-amqp-common-go/v2 v2.1.0/go.mod h1:R8rea+gJRuJR6QxTir/XuEd+YuKoUiazDC/N96FiDEU=
github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4=
github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc=
@ -246,6 +246,7 @@ github.com/campoy/unique v0.0.0-20180121183637-88950e537e7e/go.mod h1:9IOqJGCPMS
github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e/go.mod h1:oDpT4efm8tSYHXV5tHSdRvBet/b/QzxZ+XyyPehvm3A= github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e/go.mod h1:oDpT4efm8tSYHXV5tHSdRvBet/b/QzxZ+XyyPehvm3A=
github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo=
github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@ -268,8 +269,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA= github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
github.com/cloudflare/cfssl v0.0.0-20181213083726-b94e044bb51e/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA= github.com/cloudflare/cfssl v0.0.0-20181213083726-b94e044bb51e/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
github.com/cloudflare/cfssl v1.4.1 h1:vScfU2DrIUI9VPHBVeeAQ0q5A+9yshO1Gz+3QoUQiKw= github.com/cloudflare/cfssl v1.4.1 h1:vScfU2DrIUI9VPHBVeeAQ0q5A+9yshO1Gz+3QoUQiKw=
github.com/cnabio/cnab-go v0.23.4 h1:jplQcSnvFyQlD6swiqL3BmqRnhbnS+lc/EKdBLH9E80= github.com/cnabio/cnab-go v0.24.1-0.20220907172316-1ca5c8721bf7 h1:6cETeoyahKaH4hNShuB4KUqkTdjLVKEpTakHW5bpDW8=
github.com/cnabio/cnab-go v0.23.4/go.mod h1:9EmgHR51LFqQStzaC+xHPJlkD4OPsF6Ev5Y8e/YHEns= github.com/cnabio/cnab-go v0.24.1-0.20220907172316-1ca5c8721bf7/go.mod h1:Zm0HTH8xxzinB64SXm7KFSna7DEN0ZjZwrRwZpfgChU=
github.com/cnabio/cnab-to-oci v0.3.7 h1:wA2AG3HQMaJZhWlr3zsfVoa2m5B1R/SP+YcoFuNfP9o= github.com/cnabio/cnab-to-oci v0.3.7 h1:wA2AG3HQMaJZhWlr3zsfVoa2m5B1R/SP+YcoFuNfP9o=
github.com/cnabio/cnab-to-oci v0.3.7/go.mod h1:AvVNl0Hh3VBk1zqeLdyE5S3bTQ5EsZPPF4mUUJYyy1Y= github.com/cnabio/cnab-to-oci v0.3.7/go.mod h1:AvVNl0Hh3VBk1zqeLdyE5S3bTQ5EsZPPF4mUUJYyy1Y=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
@ -449,8 +450,8 @@ github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
github.com/distribution/distribution/v3 v3.0.0-20210316161203-a01c71e2477e/go.mod h1:xpWTC2KnJMiDLkoawhsPQcXjvwATEBcbq0xevG2YR9M= github.com/distribution/distribution/v3 v3.0.0-20210316161203-a01c71e2477e/go.mod h1:xpWTC2KnJMiDLkoawhsPQcXjvwATEBcbq0xevG2YR9M=
github.com/distribution/distribution/v3 v3.0.0-20220729163034-26163d82560f h1:3NCYdjXycNd/Xn/iICZzmxkiDX1e1cjTHjbMAz+wRVk= github.com/distribution/distribution/v3 v3.0.0-20220902125104-0122d7ddaec0 h1:0UuPq7m6stSY6at1v5PLo0zzYTpailcwjhmkJpgnGBY=
github.com/distribution/distribution/v3 v3.0.0-20220729163034-26163d82560f/go.mod h1:28YO/VJk9/64+sTGNuYaBjWxrXTPrj0C0XmgTIOjxX4= github.com/distribution/distribution/v3 v3.0.0-20220902125104-0122d7ddaec0/go.mod h1:28YO/VJk9/64+sTGNuYaBjWxrXTPrj0C0XmgTIOjxX4=
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
github.com/docker/buildx v0.8.2 h1:dsd3F0hhmUydFX/KFrvbK81JvlTA4T3Iy0lwDJt4PsU= github.com/docker/buildx v0.8.2 h1:dsd3F0hhmUydFX/KFrvbK81JvlTA4T3Iy0lwDJt4PsU=
github.com/docker/buildx v0.8.2/go.mod h1:5sMOfNwOmO2jy/MxBL4ySk2LoLIG1tQFu2EU8wbKa34= github.com/docker/buildx v0.8.2/go.mod h1:5sMOfNwOmO2jy/MxBL4ySk2LoLIG1tQFu2EU8wbKa34=
@ -498,6 +499,7 @@ github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5m
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484 h1:pEtiCjIXx3RvGjlUJuCNxNOw0MNblyR9Wi+vJGBFh+8=
github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
@ -556,8 +558,9 @@ github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTg
github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.2 h1:ahHml/yUpnlb96Rp8HCvtYVPY8ZYpxq3g7UYchIYwbs=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI= github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
@ -761,6 +764,7 @@ github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsC
github.com/googleapis/gnostic v0.2.2/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.2.2/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw=
github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA=
github.com/gookit/color v1.2.4/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg= github.com/gookit/color v1.2.4/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg=
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
@ -998,6 +1002,8 @@ github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk= github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk=
@ -1016,12 +1022,15 @@ github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/moby/buildkit v0.8.1/go.mod h1:/kyU1hKy/aYCuP39GZA9MaKioovHku57N6cqlKZIaiQ= github.com/moby/buildkit v0.8.1/go.mod h1:/kyU1hKy/aYCuP39GZA9MaKioovHku57N6cqlKZIaiQ=
github.com/moby/buildkit v0.10.1-0.20220403220257-10e6f94bf90d/go.mod h1:WvwAZv8aRScHkqc/+X46cRC2CKMKpqcaX+pRvUTtPes= github.com/moby/buildkit v0.10.1-0.20220403220257-10e6f94bf90d/go.mod h1:WvwAZv8aRScHkqc/+X46cRC2CKMKpqcaX+pRvUTtPes=
github.com/moby/buildkit v0.10.4 h1:FvC+buO8isGpUFZ1abdSLdGHZVqg9sqI4BbFL8tlzP4= github.com/moby/buildkit v0.10.4 h1:FvC+buO8isGpUFZ1abdSLdGHZVqg9sqI4BbFL8tlzP4=
github.com/moby/buildkit v0.10.4/go.mod h1:Yajz9vt1Zw5q9Pp4pdb3TCSUXJBIroIQGQ3TTs/sLug= github.com/moby/buildkit v0.10.4/go.mod h1:Yajz9vt1Zw5q9Pp4pdb3TCSUXJBIroIQGQ3TTs/sLug=
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
github.com/moby/sys/mount v0.1.0/go.mod h1:FVQFLDRWwyBjDTBNQXDlWnSFREqOo3OKX9aqhmeoo74= github.com/moby/sys/mount v0.1.0/go.mod h1:FVQFLDRWwyBjDTBNQXDlWnSFREqOo3OKX9aqhmeoo74=
github.com/moby/sys/mount v0.1.1/go.mod h1:FVQFLDRWwyBjDTBNQXDlWnSFREqOo3OKX9aqhmeoo74= github.com/moby/sys/mount v0.1.1/go.mod h1:FVQFLDRWwyBjDTBNQXDlWnSFREqOo3OKX9aqhmeoo74=
@ -1041,8 +1050,9 @@ github.com/moby/sys/symlink v0.2.0/go.mod h1:7uZVF2dqJjG/NsClqul95CqKOBRQyYSNnJ6
github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo=
github.com/moby/term v0.0.0-20200915141129-7f0af18e79f2/go.mod h1:TjQg8pa4iejrUrjiz0MCtMV38jdMNW4doKSiBrEvCQQ= github.com/moby/term v0.0.0-20200915141129-7f0af18e79f2/go.mod h1:TjQg8pa4iejrUrjiz0MCtMV38jdMNW4doKSiBrEvCQQ=
github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A=
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc=
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=
github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae h1:O4SWKdcHVCvYqyDV+9CJA1fcDN2L11Bule0iFy3YlAI=
github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@ -1235,6 +1245,7 @@ github.com/securego/gosec v0.0.0-20200401082031-e946c8c39989/go.mod h1:i9l/TNj+y
github.com/securego/gosec/v2 v2.3.0/go.mod h1:UzeVyUXbxukhLeHKV3VVqo7HdoQR9MrRfFmZYotn8ME= github.com/securego/gosec/v2 v2.3.0/go.mod h1:UzeVyUXbxukhLeHKV3VVqo7HdoQR9MrRfFmZYotn8ME=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/serialx/hashring v0.0.0-20190422032157-8b2912629002 h1:ka9QPuQg2u4LGipiZGsgkg3rJCo4iIUCy75FddM0GRQ=
github.com/serialx/hashring v0.0.0-20190422032157-8b2912629002/go.mod h1:/yeG0My1xr/u+HZrFQ1tOQQQQrOawfyMUH13ai5brBc= github.com/serialx/hashring v0.0.0-20190422032157-8b2912629002/go.mod h1:/yeG0My1xr/u+HZrFQ1tOQQQQrOawfyMUH13ai5brBc=
github.com/shirou/gopsutil v0.0.0-20190901111213-e4ec7b275ada/go.mod h1:WWnYX4lzhCH5h/3YBfyVA3VbLYjlMZZAQcW9ojMexNc= github.com/shirou/gopsutil v0.0.0-20190901111213-e4ec7b275ada/go.mod h1:WWnYX4lzhCH5h/3YBfyVA3VbLYjlMZZAQcW9ojMexNc=
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
@ -1460,18 +1471,22 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.29.0/go.mod h1:
go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo=
go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs= go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs=
go.opentelemetry.io/otel v1.4.0/go.mod h1:jeAqMFKy2uLIxCtKxoFj0FAL5zAPKQagc3+GtBWakzk= go.opentelemetry.io/otel v1.4.0/go.mod h1:jeAqMFKy2uLIxCtKxoFj0FAL5zAPKQagc3+GtBWakzk=
go.opentelemetry.io/otel v1.4.1 h1:QbINgGDDcoQUoMJa2mMaWno49lja9sHwp6aoa2n3a4g=
go.opentelemetry.io/otel v1.4.1/go.mod h1:StM6F/0fSwpd8dKWDCdRr7uRvEPYdW0hBSlbdTiUde4= go.opentelemetry.io/otel v1.4.1/go.mod h1:StM6F/0fSwpd8dKWDCdRr7uRvEPYdW0hBSlbdTiUde4=
go.opentelemetry.io/otel v1.10.0 h1:Y7DTJMR6zs1xkS/upamJYk0SxxN4C9AqRd77jmZnyY4=
go.opentelemetry.io/otel v1.10.0/go.mod h1:NbvWjCthWHKBEUMpf0/v8ZRZlni86PpGFEMA9pnQSnQ=
go.opentelemetry.io/otel/exporters/jaeger v1.4.1/go.mod h1:ZW7vkOu9nC1CxsD8bHNHCia5JUbwP39vxgd1q4Z5rCI= go.opentelemetry.io/otel/exporters/jaeger v1.4.1/go.mod h1:ZW7vkOu9nC1CxsD8bHNHCia5JUbwP39vxgd1q4Z5rCI=
go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM=
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4= go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4=
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.4.1 h1:imIM3vRDMyZK1ypQlQlO+brE22I9lRhJsBDXpDWjlz8=
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.4.1/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4= go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.4.1/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0/go.mod h1:hO1KLR7jcKaDDKDkvI9dP/FIhpmna5lkqPUQdEjFAM8= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0/go.mod h1:hO1KLR7jcKaDDKDkvI9dP/FIhpmna5lkqPUQdEjFAM8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.4.1 h1:WPpPsAAs8I2rA47v5u0558meKmmwm1Dj99ZbqCV8sZ8= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.4.1 h1:WPpPsAAs8I2rA47v5u0558meKmmwm1Dj99ZbqCV8sZ8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.4.1/go.mod h1:o5RW5o2pKpJLD5dNTCmjF1DorYwMeFJmb/rKr5sLaa8= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.4.1/go.mod h1:o5RW5o2pKpJLD5dNTCmjF1DorYwMeFJmb/rKr5sLaa8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0/go.mod h1:keUU7UfnwWTWpJ+FWnyqmogPa82nuU5VUANFq49hlMY= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0/go.mod h1:keUU7UfnwWTWpJ+FWnyqmogPa82nuU5VUANFq49hlMY=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.4.1 h1:AxqDiGk8CorEXStMDZF5Hz9vo9Z7ZZ+I5m8JRl/ko40=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.4.1/go.mod h1:c6E4V3/U+miqjs/8l950wggHGL1qzlp0Ypj9xoGrPqo= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.4.1/go.mod h1:c6E4V3/U+miqjs/8l950wggHGL1qzlp0Ypj9xoGrPqo=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.3.0/go.mod h1:QNX1aly8ehqqX1LEa6YniTU7VY9I6R3X/oPxhGdTceE= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.3.0/go.mod h1:QNX1aly8ehqqX1LEa6YniTU7VY9I6R3X/oPxhGdTceE=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.4.1 h1:8qOago/OqoFclMUUj/184tZyRdDZFpcejSjbk5Jrl6Y=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.4.1/go.mod h1:VwYo0Hak6Efuy0TXsZs8o1hnV3dHDPNtDbycG0hI8+M= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.4.1/go.mod h1:VwYo0Hak6Efuy0TXsZs8o1hnV3dHDPNtDbycG0hI8+M=
go.opentelemetry.io/otel/internal/metric v0.27.0 h1:9dAVGAfFiiEq5NVB9FUJ5et+btbDQAUIJehJ+ikyryk= go.opentelemetry.io/otel/internal/metric v0.27.0 h1:9dAVGAfFiiEq5NVB9FUJ5et+btbDQAUIJehJ+ikyryk=
go.opentelemetry.io/otel/internal/metric v0.27.0/go.mod h1:n1CVxRqKqYZtqyTh9U/onvKapPGv7y/rpyOTI+LFNzw= go.opentelemetry.io/otel/internal/metric v0.27.0/go.mod h1:n1CVxRqKqYZtqyTh9U/onvKapPGv7y/rpyOTI+LFNzw=
@ -1488,8 +1503,9 @@ go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4
go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw=
go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk= go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk=
go.opentelemetry.io/otel/trace v1.4.0/go.mod h1:uc3eRsqDfWs9R7b92xbQbU42/eTNz4N+gLP8qJCi4aE= go.opentelemetry.io/otel/trace v1.4.0/go.mod h1:uc3eRsqDfWs9R7b92xbQbU42/eTNz4N+gLP8qJCi4aE=
go.opentelemetry.io/otel/trace v1.4.1 h1:O+16qcdTrT7zxv2J6GejTPFinSwA++cYerC5iSiF8EQ=
go.opentelemetry.io/otel/trace v1.4.1/go.mod h1:iYEVbroFCNut9QkwEczV9vMRPHNKSSwYZjulEtsmhFc= go.opentelemetry.io/otel/trace v1.4.1/go.mod h1:iYEVbroFCNut9QkwEczV9vMRPHNKSSwYZjulEtsmhFc=
go.opentelemetry.io/otel/trace v1.10.0 h1:npQMbR8o7mum8uF95yFbOEJffhs1sbCOfDh8zAJiH5E=
go.opentelemetry.io/otel/trace v1.10.0/go.mod h1:Sij3YYczqAdz+EhmGhE6TpTxUO5/F/AzrK+kxfGqySM=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ= go.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ=
go.opentelemetry.io/proto/otlp v0.12.0 h1:CMJ/3Wp7iOWES+CYLfnBv+DVmPbB+kmy9PJ92XvlR6c= go.opentelemetry.io/proto/otlp v0.12.0 h1:CMJ/3Wp7iOWES+CYLfnBv+DVmPbB+kmy9PJ92XvlR6c=
@ -1498,6 +1514,7 @@ go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
@ -1644,8 +1661,8 @@ golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc= golang.org/x/net v0.0.0-20220906165146-f3363e06e74c h1:yKufUcDwucU5urd+50/Opbt4AYpqthk7wHpHok8f1lo=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/oauth2 v0.0.0-20180724155351-3d292e4d0cdc/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180724155351-3d292e4d0cdc/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -1679,8 +1696,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=

View File

@ -129,6 +129,8 @@ type StartOptions struct {
ExitCodeFrom string ExitCodeFrom string
// Wait won't return until containers reached the running|healthy state // Wait won't return until containers reached the running|healthy state
Wait bool Wait bool
// Services passed in the command line to be started
Services []string
} }
// RestartOptions group options of the Restart API // RestartOptions group options of the Restart API
@ -378,6 +380,7 @@ type ServiceStatus struct {
// LogOptions defines optional parameters for the `Log` API // LogOptions defines optional parameters for the `Log` API
type LogOptions struct { type LogOptions struct {
Project *types.Project
Services []string Services []string
Tail string Tail string
Since string Since string
@ -429,7 +432,7 @@ type Stack struct {
// LogConsumer is a callback to process log messages from services // LogConsumer is a callback to process log messages from services
type LogConsumer interface { type LogConsumer interface {
Log(service, container, message string) Log(containerName, service, message string)
Status(container, msg string) Status(container, msg string)
Register(container string) Register(container string)
} }
@ -439,7 +442,11 @@ type ContainerEventListener func(event ContainerEvent)
// ContainerEvent notify an event has been collected on source container implementing Service // ContainerEvent notify an event has been collected on source container implementing Service
type ContainerEvent struct { type ContainerEvent struct {
Type int Type int
// Container is the name of the container _without the project prefix_.
//
// This is only suitable for display purposes within Compose, as it's
// not guaranteed to be unique across services.
Container string Container string
Service string Service string
Line string Line string

View File

@ -81,6 +81,12 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
Attrs: map[string]string{"ref": image}, Attrs: map[string]string{"ref": image},
}) })
} }
if len(buildOptions.Platforms) > 1 {
buildOptions.Exports = []bclient.ExportEntry{{
Type: "image",
Attrs: map[string]string{},
}}
}
opts[imageName] = buildOptions opts[imageName] = buildOptions
} }
@ -161,6 +167,15 @@ func (s *composeService) getBuildOptions(project *types.Project, images map[stri
if err != nil { if err != nil {
return nil, err return nil, err
} }
opt.Exports = []bclient.ExportEntry{{
Type: "docker",
Attrs: map[string]string{
"load": "true",
},
}}
if opt.Platforms, err = useDockerDefaultPlatform(project, service.Build.Platforms); err != nil {
opt.Platforms = []specs.Platform{}
}
opts[imageName] = opt opts[imageName] = opt
continue continue
} }
@ -204,7 +219,7 @@ func (s *composeService) doBuild(ctx context.Context, project *types.Project, op
if buildkitEnabled, err := s.dockerCli.BuildKitEnabled(); err != nil || !buildkitEnabled { if buildkitEnabled, err := s.dockerCli.BuildKitEnabled(); err != nil || !buildkitEnabled {
return s.doBuildClassic(ctx, project, opts) return s.doBuildClassic(ctx, project, opts)
} }
return s.doBuildBuildkit(ctx, project, opts, mode) return s.doBuildBuildkit(ctx, opts, mode)
} }
func (s *composeService) toBuildOptions(project *types.Project, service types.ServiceConfig, imageTag string, sshKeys []types.SSHKey) (build.Options, error) { func (s *composeService) toBuildOptions(project *types.Project, service types.ServiceConfig, imageTag string, sshKeys []types.SSHKey) (build.Options, error) {
@ -213,20 +228,9 @@ func (s *composeService) toBuildOptions(project *types.Project, service types.Se
buildArgs := flatten(service.Build.Args.Resolve(envResolver(project.Environment))) buildArgs := flatten(service.Build.Args.Resolve(envResolver(project.Environment)))
var plats []specs.Platform plats, err := addPlatforms(project, service)
if platform, ok := project.Environment["DOCKER_DEFAULT_PLATFORM"]; ok { if err != nil {
p, err := platforms.Parse(platform) return build.Options{}, err
if err != nil {
return build.Options{}, err
}
plats = append(plats, p)
}
if service.Platform != "" {
p, err := platforms.Parse(service.Platform)
if err != nil {
return build.Options{}, err
}
plats = append(plats, p)
} }
cacheFrom, err := buildflags.ParseCacheEntry(service.Build.CacheFrom) cacheFrom, err := buildflags.ParseCacheEntry(service.Build.CacheFrom)
@ -352,6 +356,28 @@ func addSecretsConfig(project *types.Project, service types.ServiceConfig) (sess
return secretsprovider.NewSecretProvider(store), nil return secretsprovider.NewSecretProvider(store), nil
} }
func addPlatforms(project *types.Project, service types.ServiceConfig) ([]specs.Platform, error) {
plats, err := useDockerDefaultPlatform(project, service.Build.Platforms)
if err != nil {
return nil, err
}
if service.Platform != "" && !utils.StringContains(service.Build.Platforms, service.Platform) {
return nil, fmt.Errorf("service.platform should be part of the service.build.platforms: %q", service.Platform)
}
for _, buildPlatform := range service.Build.Platforms {
p, err := platforms.Parse(buildPlatform)
if err != nil {
return nil, err
}
if !utils.Contains(plats, p) {
plats = append(plats, p)
}
}
return plats, nil
}
func getImageBuildLabels(project *types.Project, service types.ServiceConfig) types.Labels { func getImageBuildLabels(project *types.Project, service types.ServiceConfig) types.Labels {
ret := make(types.Labels) ret := make(types.Labels)
if service.Build != nil { if service.Build != nil {
@ -365,3 +391,18 @@ func getImageBuildLabels(project *types.Project, service types.ServiceConfig) ty
ret.Add(api.ServiceLabel, service.Name) ret.Add(api.ServiceLabel, service.Name)
return ret return ret
} }
func useDockerDefaultPlatform(project *types.Project, platformList types.StringList) ([]specs.Platform, error) {
var plats []specs.Platform
if platform, ok := project.Environment["DOCKER_DEFAULT_PLATFORM"]; ok {
if !utils.StringContains(platformList, platform) {
return nil, fmt.Errorf("the DOCKER_DEFAULT_PLATFORM value should be part of the service.build.platforms: %q", platform)
}
p, err := platforms.Parse(platform)
if err != nil {
return nil, err
}
plats = append(plats, p)
}
return plats, nil
}

View File

@ -18,29 +18,36 @@ package compose
import ( import (
"context" "context"
"fmt"
"net/url"
"os" "os"
"path/filepath" "path/filepath"
"strings"
ctxkube "github.com/docker/buildx/driver/kubernetes/context"
"github.com/docker/buildx/store"
"github.com/docker/buildx/store/storeutil"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/context/docker"
ctxstore "github.com/docker/cli/cli/context/store"
dockerclient "github.com/docker/docker/client"
"github.com/sirupsen/logrus"
"golang.org/x/sync/errgroup"
"k8s.io/client-go/tools/clientcmd"
"github.com/compose-spec/compose-go/types"
"github.com/docker/buildx/build" "github.com/docker/buildx/build"
"github.com/docker/buildx/driver" "github.com/docker/buildx/driver"
_ "github.com/docker/buildx/driver/docker" //nolint:blank-imports
_ "github.com/docker/buildx/driver/docker-container" //nolint:blank-imports
_ "github.com/docker/buildx/driver/kubernetes" //nolint:blank-imports
xprogress "github.com/docker/buildx/util/progress" xprogress "github.com/docker/buildx/util/progress"
"github.com/docker/compose/v2/pkg/api"
) )
func (s *composeService) doBuildBuildkit(ctx context.Context, project *types.Project, opts map[string]build.Options, mode string) (map[string]string, error) { func (s *composeService) doBuildBuildkit(ctx context.Context, opts map[string]build.Options, mode string) (map[string]string, error) {
const drivername = "default" dis, err := s.getDrivers(ctx)
d, err := driver.GetDriver(ctx, drivername, nil, s.apiClient(), s.configFile(), nil, nil, nil, nil, nil, project.WorkingDir)
if err != nil { if err != nil {
return nil, err return nil, err
} }
driverInfo := []build.DriverInfo{
{
Name: drivername,
Driver: d,
},
}
// Progress needs its own context that lives longer than the // Progress needs its own context that lives longer than the
// build one otherwise it won't read all the messages from // build one otherwise it won't read all the messages from
@ -49,17 +56,7 @@ func (s *composeService) doBuildBuildkit(ctx context.Context, project *types.Pro
defer cancel() defer cancel()
w := xprogress.NewPrinter(progressCtx, s.stdout(), os.Stdout, mode) w := xprogress.NewPrinter(progressCtx, s.stdout(), os.Stdout, mode)
for k := range opts { response, err := build.Build(ctx, dis, opts, &internalAPI{dockerCli: s.dockerCli}, filepath.Dir(s.configFile().Filename), w)
if opts[k].Labels == nil {
opt := opts[k]
opt.Labels = make(map[string]string)
opts[k] = opt
}
opts[k].Labels[api.ImageBuilderLabel] = "buildkit"
}
// We rely on buildx "docker" builder integrated in docker engine, so don't need a DockerAPI here
response, err := build.Build(ctx, driverInfo, opts, nil, filepath.Dir(s.configFile().Filename), w)
errW := w.Wait() errW := w.Wait()
if err == nil { if err == nil {
err = errW err = errW
@ -82,3 +79,187 @@ func (s *composeService) doBuildBuildkit(ctx context.Context, project *types.Pro
return imagesBuilt, err return imagesBuilt, err
} }
func (s *composeService) getDrivers(ctx context.Context) ([]build.DriverInfo, error) { //nolint:gocyclo
txn, release, err := storeutil.GetStore(s.dockerCli)
if err != nil {
return nil, err
}
defer release()
ng, err := storeutil.GetCurrentInstance(txn, s.dockerCli)
if err != nil {
return nil, err
}
dis := make([]build.DriverInfo, len(ng.Nodes))
var f driver.Factory
if ng.Driver != "" {
factories := driver.GetFactories()
for _, fac := range factories {
if fac.Name() == ng.Driver {
f = fac
continue
}
}
if f == nil {
if f = driver.GetFactory(ng.Driver, true); f == nil {
return nil, fmt.Errorf("failed to find buildx driver %q", ng.Driver)
}
}
} else {
ep := ng.Nodes[0].Endpoint
dockerapi, err := clientForEndpoint(s.dockerCli, ep)
if err != nil {
return nil, err
}
f, err = driver.GetDefaultFactory(ctx, dockerapi, false)
if err != nil {
return nil, err
}
ng.Driver = f.Name()
}
imageopt, err := storeutil.GetImageConfig(s.dockerCli, ng)
if err != nil {
return nil, err
}
eg, _ := errgroup.WithContext(ctx)
for i, n := range ng.Nodes {
func(i int, n store.Node) {
eg.Go(func() error {
di := build.DriverInfo{
Name: n.Name,
Platform: n.Platforms,
ProxyConfig: storeutil.GetProxyConfig(s.dockerCli),
}
defer func() {
dis[i] = di
}()
dockerapi, err := clientForEndpoint(s.dockerCli, n.Endpoint)
if err != nil {
di.Err = err
return nil
}
// TODO: replace the following line with dockerclient.WithAPIVersionNegotiation option in clientForEndpoint
dockerapi.NegotiateAPIVersion(ctx)
contextStore := s.dockerCli.ContextStore()
var kcc driver.KubeClientConfig
kcc, err = configFromContext(n.Endpoint, contextStore)
if err != nil {
// err is returned if n.Endpoint is non-context name like "unix:///var/run/docker.sock".
// try again with name="default".
// FIXME: n should retain real context name.
kcc, err = configFromContext("default", contextStore)
if err != nil {
logrus.Error(err)
}
}
tryToUseKubeConfigInCluster := false
if kcc == nil {
tryToUseKubeConfigInCluster = true
} else {
if _, err := kcc.ClientConfig(); err != nil {
tryToUseKubeConfigInCluster = true
}
}
if tryToUseKubeConfigInCluster {
kccInCluster := driver.KubeClientConfigInCluster{}
if _, err := kccInCluster.ClientConfig(); err == nil {
logrus.Debug("using kube config in cluster")
kcc = kccInCluster
}
}
d, err := driver.GetDriver(ctx, "buildx_buildkit_"+n.Name, f, dockerapi, imageopt.Auth, kcc, n.Flags, n.Files, n.DriverOpts, n.Platforms, "")
if err != nil {
di.Err = err
return nil
}
di.Driver = d
di.ImageOpt = imageopt
return nil
})
}(i, n)
}
if err := eg.Wait(); err != nil {
return nil, err
}
return dis, nil
}
func clientForEndpoint(dockerCli command.Cli, name string) (dockerclient.APIClient, error) {
list, err := dockerCli.ContextStore().List()
if err != nil {
return nil, err
}
for _, l := range list {
if l.Name != name {
continue
}
dep, ok := l.Endpoints["docker"]
if !ok {
return nil, fmt.Errorf("context %q does not have a Docker endpoint", name)
}
epm, ok := dep.(docker.EndpointMeta)
if !ok {
return nil, fmt.Errorf("endpoint %q is not of type EndpointMeta, %T", dep, dep)
}
ep, err := docker.WithTLSData(dockerCli.ContextStore(), name, epm)
if err != nil {
return nil, err
}
clientOpts, err := ep.ClientOpts()
if err != nil {
return nil, err
}
return dockerclient.NewClientWithOpts(clientOpts...)
}
ep := docker.Endpoint{
EndpointMeta: docker.EndpointMeta{
Host: name,
},
}
clientOpts, err := ep.ClientOpts()
if err != nil {
return nil, err
}
return dockerclient.NewClientWithOpts(clientOpts...)
}
func configFromContext(endpointName string, s ctxstore.Reader) (clientcmd.ClientConfig, error) {
if strings.HasPrefix(endpointName, "kubernetes://") {
u, _ := url.Parse(endpointName)
if kubeconfig := u.Query().Get("kubeconfig"); kubeconfig != "" {
_ = os.Setenv(clientcmd.RecommendedConfigPathEnvVar, kubeconfig)
}
rules := clientcmd.NewDefaultClientConfigLoadingRules()
apiConfig, err := rules.Load()
if err != nil {
return nil, err
}
return clientcmd.NewDefaultClientConfig(*apiConfig, &clientcmd.ConfigOverrides{}), nil
}
return ctxkube.ConfigFromContext(endpointName, s)
}
type internalAPI struct {
dockerCli command.Cli
}
func (a *internalAPI) DockerAPI(name string) (dockerclient.APIClient, error) {
if name == "" {
name = a.dockerCli.CurrentContext()
}
return clientForEndpoint(a.dockerCli, name)
}

View File

@ -89,6 +89,10 @@ func (s *composeService) doBuildClassicSimpleImage(ctx context.Context, options
} }
} }
if len(options.Platforms) > 1 {
return "", errors.Errorf("this builder doesn't support multi-arch build, set DOCKER_BUILDKIT=1 to use multi-arch builder")
}
if options.Labels == nil { if options.Labels == nil {
options.Labels = make(map[string]string) options.Labels = make(map[string]string)
} }

View File

@ -17,7 +17,6 @@
package compose package compose
import ( import (
"bytes"
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
@ -96,28 +95,14 @@ func getContainerNameWithoutProject(c moby.Container) string {
func (s *composeService) Convert(ctx context.Context, project *types.Project, options api.ConvertOptions) ([]byte, error) { func (s *composeService) Convert(ctx context.Context, project *types.Project, options api.ConvertOptions) ([]byte, error) {
switch options.Format { switch options.Format {
case "json": case "json":
marshal, err := json.MarshalIndent(project, "", " ") return json.MarshalIndent(project, "", " ")
if err != nil {
return nil, err
}
return escapeDollarSign(marshal), nil
case "yaml": case "yaml":
marshal, err := yaml.Marshal(project) return yaml.Marshal(project)
if err != nil {
return nil, err
}
return escapeDollarSign(marshal), nil
default: default:
return nil, fmt.Errorf("unsupported format %q", options) return nil, fmt.Errorf("unsupported format %q", options)
} }
} }
func escapeDollarSign(marshal []byte) []byte {
dollar := []byte{'$'}
escDollar := []byte{'$', '$'}
return bytes.ReplaceAll(marshal, dollar, escDollar)
}
// projectFromName builds a types.Project based on actual resources with compose labels set // projectFromName builds a types.Project based on actual resources with compose labels set
func (s *composeService) projectFromName(containers Containers, projectName string, services ...string) (*types.Project, error) { func (s *composeService) projectFromName(containers Containers, projectName string, services ...string) (*types.Project, error) {
project := &types.Project{ project := &types.Project{

View File

@ -23,12 +23,13 @@ import (
"testing" "testing"
"github.com/compose-spec/compose-go/types" "github.com/compose-spec/compose-go/types"
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/mocks"
moby "github.com/docker/docker/api/types" moby "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/filters"
"github.com/golang/mock/gomock" "github.com/golang/mock/gomock"
"gotest.tools/assert" "gotest.tools/assert"
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/mocks"
) )
func TestContainerName(t *testing.T) { func TestContainerName(t *testing.T) {
@ -77,7 +78,9 @@ func TestServiceLinks(t *testing.T) {
apiClient := mocks.NewMockAPIClient(mockCtrl) apiClient := mocks.NewMockAPIClient(mockCtrl)
cli := mocks.NewMockCli(mockCtrl) cli := mocks.NewMockCli(mockCtrl)
tested.dockerCli = cli tested := composeService{
dockerCli: cli,
}
cli.EXPECT().Client().Return(apiClient).AnyTimes() cli.EXPECT().Client().Return(apiClient).AnyTimes()
s.Links = []string{"db"} s.Links = []string{"db"}
@ -99,7 +102,9 @@ func TestServiceLinks(t *testing.T) {
defer mockCtrl.Finish() defer mockCtrl.Finish()
apiClient := mocks.NewMockAPIClient(mockCtrl) apiClient := mocks.NewMockAPIClient(mockCtrl)
cli := mocks.NewMockCli(mockCtrl) cli := mocks.NewMockCli(mockCtrl)
tested.dockerCli = cli tested := composeService{
dockerCli: cli,
}
cli.EXPECT().Client().Return(apiClient).AnyTimes() cli.EXPECT().Client().Return(apiClient).AnyTimes()
s.Links = []string{"db:db"} s.Links = []string{"db:db"}
@ -121,7 +126,9 @@ func TestServiceLinks(t *testing.T) {
defer mockCtrl.Finish() defer mockCtrl.Finish()
apiClient := mocks.NewMockAPIClient(mockCtrl) apiClient := mocks.NewMockAPIClient(mockCtrl)
cli := mocks.NewMockCli(mockCtrl) cli := mocks.NewMockCli(mockCtrl)
tested.dockerCli = cli tested := composeService{
dockerCli: cli,
}
cli.EXPECT().Client().Return(apiClient).AnyTimes() cli.EXPECT().Client().Return(apiClient).AnyTimes()
s.Links = []string{"db:dbname"} s.Links = []string{"db:dbname"}
@ -143,7 +150,9 @@ func TestServiceLinks(t *testing.T) {
defer mockCtrl.Finish() defer mockCtrl.Finish()
apiClient := mocks.NewMockAPIClient(mockCtrl) apiClient := mocks.NewMockAPIClient(mockCtrl)
cli := mocks.NewMockCli(mockCtrl) cli := mocks.NewMockCli(mockCtrl)
tested.dockerCli = cli tested := composeService{
dockerCli: cli,
}
cli.EXPECT().Client().Return(apiClient).AnyTimes() cli.EXPECT().Client().Return(apiClient).AnyTimes()
s.Links = []string{"db:dbname"} s.Links = []string{"db:dbname"}
@ -169,7 +178,9 @@ func TestServiceLinks(t *testing.T) {
defer mockCtrl.Finish() defer mockCtrl.Finish()
apiClient := mocks.NewMockAPIClient(mockCtrl) apiClient := mocks.NewMockAPIClient(mockCtrl)
cli := mocks.NewMockCli(mockCtrl) cli := mocks.NewMockCli(mockCtrl)
tested.dockerCli = cli tested := composeService{
dockerCli: cli,
}
cli.EXPECT().Client().Return(apiClient).AnyTimes() cli.EXPECT().Client().Return(apiClient).AnyTimes()
s.Links = []string{} s.Links = []string{}
@ -203,7 +214,9 @@ func TestWaitDependencies(t *testing.T) {
apiClient := mocks.NewMockAPIClient(mockCtrl) apiClient := mocks.NewMockAPIClient(mockCtrl)
cli := mocks.NewMockCli(mockCtrl) cli := mocks.NewMockCli(mockCtrl)
tested.dockerCli = cli tested := composeService{
dockerCli: cli,
}
cli.EXPECT().Client().Return(apiClient).AnyTimes() cli.EXPECT().Client().Return(apiClient).AnyTimes()
t.Run("should skip dependencies with scale 0", func(t *testing.T) { t.Run("should skip dependencies with scale 0", func(t *testing.T) {

View File

@ -63,21 +63,24 @@ var (
) )
// InDependencyOrder applies the function to the services of the project taking in account the dependency order // InDependencyOrder applies the function to the services of the project taking in account the dependency order
func InDependencyOrder(ctx context.Context, project *types.Project, fn func(context.Context, string) error) error { func InDependencyOrder(ctx context.Context, project *types.Project, fn func(context.Context, string) error, options ...func(*graphTraversalConfig)) error {
return visit(ctx, project, upDirectionTraversalConfig, fn, ServiceStopped) graph, err := NewGraph(project.Services, ServiceStopped)
if err != nil {
return err
}
return visit(ctx, graph, upDirectionTraversalConfig, fn)
} }
// InReverseDependencyOrder applies the function to the services of the project in reverse order of dependencies // InReverseDependencyOrder applies the function to the services of the project in reverse order of dependencies
func InReverseDependencyOrder(ctx context.Context, project *types.Project, fn func(context.Context, string) error) error { func InReverseDependencyOrder(ctx context.Context, project *types.Project, fn func(context.Context, string) error) error {
return visit(ctx, project, downDirectionTraversalConfig, fn, ServiceStarted) graph, err := NewGraph(project.Services, ServiceStarted)
} if err != nil {
func visit(ctx context.Context, project *types.Project, traversalConfig graphTraversalConfig, fn func(context.Context, string) error, initialStatus ServiceStatus) error {
g := NewGraph(project.Services, initialStatus)
if b, err := g.HasCycles(); b {
return err return err
} }
return visit(ctx, graph, downDirectionTraversalConfig, fn)
}
func visit(ctx context.Context, g *Graph, traversalConfig graphTraversalConfig, fn func(context.Context, string) error) error {
nodes := traversalConfig.extremityNodesFn(g) nodes := traversalConfig.extremityNodesFn(g)
eg, _ := errgroup.WithContext(ctx) eg, _ := errgroup.WithContext(ctx)
@ -155,7 +158,7 @@ func (v *Vertex) GetChildren() []*Vertex {
} }
// NewGraph returns the dependency graph of the services // NewGraph returns the dependency graph of the services
func NewGraph(services types.Services, initialStatus ServiceStatus) *Graph { func NewGraph(services types.Services, initialStatus ServiceStatus) (*Graph, error) {
graph := &Graph{ graph := &Graph{
lock: sync.RWMutex{}, lock: sync.RWMutex{},
Vertices: map[string]*Vertex{}, Vertices: map[string]*Vertex{},
@ -171,7 +174,11 @@ func NewGraph(services types.Services, initialStatus ServiceStatus) *Graph {
} }
} }
return graph if b, err := graph.HasCycles(); b {
return nil, err
}
return graph, nil
} }
// NewVertex is the constructor function for the Vertex // NewVertex is the constructor function for the Vertex

View File

@ -18,10 +18,12 @@ package compose
import ( import (
"context" "context"
"fmt"
"testing" "testing"
"github.com/compose-spec/compose-go/types" "github.com/compose-spec/compose-go/types"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"gotest.tools/assert"
) )
var project = types.Project{ var project = types.Project{
@ -69,3 +71,181 @@ func TestInDependencyReverseDownCommandOrder(t *testing.T) {
require.NoError(t, err, "Error during iteration") require.NoError(t, err, "Error during iteration")
require.Equal(t, []string{"test1", "test2", "test3"}, order) require.Equal(t, []string{"test1", "test2", "test3"}, order)
} }
func TestBuildGraph(t *testing.T) {
testCases := []struct {
desc string
services types.Services
expectedVertices map[string]*Vertex
}{
{
desc: "builds graph with single service",
services: types.Services{
{
Name: "test",
DependsOn: types.DependsOnConfig{},
},
},
expectedVertices: map[string]*Vertex{
"test": {
Key: "test",
Service: "test",
Status: ServiceStopped,
Children: map[string]*Vertex{},
Parents: map[string]*Vertex{},
},
},
},
{
desc: "builds graph with two separate services",
services: types.Services{
{
Name: "test",
DependsOn: types.DependsOnConfig{},
},
{
Name: "another",
DependsOn: types.DependsOnConfig{},
},
},
expectedVertices: map[string]*Vertex{
"test": {
Key: "test",
Service: "test",
Status: ServiceStopped,
Children: map[string]*Vertex{},
Parents: map[string]*Vertex{},
},
"another": {
Key: "another",
Service: "another",
Status: ServiceStopped,
Children: map[string]*Vertex{},
Parents: map[string]*Vertex{},
},
},
},
{
desc: "builds graph with a service and a dependency",
services: types.Services{
{
Name: "test",
DependsOn: types.DependsOnConfig{
"another": types.ServiceDependency{},
},
},
{
Name: "another",
DependsOn: types.DependsOnConfig{},
},
},
expectedVertices: map[string]*Vertex{
"test": {
Key: "test",
Service: "test",
Status: ServiceStopped,
Children: map[string]*Vertex{
"another": {},
},
Parents: map[string]*Vertex{},
},
"another": {
Key: "another",
Service: "another",
Status: ServiceStopped,
Children: map[string]*Vertex{},
Parents: map[string]*Vertex{
"test": {},
},
},
},
},
{
desc: "builds graph with multiple dependency levels",
services: types.Services{
{
Name: "test",
DependsOn: types.DependsOnConfig{
"another": types.ServiceDependency{},
},
},
{
Name: "another",
DependsOn: types.DependsOnConfig{
"another_dep": types.ServiceDependency{},
},
},
{
Name: "another_dep",
DependsOn: types.DependsOnConfig{},
},
},
expectedVertices: map[string]*Vertex{
"test": {
Key: "test",
Service: "test",
Status: ServiceStopped,
Children: map[string]*Vertex{
"another": {},
},
Parents: map[string]*Vertex{},
},
"another": {
Key: "another",
Service: "another",
Status: ServiceStopped,
Children: map[string]*Vertex{
"another_dep": {},
},
Parents: map[string]*Vertex{
"test": {},
},
},
"another_dep": {
Key: "another_dep",
Service: "another_dep",
Status: ServiceStopped,
Children: map[string]*Vertex{},
Parents: map[string]*Vertex{
"another": {},
},
},
},
},
}
for _, tC := range testCases {
t.Run(tC.desc, func(t *testing.T) {
project := types.Project{
Services: tC.services,
}
graph, err := NewGraph(project.Services, ServiceStopped)
assert.NilError(t, err, fmt.Sprintf("failed to build graph for: %s", tC.desc))
for k, vertex := range graph.Vertices {
expected, ok := tC.expectedVertices[k]
assert.Equal(t, true, ok)
assert.Equal(t, true, isVertexEqual(*expected, *vertex))
}
})
}
}
func isVertexEqual(a, b Vertex) bool {
childrenEquality := true
for c := range a.Children {
if _, ok := b.Children[c]; !ok {
childrenEquality = false
}
}
parentEquality := true
for p := range a.Parents {
if _, ok := b.Parents[p]; !ok {
parentEquality = false
}
}
return a.Key == b.Key &&
a.Service == b.Service &&
childrenEquality &&
parentEquality
}

View File

@ -40,7 +40,9 @@ func TestDown(t *testing.T) {
api := mocks.NewMockAPIClient(mockCtrl) api := mocks.NewMockAPIClient(mockCtrl)
cli := mocks.NewMockCli(mockCtrl) cli := mocks.NewMockCli(mockCtrl)
tested.dockerCli = cli tested := composeService{
dockerCli: cli,
}
cli.EXPECT().Client().Return(api).AnyTimes() cli.EXPECT().Client().Return(api).AnyTimes()
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return( api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return(
@ -88,7 +90,9 @@ func TestDownRemoveOrphans(t *testing.T) {
api := mocks.NewMockAPIClient(mockCtrl) api := mocks.NewMockAPIClient(mockCtrl)
cli := mocks.NewMockCli(mockCtrl) cli := mocks.NewMockCli(mockCtrl)
tested.dockerCli = cli tested := composeService{
dockerCli: cli,
}
cli.EXPECT().Client().Return(api).AnyTimes() cli.EXPECT().Client().Return(api).AnyTimes()
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(true)).Return( api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(true)).Return(
@ -125,7 +129,9 @@ func TestDownRemoveVolumes(t *testing.T) {
api := mocks.NewMockAPIClient(mockCtrl) api := mocks.NewMockAPIClient(mockCtrl)
cli := mocks.NewMockCli(mockCtrl) cli := mocks.NewMockCli(mockCtrl)
tested.dockerCli = cli tested := composeService{
dockerCli: cli,
}
cli.EXPECT().Client().Return(api).AnyTimes() cli.EXPECT().Client().Return(api).AnyTimes()
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return( api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return(
@ -166,7 +172,9 @@ func TestDownRemoveImages(t *testing.T) {
api := mocks.NewMockAPIClient(mockCtrl) api := mocks.NewMockAPIClient(mockCtrl)
cli := mocks.NewMockCli(mockCtrl) cli := mocks.NewMockCli(mockCtrl)
tested.dockerCli = cli tested := composeService{
dockerCli: cli,
}
cli.EXPECT().Client().Return(api).AnyTimes() cli.EXPECT().Client().Return(api).AnyTimes()
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)). api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).
@ -255,7 +263,9 @@ func TestDownRemoveImages_NoLabel(t *testing.T) {
api := mocks.NewMockAPIClient(mockCtrl) api := mocks.NewMockAPIClient(mockCtrl)
cli := mocks.NewMockCli(mockCtrl) cli := mocks.NewMockCli(mockCtrl)
tested.dockerCli = cli tested := composeService{
dockerCli: cli,
}
cli.EXPECT().Client().Return(api).AnyTimes() cli.EXPECT().Client().Return(api).AnyTimes()
container := testContainer("service1", "123", false) container := testContainer("service1", "123", false)

View File

@ -35,15 +35,15 @@ import (
const testProject = "testProject" const testProject = "testProject"
var tested = composeService{}
func TestKillAll(t *testing.T) { func TestKillAll(t *testing.T) {
mockCtrl := gomock.NewController(t) mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish() defer mockCtrl.Finish()
api := mocks.NewMockAPIClient(mockCtrl) api := mocks.NewMockAPIClient(mockCtrl)
cli := mocks.NewMockCli(mockCtrl) cli := mocks.NewMockCli(mockCtrl)
tested.dockerCli = cli tested := composeService{
dockerCli: cli,
}
cli.EXPECT().Client().Return(api).AnyTimes() cli.EXPECT().Client().Return(api).AnyTimes()
name := strings.ToLower(testProject) name := strings.ToLower(testProject)
@ -74,7 +74,9 @@ func TestKillSignal(t *testing.T) {
api := mocks.NewMockAPIClient(mockCtrl) api := mocks.NewMockAPIClient(mockCtrl)
cli := mocks.NewMockCli(mockCtrl) cli := mocks.NewMockCli(mockCtrl)
tested.dockerCli = cli tested := composeService{
dockerCli: cli,
}
cli.EXPECT().Client().Return(api).AnyTimes() cli.EXPECT().Client().Return(api).AnyTimes()
name := strings.ToLower(testProject) name := strings.ToLower(testProject)
@ -97,9 +99,13 @@ func TestKillSignal(t *testing.T) {
} }
func testContainer(service string, id string, oneOff bool) moby.Container { func testContainer(service string, id string, oneOff bool) moby.Container {
// canonical docker names in the API start with a leading slash, some
// parts of Compose code will attempt to strip this off, so make sure
// it's consistently present
name := "/" + strings.TrimPrefix(id, "/")
return moby.Container{ return moby.Container{
ID: id, ID: id,
Names: []string{id}, Names: []string{name},
Labels: containerLabels(service, oneOff), Labels: containerLabels(service, oneOff),
} }
} }

View File

@ -29,13 +29,32 @@ import (
"github.com/docker/compose/v2/pkg/utils" "github.com/docker/compose/v2/pkg/utils"
) )
func (s *composeService) Logs(ctx context.Context, projectName string, consumer api.LogConsumer, options api.LogOptions) error { func (s *composeService) Logs(
ctx context.Context,
projectName string,
consumer api.LogConsumer,
options api.LogOptions,
) error {
projectName = strings.ToLower(projectName) projectName = strings.ToLower(projectName)
containers, err := s.getContainers(ctx, projectName, oneOffExclude, true, options.Services...) containers, err := s.getContainers(ctx, projectName, oneOffExclude, true, options.Services...)
if err != nil { if err != nil {
return err return err
} }
project := options.Project
if project == nil {
project, err = s.getProjectWithResources(ctx, containers, projectName)
if err != nil {
return err
}
}
if len(options.Services) == 0 {
options.Services = project.ServiceNames()
}
containers = containers.filter(isService(options.Services...))
eg, ctx := errgroup.WithContext(ctx) eg, ctx := errgroup.WithContext(ctx)
for _, c := range containers { for _, c := range containers {
c := c c := c

204
pkg/compose/logs_test.go Normal file
View File

@ -0,0 +1,204 @@
/*
Copyright 2022 Docker Compose CLI authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package compose
import (
"context"
"io"
"strings"
"sync"
"testing"
"github.com/compose-spec/compose-go/types"
moby "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/pkg/stdcopy"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
compose "github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/mocks"
)
func TestComposeService_Logs_Demux(t *testing.T) {
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
api := mocks.NewMockAPIClient(mockCtrl)
cli := mocks.NewMockCli(mockCtrl)
tested := composeService{
dockerCli: cli,
}
cli.EXPECT().Client().Return(api).AnyTimes()
name := strings.ToLower(testProject)
ctx := context.Background()
api.EXPECT().ContainerList(ctx, moby.ContainerListOptions{
All: true,
Filters: filters.NewArgs(oneOffFilter(false), projectFilter(name)),
}).Return(
[]moby.Container{
testContainer("service", "c", false),
},
nil,
)
api.EXPECT().
ContainerInspect(anyCancellableContext(), "c").
Return(moby.ContainerJSON{
ContainerJSONBase: &moby.ContainerJSONBase{ID: "c"},
Config: &container.Config{Tty: false},
}, nil)
c1Reader, c1Writer := io.Pipe()
t.Cleanup(func() {
_ = c1Reader.Close()
_ = c1Writer.Close()
})
c1Stdout := stdcopy.NewStdWriter(c1Writer, stdcopy.Stdout)
c1Stderr := stdcopy.NewStdWriter(c1Writer, stdcopy.Stderr)
go func() {
_, err := c1Stdout.Write([]byte("hello stdout\n"))
assert.NoError(t, err, "Writing to fake stdout")
_, err = c1Stderr.Write([]byte("hello stderr\n"))
assert.NoError(t, err, "Writing to fake stderr")
_ = c1Writer.Close()
}()
api.EXPECT().ContainerLogs(anyCancellableContext(), "c", gomock.Any()).
Return(c1Reader, nil)
opts := compose.LogOptions{
Project: &types.Project{
Services: types.Services{
{Name: "service"},
},
},
}
consumer := &testLogConsumer{}
err := tested.Logs(ctx, name, consumer, opts)
require.NoError(t, err)
require.Equal(
t,
[]string{"hello stdout", "hello stderr"},
consumer.LogsForContainer("service", "c"),
)
}
// TestComposeService_Logs_ServiceFiltering ensures that we do not include
// logs from out-of-scope services based on the Compose file vs actual state.
//
// NOTE(milas): This test exists because each method is currently duplicating
// a lot of the project/service filtering logic. We should consider moving it
// to an earlier point in the loading process, at which point this test could
// safely be removed.
func TestComposeService_Logs_ServiceFiltering(t *testing.T) {
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
api := mocks.NewMockAPIClient(mockCtrl)
cli := mocks.NewMockCli(mockCtrl)
tested := composeService{
dockerCli: cli,
}
cli.EXPECT().Client().Return(api).AnyTimes()
name := strings.ToLower(testProject)
ctx := context.Background()
api.EXPECT().ContainerList(ctx, moby.ContainerListOptions{
All: true,
Filters: filters.NewArgs(oneOffFilter(false), projectFilter(name)),
}).Return(
[]moby.Container{
testContainer("serviceA", "c1", false),
testContainer("serviceA", "c2", false),
// serviceB will be filtered out by the project definition to
// ensure we ignore "orphan" containers
testContainer("serviceB", "c3", false),
testContainer("serviceC", "c4", false),
},
nil,
)
for _, id := range []string{"c1", "c2", "c4"} {
id := id
api.EXPECT().
ContainerInspect(anyCancellableContext(), id).
Return(
moby.ContainerJSON{
ContainerJSONBase: &moby.ContainerJSONBase{ID: id},
Config: &container.Config{Tty: true},
},
nil,
)
api.EXPECT().ContainerLogs(anyCancellableContext(), id, gomock.Any()).
Return(io.NopCloser(strings.NewReader("hello "+id+"\n")), nil).
Times(1)
}
// this simulates passing `--filename` with a Compose file that does NOT
// reference `serviceB` even though it has running services for this proj
proj := &types.Project{
Services: types.Services{
{Name: "serviceA"},
{Name: "serviceC"},
},
}
consumer := &testLogConsumer{}
opts := compose.LogOptions{
Project: proj,
}
err := tested.Logs(ctx, name, consumer, opts)
require.NoError(t, err)
require.Equal(t, []string{"hello c1"}, consumer.LogsForContainer("serviceA", "c1"))
require.Equal(t, []string{"hello c2"}, consumer.LogsForContainer("serviceA", "c2"))
require.Empty(t, consumer.LogsForContainer("serviceB", "c3"))
require.Equal(t, []string{"hello c4"}, consumer.LogsForContainer("serviceC", "c4"))
}
type testLogConsumer struct {
mu sync.Mutex
// logs is keyed by service, then container; values are log lines
logs map[string]map[string][]string
}
func (l *testLogConsumer) Log(containerName, service, message string) {
l.mu.Lock()
defer l.mu.Unlock()
if l.logs == nil {
l.logs = make(map[string]map[string][]string)
}
if l.logs[service] == nil {
l.logs[service] = make(map[string][]string)
}
l.logs[service][containerName] = append(l.logs[service][containerName], message)
}
func (l *testLogConsumer) Status(containerName, msg string) {}
func (l *testLogConsumer) Register(containerName string) {}
func (l *testLogConsumer) LogsForContainer(svc string, containerName string) []string {
l.mu.Lock()
defer l.mu.Unlock()
return l.logs[svc][containerName]
}

View File

@ -38,7 +38,9 @@ func TestPs(t *testing.T) {
api := mocks.NewMockAPIClient(mockCtrl) api := mocks.NewMockAPIClient(mockCtrl)
cli := mocks.NewMockCli(mockCtrl) cli := mocks.NewMockCli(mockCtrl)
tested.dockerCli = cli tested := composeService{
dockerCli: cli,
}
cli.EXPECT().Client().Return(api).AnyTimes() cli.EXPECT().Client().Return(api).AnyTimes()
ctx := context.Background() ctx := context.Background()

View File

@ -181,6 +181,18 @@ func (s *composeService) pullServiceImage(ctx context.Context, service types.Ser
RegistryAuth: base64.URLEncoding.EncodeToString(buf), RegistryAuth: base64.URLEncoding.EncodeToString(buf),
Platform: service.Platform, Platform: service.Platform,
}) })
// check if has error and the service has a build section
// then the status should be warning instead of error
if err != nil && service.Build != nil {
w.Event(progress.Event{
ID: service.Name,
Status: progress.Warning,
Text: "Warning",
})
return "", WrapCategorisedComposeError(err, PullFailure)
}
if err != nil { if err != nil {
w.Event(progress.Event{ w.Event(progress.Event{
ID: service.Name, ID: service.Name,

View File

@ -50,6 +50,13 @@ func (s *composeService) start(ctx context.Context, projectName string, options
} }
} }
if len(options.Services) > 0 {
err := project.ForServices(options.Services)
if err != nil {
return err
}
}
eg, ctx := errgroup.WithContext(ctx) eg, ctx := errgroup.WithContext(ctx)
if listener != nil { if listener != nil {
attached, err := s.attach(ctx, project, listener, options.AttachTo) attached, err := s.attach(ctx, project, listener, options.AttachTo)

View File

@ -38,7 +38,9 @@ func TestStopTimeout(t *testing.T) {
api := mocks.NewMockAPIClient(mockCtrl) api := mocks.NewMockAPIClient(mockCtrl)
cli := mocks.NewMockCli(mockCtrl) cli := mocks.NewMockCli(mockCtrl)
tested.dockerCli = cli tested := composeService{
dockerCli: cli,
}
cli.EXPECT().Client().Return(api).AnyTimes() cli.EXPECT().Client().Return(api).AnyTimes()
ctx := context.Background() ctx := context.Background()

View File

@ -257,3 +257,105 @@ func TestBuildImageDependencies(t *testing.T) {
t.Skip("See https://github.com/docker/compose/issues/9232") t.Skip("See https://github.com/docker/compose/issues/9232")
}) })
} }
func TestBuildPlatformsWithCorrectBuildxConfig(t *testing.T) {
c := NewParallelCLI(t)
// declare builder
result := c.RunDockerCmd(t, "buildx", "create", "--name", "build-platform", "--use", "--bootstrap")
assert.NilError(t, result.Error)
t.Cleanup(func() {
c.RunDockerComposeCmd(t, "--project-directory", "fixtures/build-test/platforms", "down")
_ = c.RunDockerCmd(t, "buildx", "rm", "-f", "build-platform")
})
t.Run("platform not supported by builder", func(t *testing.T) {
res := c.RunDockerComposeCmdNoCheck(t, "--project-directory", "fixtures/build-test/platforms",
"-f", "fixtures/build-test/platforms/compose-unsupported-platform.yml", "build")
res.Assert(t, icmd.Expected{
ExitCode: 17,
Err: "failed to solve: alpine: no match for platform in",
})
})
t.Run("multi-arch build ok", func(t *testing.T) {
res := c.RunDockerComposeCmdNoCheck(t, "--project-directory", "fixtures/build-test/platforms", "build")
assert.NilError(t, res.Error, res.Stderr())
res.Assert(t, icmd.Expected{Out: "I am building for linux/arm64"})
res.Assert(t, icmd.Expected{Out: "I am building for linux/amd64"})
})
t.Run("multi-arch multi service builds ok", func(t *testing.T) {
res := c.RunDockerComposeCmdNoCheck(t, "--project-directory", "fixtures/build-test/platforms",
"-f", "fixtures/build-test/platforms/compose-multiple-platform-builds.yaml", "build")
assert.NilError(t, res.Error, res.Stderr())
res.Assert(t, icmd.Expected{Out: "I'm Service A and I am building for linux/arm64"})
res.Assert(t, icmd.Expected{Out: "I'm Service A and I am building for linux/amd64"})
res.Assert(t, icmd.Expected{Out: "I'm Service B and I am building for linux/arm64"})
res.Assert(t, icmd.Expected{Out: "I'm Service B and I am building for linux/amd64"})
res.Assert(t, icmd.Expected{Out: "I'm Service C and I am building for linux/arm64"})
res.Assert(t, icmd.Expected{Out: "I'm Service C and I am building for linux/amd64"})
})
t.Run("multi-arch up --build", func(t *testing.T) {
res := c.RunDockerComposeCmdNoCheck(t, "--project-directory", "fixtures/build-test/platforms", "up", "--build")
assert.NilError(t, res.Error, res.Stderr())
res.Assert(t, icmd.Expected{Out: "platforms-platforms-1 exited with code 0"})
})
t.Run("use DOCKER_DEFAULT_PLATFORM value when up --build", func(t *testing.T) {
cmd := c.NewDockerComposeCmd(t, "--project-directory", "fixtures/build-test/platforms", "up", "--build")
res := icmd.RunCmd(cmd, func(cmd *icmd.Cmd) {
cmd.Env = append(cmd.Env, "DOCKER_DEFAULT_PLATFORM=linux/amd64")
})
assert.NilError(t, res.Error, res.Stderr())
res.Assert(t, icmd.Expected{Out: "I am building for linux/amd64"})
assert.Assert(t, !strings.Contains(res.Stdout(), "I am building for linux/arm64"))
})
}
func TestBuildPlatformsStandardErrors(t *testing.T) {
c := NewParallelCLI(t)
t.Run("no platform support with Classic Builder", func(t *testing.T) {
cmd := c.NewDockerComposeCmd(t, "--project-directory", "fixtures/build-test/platforms", "build")
res := icmd.RunCmd(cmd, func(cmd *icmd.Cmd) {
cmd.Env = append(cmd.Env, "DOCKER_BUILDKIT=0")
})
res.Assert(t, icmd.Expected{
ExitCode: 1,
Err: "this builder doesn't support multi-arch build, set DOCKER_BUILDKIT=1 to use multi-arch builder",
})
})
t.Run("builder does not support multi-arch", func(t *testing.T) {
res := c.RunDockerComposeCmdNoCheck(t, "--project-directory", "fixtures/build-test/platforms", "build")
res.Assert(t, icmd.Expected{
ExitCode: 17,
Err: `multiple platforms feature is currently not supported for docker driver. Please switch to a different driver (eg. "docker buildx create --use")`,
})
})
t.Run("service platform not defined in platforms build section", func(t *testing.T) {
res := c.RunDockerComposeCmdNoCheck(t, "--project-directory", "fixtures/build-test/platforms",
"-f", "fixtures/build-test/platforms/compose-service-platform-not-in-build-platforms.yaml", "build")
res.Assert(t, icmd.Expected{
ExitCode: 1,
Err: `service.platform should be part of the service.build.platforms: "linux/riscv64"`,
})
})
t.Run("DOCKER_DEFAULT_PLATFORM value not defined in platforms build section", func(t *testing.T) {
cmd := c.NewDockerComposeCmd(t, "--project-directory", "fixtures/build-test/platforms", "build")
res := icmd.RunCmd(cmd, func(cmd *icmd.Cmd) {
cmd.Env = append(cmd.Env, "DOCKER_DEFAULT_PLATFORM=windows/amd64")
})
res.Assert(t, icmd.Expected{
ExitCode: 1,
Err: `DOCKER_DEFAULT_PLATFORM value should be part of the service.build.platforms: "windows/amd64"`,
})
})
}

View File

@ -234,3 +234,25 @@ networks:
name: compose-e2e-convert_default`, filepath.Join(wd, "fixtures", "simple-build-test", "nginx-build")), ExitCode: 0}) name: compose-e2e-convert_default`, filepath.Join(wd, "fixtures", "simple-build-test", "nginx-build")), ExitCode: 0})
}) })
} }
func TestConvertInterpolate(t *testing.T) {
const projectName = "compose-e2e-convert-interpolate"
c := NewParallelCLI(t)
wd, err := os.Getwd()
assert.NilError(t, err)
t.Run("convert", func(t *testing.T) {
res := c.RunDockerComposeCmd(t, "-f", "./fixtures/simple-build-test/compose-interpolate.yaml", "-p", projectName, "convert", "--no-interpolate")
res.Assert(t, icmd.Expected{Out: fmt.Sprintf(`services:
nginx:
build:
context: %s
dockerfile: ${MYVAR}
networks:
default: null
networks:
default:
name: compose-e2e-convert-interpolate_default`, filepath.Join(wd, "fixtures", "simple-build-test", "nginx-build")), ExitCode: 0})
})
}

View File

@ -0,0 +1,22 @@
# 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.
FROM --platform=$BUILDPLATFORM golang:alpine AS build
ARG TARGETPLATFORM
ARG BUILDPLATFORM
RUN echo "I am building for $TARGETPLATFORM, running on $BUILDPLATFORM" > /log
FROM alpine
COPY --from=build /log /log

View File

@ -0,0 +1,23 @@
services:
serviceA:
image: build-test-platform-a:test
build:
context: ./contextServiceA
platforms:
- linux/amd64
- linux/arm64
serviceB:
image: build-test-platform-b:test
build:
context: ./contextServiceB
platforms:
- linux/amd64
- linux/arm64
serviceC:
image: build-test-platform-c:test
build:
context: ./contextServiceC
platforms:
- linux/amd64
- linux/arm64

View File

@ -0,0 +1,9 @@
services:
platforms:
image: build-test-platform:test
platform: linux/riscv64
build:
context: .
platforms:
- linux/amd64
- linux/arm64

View File

@ -0,0 +1,8 @@
services:
platforms:
image: build-test-platform:test
build:
context: .
platforms:
- unsupported/unsupported
- linux/amd64

View File

@ -0,0 +1,9 @@
services:
platforms:
image: build-test-platform:test
build:
context: .
platforms:
- linux/amd64
- linux/arm64

View File

@ -0,0 +1,22 @@
# 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.
FROM --platform=$BUILDPLATFORM golang:alpine AS build
ARG TARGETPLATFORM
ARG BUILDPLATFORM
RUN echo "I'm Service A and I am building for $TARGETPLATFORM, running on $BUILDPLATFORM" > /log
FROM alpine
COPY --from=build /log /log

View File

@ -0,0 +1,22 @@
# 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.
FROM --platform=$BUILDPLATFORM golang:alpine AS build
ARG TARGETPLATFORM
ARG BUILDPLATFORM
RUN echo "I'm Service B and I am building for $TARGETPLATFORM, running on $BUILDPLATFORM" > /log
FROM alpine
COPY --from=build /log /log

View File

@ -0,0 +1,22 @@
# 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.
FROM --platform=$BUILDPLATFORM golang:alpine AS build
ARG TARGETPLATFORM
ARG BUILDPLATFORM
RUN echo "I'm Service C and I am building for $TARGETPLATFORM, running on $BUILDPLATFORM" > /log
FROM alpine
COPY --from=build /log /log

View File

@ -0,0 +1,5 @@
services:
nginx:
build:
context: nginx-build
dockerfile: ${MYVAR}

View File

@ -0,0 +1,17 @@
services:
another_2:
image: nginx:alpine
another:
image: nginx:alpine
depends_on:
- another_2
dep_2:
image: nginx:alpine
dep_1:
image: nginx:alpine
depends_on:
- dep_2
desired:
image: nginx:alpine
depends_on:
- dep_1

View File

@ -247,6 +247,30 @@ func TestStartStopMultipleServices(t *testing.T) {
} }
} }
func TestStartSingleServiceAndDependency(t *testing.T) {
cli := NewParallelCLI(t, WithEnv(
"COMPOSE_PROJECT_NAME=e2e-start-single-deps",
"COMPOSE_FILE=./fixtures/start-stop/start-stop-deps.yaml"))
t.Cleanup(func() {
cli.RunDockerComposeCmd(t, "down", "--remove-orphans", "-v", "-t", "0")
})
cli.RunDockerComposeCmd(t, "create", "desired")
res := cli.RunDockerComposeCmd(t, "start", "desired")
desiredServices := []string{"desired", "dep_1", "dep_2"}
for _, s := range desiredServices {
startMsg := fmt.Sprintf("Container e2e-start-single-deps-%s-1 Started", s)
assert.Assert(t, strings.Contains(res.Combined(), startMsg),
fmt.Sprintf("Missing start message for service: %s\n%s", s, res.Combined()))
}
undesiredServices := []string{"another", "another_2"}
for _, s := range undesiredServices {
assert.Assert(t, !strings.Contains(res.Combined(), s),
fmt.Sprintf("Shouldn't have message for service: %s\n%s", s, res.Combined()))
}
}
func TestStartStopMultipleFiles(t *testing.T) { func TestStartStopMultipleFiles(t *testing.T) {
cli := NewParallelCLI(t, WithEnv("COMPOSE_PROJECT_NAME=e2e-start-stop-svc-multiple-files")) cli := NewParallelCLI(t, WithEnv("COMPOSE_PROJECT_NAME=e2e-start-stop-svc-multiple-files"))
t.Cleanup(func() { t.Cleanup(func() {

View File

@ -28,6 +28,8 @@ const (
Done Done
// Error means that the current task has errored // Error means that the current task has errored
Error Error
// Warning means that the current task has warning
Warning
) )
// Event represents a progress event. // Event represents a progress event.

View File

@ -75,7 +75,7 @@ func (w *ttyWriter) Event(e Event) {
if _, ok := w.events[e.ID]; ok { if _, ok := w.events[e.ID]; ok {
last := w.events[e.ID] last := w.events[e.ID]
switch e.Status { switch e.Status {
case Done, Error: case Done, Error, Warning:
if last.Status != e.Status { if last.Status != e.Status {
last.stop() last.stop()
} }
@ -222,6 +222,9 @@ func lineText(event Event, pad string, terminalWidth, statusPadding int, color b
if event.Status == Error { if event.Status == Error {
color = aec.RedF color = aec.RedF
} }
if event.Status == Warning {
color = aec.YellowF
}
return aec.Apply(o, color) return aec.Apply(o, color)
} }

View File

@ -54,6 +54,10 @@ func TestLineText(t *testing.T) {
ev.Status = Error ev.Status = Error
out = lineText(ev, "", 50, lineWidth, true) out = lineText(ev, "", 50, lineWidth, true)
assert.Equal(t, out, "\x1b[31m . id Text Status 0.0s\n\x1b[0m") assert.Equal(t, out, "\x1b[31m . id Text Status 0.0s\n\x1b[0m")
ev.Status = Warning
out = lineText(ev, "", 50, lineWidth, true)
assert.Equal(t, out, "\x1b[33m . id Text Status 0.0s\n\x1b[0m")
} }
func TestLineTextSingleEvent(t *testing.T) { func TestLineTextSingleEvent(t *testing.T) {
@ -103,3 +107,32 @@ func TestErrorEvent(t *testing.T) {
assert.Assert(t, ok) assert.Assert(t, ok)
assert.Assert(t, event.endTime.After(time.Now().Add(-10*time.Second))) assert.Assert(t, event.endTime.After(time.Now().Add(-10*time.Second)))
} }
func TestWarningEvent(t *testing.T) {
w := &ttyWriter{
events: map[string]Event{},
mtx: &sync.Mutex{},
}
e := Event{
ID: "id",
Text: "Text",
Status: Working,
StatusText: "Working",
startTime: time.Now(),
spinner: &spinner{
chars: []string{"."},
},
}
// Fire "Working" event and check end time isn't touched
w.Event(e)
event, ok := w.events[e.ID]
assert.Assert(t, ok)
assert.Assert(t, event.endTime.Equal(time.Time{}))
// Fire "Warning" event and check end time is set
e.Status = Warning
w.Event(e)
event, ok = w.events[e.ID]
assert.Assert(t, ok)
assert.Assert(t, event.endTime.After(time.Now().Add(-10*time.Second)))
}

30
pkg/utils/slices.go Normal file
View File

@ -0,0 +1,30 @@
/*
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 utils
import "reflect"
// Contains helps to detect if a non-comparable struct is part of an array
// only use this method if you can't rely on existing golang Contains function of slices (https://pkg.go.dev/golang.org/x/exp/slices#Contains)
func Contains[T any](origin []T, element T) bool {
for _, v := range origin {
if reflect.DeepEqual(v, element) {
return true
}
}
return false
}

95
pkg/utils/slices_test.go Normal file
View File

@ -0,0 +1,95 @@
/*
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 utils
import (
"testing"
specs "github.com/opencontainers/image-spec/specs-go/v1"
)
func TestContains(t *testing.T) {
source := []specs.Platform{
{
Architecture: "linux/amd64",
OS: "darwin",
OSVersion: "",
OSFeatures: nil,
Variant: "",
},
{
Architecture: "linux/arm64",
OS: "linux",
OSVersion: "12",
OSFeatures: nil,
Variant: "v8",
},
{
Architecture: "",
OS: "",
OSVersion: "",
OSFeatures: nil,
Variant: "",
},
}
type args struct {
origin []specs.Platform
element specs.Platform
}
tests := []struct {
name string
args args
want bool
}{
{
name: "element found",
args: args{
origin: source,
element: specs.Platform{
Architecture: "linux/arm64",
OS: "linux",
OSVersion: "12",
OSFeatures: nil,
Variant: "v8",
},
},
want: true,
},
{
name: "element not found",
args: args{
origin: source,
element: specs.Platform{
Architecture: "linux/arm64",
OS: "darwin",
OSVersion: "12",
OSFeatures: nil,
Variant: "v8",
},
},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := Contains(tt.args.origin, tt.args.element); got != tt.want {
t.Errorf("Contains() = %v, want %v", got, tt.want)
}
})
}
}