зеркало из https://github.com/docker/compose-cli.git
docker compose exec to return command exit code
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
Родитель
38b4220bdb
Коммит
15eab93b31
|
@ -227,8 +227,8 @@ func (cs *aciComposeService) Remove(ctx context.Context, project *types.Project,
|
|||
return nil, errdefs.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (cs *aciComposeService) Exec(ctx context.Context, project *types.Project, opts compose.RunOptions) error {
|
||||
return errdefs.ErrNotImplemented
|
||||
func (cs *aciComposeService) Exec(ctx context.Context, project *types.Project, opts compose.RunOptions) (int, error) {
|
||||
return 0, errdefs.ErrNotImplemented
|
||||
}
|
||||
func (cs *aciComposeService) Top(ctx context.Context, projectName string, services []string) ([]compose.ContainerProcSummary, error) {
|
||||
return nil, errdefs.ErrNotImplemented
|
||||
|
|
|
@ -92,8 +92,8 @@ func (c *composeService) Remove(ctx context.Context, project *types.Project, opt
|
|||
return nil, errdefs.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (c *composeService) Exec(ctx context.Context, project *types.Project, opts compose.RunOptions) error {
|
||||
return errdefs.ErrNotImplemented
|
||||
func (c *composeService) Exec(ctx context.Context, project *types.Project, opts compose.RunOptions) (int, error) {
|
||||
return 0, errdefs.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (c *composeService) Pause(ctx context.Context, project string, options compose.PauseOptions) error {
|
||||
|
|
|
@ -61,7 +61,7 @@ type Service interface {
|
|||
// Remove executes the equivalent to a `compose rm`
|
||||
Remove(ctx context.Context, project *types.Project, options RemoveOptions) ([]string, error)
|
||||
// Exec executes a command in a running service container
|
||||
Exec(ctx context.Context, project *types.Project, opts RunOptions) error
|
||||
Exec(ctx context.Context, project *types.Project, opts RunOptions) (int, error)
|
||||
// Pause executes the equivalent to a `compose pause`
|
||||
Pause(ctx context.Context, project string, options PauseOptions) error
|
||||
// UnPause executes the equivalent to a `compose unpause`
|
||||
|
|
|
@ -108,7 +108,7 @@ func (s *ServiceDelegator) Remove(ctx context.Context, project *types.Project, o
|
|||
}
|
||||
|
||||
//Exec implements Service interface
|
||||
func (s *ServiceDelegator) Exec(ctx context.Context, project *types.Project, options RunOptions) error {
|
||||
func (s *ServiceDelegator) Exec(ctx context.Context, project *types.Project, options RunOptions) (int, error) {
|
||||
return s.Delegate.Exec(ctx, project, options)
|
||||
}
|
||||
|
||||
|
|
|
@ -108,8 +108,8 @@ func (s NoImpl) Remove(ctx context.Context, project *types.Project, options Remo
|
|||
}
|
||||
|
||||
//Exec implements Service interface
|
||||
func (s NoImpl) Exec(ctx context.Context, project *types.Project, options RunOptions) error {
|
||||
return errdefs.ErrNotImplemented
|
||||
func (s NoImpl) Exec(ctx context.Context, project *types.Project, opts RunOptions) (int, error) {
|
||||
return 0, errdefs.ErrNotImplemented
|
||||
}
|
||||
|
||||
//Pause implements Service interface
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"os"
|
||||
|
||||
"github.com/containerd/console"
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose-cli/api/compose"
|
||||
|
@ -108,5 +109,13 @@ func runExec(ctx context.Context, backend compose.Service, opts execOpts) error
|
|||
execOpts.Writer = con
|
||||
execOpts.Reader = con
|
||||
}
|
||||
return backend.Exec(ctx, project, execOpts)
|
||||
exitCode, err := backend.Exec(ctx, project, execOpts)
|
||||
if exitCode != 0 {
|
||||
errMsg := ""
|
||||
if err != nil {
|
||||
errMsg = err.Error()
|
||||
}
|
||||
return cli.StatusError{StatusCode: exitCode, Status: errMsg}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -25,6 +25,6 @@ import (
|
|||
"github.com/docker/compose-cli/api/errdefs"
|
||||
)
|
||||
|
||||
func (b *ecsAPIService) Exec(ctx context.Context, project *types.Project, opts compose.RunOptions) error {
|
||||
return errdefs.ErrNotImplemented
|
||||
func (b *ecsAPIService) Exec(ctx context.Context, project *types.Project, opts compose.RunOptions) (int, error) {
|
||||
return 0, errdefs.ErrNotImplemented
|
||||
}
|
||||
|
|
|
@ -184,8 +184,8 @@ func (e ecsLocalSimulation) Remove(ctx context.Context, project *types.Project,
|
|||
return e.compose.Remove(ctx, project, options)
|
||||
}
|
||||
|
||||
func (e ecsLocalSimulation) Exec(ctx context.Context, project *types.Project, opts compose.RunOptions) error {
|
||||
return errdefs.ErrNotImplemented
|
||||
func (e ecsLocalSimulation) Exec(ctx context.Context, project *types.Project, opts compose.RunOptions) (int, error) {
|
||||
return 0, errdefs.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (e ecsLocalSimulation) Pause(ctx context.Context, project string, options compose.PauseOptions) error {
|
||||
|
|
|
@ -248,8 +248,8 @@ func (s *composeService) Remove(ctx context.Context, project *types.Project, opt
|
|||
}
|
||||
|
||||
// Exec executes a command in a running service container
|
||||
func (s *composeService) Exec(ctx context.Context, project *types.Project, opts compose.RunOptions) error {
|
||||
return errdefs.ErrNotImplemented
|
||||
func (s *composeService) Exec(ctx context.Context, project *types.Project, opts compose.RunOptions) (int, error) {
|
||||
return 0, errdefs.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (s *composeService) Pause(ctx context.Context, project string, options compose.PauseOptions) error {
|
||||
|
|
|
@ -28,10 +28,10 @@ import (
|
|||
"github.com/docker/compose-cli/api/compose"
|
||||
)
|
||||
|
||||
func (s *composeService) Exec(ctx context.Context, project *types.Project, opts compose.RunOptions) error {
|
||||
func (s *composeService) Exec(ctx context.Context, project *types.Project, opts compose.RunOptions) (int, error) {
|
||||
service, err := project.GetService(opts.Service)
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
|
||||
containers, err := s.apiClient.ContainerList(ctx, apitypes.ContainerListOptions{
|
||||
|
@ -42,10 +42,10 @@ func (s *composeService) Exec(ctx context.Context, project *types.Project, opts
|
|||
),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
if len(containers) < 1 {
|
||||
return fmt.Errorf("container %s not running", getContainerName(project.Name, service, opts.Index))
|
||||
return 0, fmt.Errorf("container %s not running", getContainerName(project.Name, service, opts.Index))
|
||||
}
|
||||
container := containers[0]
|
||||
|
||||
|
@ -63,11 +63,11 @@ func (s *composeService) Exec(ctx context.Context, project *types.Project, opts
|
|||
AttachStderr: true,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if opts.Detach {
|
||||
return s.apiClient.ContainerExecStart(ctx, exec.ID, apitypes.ExecStartCheck{
|
||||
return 0, s.apiClient.ContainerExecStart(ctx, exec.ID, apitypes.ExecStartCheck{
|
||||
Detach: true,
|
||||
Tty: opts.Tty,
|
||||
})
|
||||
|
@ -78,19 +78,19 @@ func (s *composeService) Exec(ctx context.Context, project *types.Project, opts
|
|||
Tty: opts.Tty,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
defer resp.Close()
|
||||
|
||||
if opts.Tty {
|
||||
s.monitorTTySize(ctx, exec.ID, s.apiClient.ContainerExecResize)
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
readChannel := make(chan error, 10)
|
||||
writeChannel := make(chan error, 10)
|
||||
readChannel := make(chan error)
|
||||
writeChannel := make(chan error)
|
||||
|
||||
go func() {
|
||||
_, err := io.Copy(opts.Writer, resp.Reader)
|
||||
|
@ -102,12 +102,23 @@ func (s *composeService) Exec(ctx context.Context, project *types.Project, opts
|
|||
writeChannel <- err
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case err := <-readChannel:
|
||||
return err
|
||||
case err := <-writeChannel:
|
||||
return err
|
||||
}
|
||||
select {
|
||||
case err = <-readChannel:
|
||||
break
|
||||
case err = <-writeChannel:
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return s.getExecExitStatus(ctx, exec.ID)
|
||||
}
|
||||
|
||||
func (s *composeService) getExecExitStatus(ctx context.Context, execID string) (int, error) {
|
||||
resp, err := s.apiClient.ContainerExecInspect(ctx, execID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return resp.ExitCode, nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
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 e2e
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/icmd"
|
||||
|
||||
. "github.com/docker/compose-cli/utils/e2e"
|
||||
)
|
||||
|
||||
func TestLocalComposeExec(t *testing.T) {
|
||||
c := NewParallelE2eCLI(t, binDir)
|
||||
|
||||
const projectName = "compose-e2e-exec"
|
||||
|
||||
c.RunDockerCmd("compose", "--project-directory", "fixtures/simple-composefile", "--project-name", projectName, "up", "-d")
|
||||
|
||||
t.Run("exec true", func(t *testing.T) {
|
||||
res := c.RunDockerOrExitError("exec", "compose-e2e-exec_simple_1", "/bin/true")
|
||||
res.Assert(t, icmd.Expected{ExitCode: 0})
|
||||
})
|
||||
|
||||
t.Run("exec false", func(t *testing.T) {
|
||||
res := c.RunDockerOrExitError("exec", "compose-e2e-exec_simple_1", "/bin/false")
|
||||
res.Assert(t, icmd.Expected{ExitCode: 1})
|
||||
})
|
||||
}
|
Загрузка…
Ссылка в новой задаче