Merge branch 'v2' into 8768-avoid-pulling-same-image-multiple-times

This commit is contained in:
Vedant Koditkar 2022-08-19 13:08:36 +05:30
commit b96b5449e5
12 changed files with 117 additions and 26 deletions

View File

@ -195,6 +195,12 @@ jobs:
with: with:
name: compose name: compose
path: ${{ env.DESTDIR }} path: ${{ env.DESTDIR }}
-
name: Create checksums
working-directory: ${{ env.DESTDIR }}
run: |
find . -type f -print0 | sort -z | xargs -r0 shasum -a 256 -b | sed 's# .*/# #' > checksums.txt
shasum -a 256 -U -c checksums.txt
- -
name: License name: License
run: cp packaging/* ${{ env.DESTDIR }}/ run: cp packaging/* ${{ env.DESTDIR }}/

View File

@ -167,7 +167,6 @@ COPY --link --from=build /usr/bin/docker-compose /docker-compose.exe
FROM binary-$TARGETOS AS binary FROM binary-$TARGETOS AS binary
FROM --platform=$BUILDPLATFORM alpine AS releaser FROM --platform=$BUILDPLATFORM alpine AS releaser
RUN apk add --no-cache file perl-utils
WORKDIR /work WORKDIR /work
ARG TARGETOS ARG TARGETOS
ARG TARGETARCH ARG TARGETARCH
@ -177,8 +176,7 @@ RUN --mount=from=binary \
# TODO: should just use standard arch # TODO: should just use standard arch
TARGETARCH=$([ "$TARGETARCH" = "amd64" ] && echo "x86_64" || echo "$TARGETARCH"); \ TARGETARCH=$([ "$TARGETARCH" = "amd64" ] && echo "x86_64" || echo "$TARGETARCH"); \
TARGETARCH=$([ "$TARGETARCH" = "arm64" ] && echo "aarch64" || echo "$TARGETARCH"); \ TARGETARCH=$([ "$TARGETARCH" = "arm64" ] && echo "aarch64" || echo "$TARGETARCH"); \
cp docker-compose* "/out/docker-compose-${TARGETOS}-${TARGETARCH}${TARGETVARIANT}$(ls docker-compose* | sed -e 's/^docker-compose//')" && \ cp docker-compose* "/out/docker-compose-${TARGETOS}-${TARGETARCH}${TARGETVARIANT}$(ls docker-compose* | sed -e 's/^docker-compose//')"
(cd /out ; for f in *; do shasum --binary --algorithm 256 $f | tee -a /out/checksums.txt > $f.sha256; done)
FROM scratch AS release FROM scratch AS release
COPY --from=releaser /out/ / COPY --from=releaser /out/ /

View File

@ -32,6 +32,8 @@ 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/cli/cli/flags"
"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,18 @@ 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(flags.NewClientOptions(),
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
@ -383,8 +397,10 @@ func setEnvWithDotEnv(prjOpts *projectOptions) error {
return err return err
} }
for k, v := range envFromFile { for k, v := range envFromFile {
if err := os.Setenv(k, v); err != nil { // overwrite the process env with merged OS + env file results if _, ok := os.LookupEnv(k); !ok { // Precedence to OS Env
return err if err := os.Setenv(k, v); err != nil {
return err
}
} }
} }
return nil return nil

View File

@ -100,8 +100,10 @@ target "binary-cross" {
"linux/arm/v7", "linux/arm/v7",
"linux/arm64", "linux/arm64",
"linux/ppc64le", "linux/ppc64le",
"linux/riscv64",
"linux/s390x", "linux/s390x",
"windows/amd64" "windows/amd64",
"windows/arm64"
] ]
} }

6
go.mod
View File

@ -6,7 +6,7 @@ require (
github.com/AlecAivazis/survey/v2 v2.3.5 github.com/AlecAivazis/survey/v2 v2.3.5
github.com/buger/goterm v1.0.4 github.com/buger/goterm v1.0.4
github.com/cnabio/cnab-to-oci v0.3.6 github.com/cnabio/cnab-to-oci v0.3.6
github.com/compose-spec/compose-go v1.4.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-20220729163034-26163d82560f
@ -19,7 +19,7 @@ require (
github.com/golang/mock v1.6.0 github.com/golang/mock v1.6.0
github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/go-version v1.6.0 github.com/hashicorp/go-version v1.6.0
github.com/mattn/go-isatty v0.0.14 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.3 github.com/moby/buildkit v0.10.3
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6
@ -111,7 +111,7 @@ require (
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-20220225172249-27dd8689420f // 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-20220715151400-c0bba94af5f8 // 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
golang.org/x/text v0.3.7 // indirect golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect

10
go.sum
View File

@ -286,8 +286,8 @@ github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoC
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
github.com/codahale/hdrhistogram v0.0.0-20160425231609-f8ad88b59a58/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/codahale/hdrhistogram v0.0.0-20160425231609-f8ad88b59a58/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/compose-spec/compose-go v1.2.1/go.mod h1:pAy7Mikpeft4pxkFU565/DRHEbDfR84G6AQuiL+Hdg8= github.com/compose-spec/compose-go v1.2.1/go.mod h1:pAy7Mikpeft4pxkFU565/DRHEbDfR84G6AQuiL+Hdg8=
github.com/compose-spec/compose-go v1.4.0 h1:zaYVAZ6lIByr7Jffi20AabfeUwcTrdXfH3X1R5HEm+g= github.com/compose-spec/compose-go v1.5.0 h1:yOmYpIm13pYt2o+oKVe/JAD6o2Tv+eUyOcRhf0qF4fA=
github.com/compose-spec/compose-go v1.4.0/go.mod h1:l7RUULbFFLzlQHuxtJr7SVLyWdqEpbJEGTWCgcu6Eqw= github.com/compose-spec/compose-go v1.5.0/go.mod h1:l7RUULbFFLzlQHuxtJr7SVLyWdqEpbJEGTWCgcu6Eqw=
github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE=
github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU=
github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
@ -971,8 +971,9 @@ github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
@ -1795,8 +1796,9 @@ golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=

View File

@ -22,6 +22,8 @@ import (
"github.com/compose-spec/compose-go/types" "github.com/compose-spec/compose-go/types"
) )
var _ Service = &ServiceProxy{}
// ServiceProxy implements Service by delegating to implementation functions. This allows lazy init and per-method overrides // ServiceProxy implements Service by delegating to implementation functions. This allows lazy init and per-method overrides
type ServiceProxy struct { type ServiceProxy struct {
BuildFn func(ctx context.Context, project *types.Project, options BuildOptions) error BuildFn func(ctx context.Context, project *types.Project, options BuildOptions) error
@ -59,8 +61,6 @@ func NewServiceProxy() *ServiceProxy {
// Interceptor allow to customize the compose types.Project before the actual Service method is executed // Interceptor allow to customize the compose types.Project before the actual Service method is executed
type Interceptor func(ctx context.Context, project *types.Project) type Interceptor func(ctx context.Context, project *types.Project)
var _ Service = &ServiceProxy{}
// WithService configure proxy to use specified Service as delegate // WithService configure proxy to use specified Service as delegate
func (s *ServiceProxy) WithService(service Service) *ServiceProxy { func (s *ServiceProxy) WithService(service Service) *ServiceProxy {
s.BuildFn = service.Build s.BuildFn = service.Build

View File

@ -352,6 +352,12 @@ func shouldWaitForDependency(serviceName string, dependencyConfig types.ServiceD
return false, nil return false, nil
} }
if service, err := project.GetService(serviceName); err != nil { if service, err := project.GetService(serviceName); err != nil {
for _, ds := range project.DisabledServices {
if ds.Name == serviceName {
// don't wait for disabled service (--no-deps)
return false, nil
}
}
return false, err return false, err
} else if service.Scale == 0 { } else if service.Scale == 0 {
// don't wait for the dependency which configured to have 0 containers running // don't wait for the dependency which configured to have 0 containers running

View File

@ -146,6 +146,9 @@ func prepareNetworks(project *types.Project) {
} }
func prepareServicesDependsOn(p *types.Project) error { func prepareServicesDependsOn(p *types.Project) error {
allServices := types.Project{}
allServices.Services = p.AllServices()
for i, service := range p.Services { for i, service := range p.Services {
var dependencies []string var dependencies []string
networkDependency := getDependentServiceFromMode(service.NetworkMode) networkDependency := getDependentServiceFromMode(service.NetworkMode)
@ -178,20 +181,24 @@ func prepareServicesDependsOn(p *types.Project) error {
dependencies = append(dependencies, strings.Split(link, ":")[0]) dependencies = append(dependencies, strings.Split(link, ":")[0])
} }
for d := range service.DependsOn {
dependencies = append(dependencies, d)
}
if len(dependencies) == 0 { if len(dependencies) == 0 {
continue continue
} }
// Verify dependencies exist in the project, whether disabled or not
deps, err := allServices.GetServices(dependencies...)
if err != nil {
return err
}
if service.DependsOn == nil { if service.DependsOn == nil {
service.DependsOn = make(types.DependsOnConfig) service.DependsOn = make(types.DependsOnConfig)
} }
// Verify dependencies exist in the project, whether disabled or not
projAllServices := types.Project{}
projAllServices.Services = p.AllServices()
deps, err := projAllServices.GetServices(dependencies...)
if err != nil {
return err
}
for _, d := range deps { for _, d := range deps {
if _, ok := service.DependsOn[d.Name]; !ok { if _, ok := service.DependsOn[d.Name]; !ok {
service.DependsOn[d.Name] = types.ServiceDependency{ service.DependsOn[d.Name] = types.ServiceDependency{

View File

@ -34,7 +34,7 @@ func TestEnvPriority(t *testing.T) {
}) })
// Full options activated // Full options activated
// 1. Command Line (docker compose run --env <KEY[=VAL]>) <-- Result expected (From environment patched by --env-file) // 1. Command Line (docker compose run --env <KEY[=VAL]>) <-- Result expected (From OS Environment)
// 2. Compose File (service::environment section) // 2. Compose File (service::environment section)
// 3. Compose File (service::env_file section file) // 3. Compose File (service::env_file section file)
// 4. Container Image ENV directive // 4. Container Image ENV directive
@ -45,7 +45,7 @@ func TestEnvPriority(t *testing.T) {
"run", "--rm", "-e", "WHEREAMI", "env-compose-priority") "run", "--rm", "-e", "WHEREAMI", "env-compose-priority")
cmd.Env = append(cmd.Env, "WHEREAMI=shell") cmd.Env = append(cmd.Env, "WHEREAMI=shell")
res := icmd.RunCmd(cmd) res := icmd.RunCmd(cmd)
assert.Equal(t, strings.TrimSpace(res.Stdout()), "override") assert.Equal(t, strings.TrimSpace(res.Stdout()), "shell")
}) })
// Full options activated // Full options activated
@ -63,7 +63,7 @@ func TestEnvPriority(t *testing.T) {
}) })
// No Compose file, all other options // No Compose file, all other options
// 1. Command Line (docker compose run --env <KEY[=VAL]>) <-- Result expected (From environment patched by --env-file) // 1. Command Line (docker compose run --env <KEY[=VAL]>) <-- Result expected (From OS Environment)
// 2. Compose File (service::environment section) // 2. Compose File (service::environment section)
// 3. Compose File (service::env_file section file) // 3. Compose File (service::env_file section file)
// 4. Container Image ENV directive // 4. Container Image ENV directive
@ -74,7 +74,7 @@ func TestEnvPriority(t *testing.T) {
"run", "--rm", "-e", "WHEREAMI", "env-compose-priority") "run", "--rm", "-e", "WHEREAMI", "env-compose-priority")
cmd.Env = append(cmd.Env, "WHEREAMI=shell") cmd.Env = append(cmd.Env, "WHEREAMI=shell")
res := icmd.RunCmd(cmd) res := icmd.RunCmd(cmd)
assert.Equal(t, strings.TrimSpace(res.Stdout()), "override") assert.Equal(t, strings.TrimSpace(res.Stdout()), "shell")
}) })
// No Compose file, all other options with env variable from OS environment // No Compose file, all other options with env variable from OS environment

View File

@ -0,0 +1,15 @@
version: '3.8'
services:
my-service:
image: alpine
command: tail -f /dev/null
depends_on:
nginx: {condition: service_healthy}
nginx:
image: nginx:alpine
healthcheck:
test: "echo | nc -w 5 localhost:80"
interval: 2s
timeout: 1s
retries: 10

View File

@ -0,0 +1,39 @@
/*
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 e2e
import (
"testing"
"gotest.tools/v3/icmd"
)
func TestRecreateWithNoDeps(t *testing.T) {
c := NewParallelCLI(t, WithEnv(
"COMPOSE_PROJECT_NAME=recreate-no-deps",
))
res := c.RunDockerComposeCmdNoCheck(t, "-f", "fixtures/dependencies/recreate-no-deps.yaml", "up", "-d")
res.Assert(t, icmd.Success)
res = c.RunDockerComposeCmdNoCheck(t, "-f", "fixtures/dependencies/recreate-no-deps.yaml", "up", "-d", "--force-recreate", "--no-deps", "my-service")
res.Assert(t, icmd.Success)
RequireServiceState(t, c, "my-service", "running")
c.RunDockerComposeCmd(t, "down")
}