diff --git a/aci/compose.go b/aci/compose.go index b33c29049..f9248f12a 100644 --- a/aci/compose.go +++ b/aci/compose.go @@ -207,6 +207,6 @@ func (cs *aciComposeService) Kill(ctx context.Context, project *types.Project, o return errdefs.ErrNotImplemented } -func (cs *aciComposeService) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) error { - return errdefs.ErrNotImplemented +func (cs *aciComposeService) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) (int, error) { + return 0, errdefs.ErrNotImplemented } diff --git a/api/client/compose.go b/api/client/compose.go index eacbc2ad8..5681e7947 100644 --- a/api/client/compose.go +++ b/api/client/compose.go @@ -80,6 +80,6 @@ func (c *composeService) Kill(ctx context.Context, project *types.Project, optio return errdefs.ErrNotImplemented } -func (c *composeService) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) error { - return errdefs.ErrNotImplemented +func (c *composeService) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) (int, error) { + return 0, errdefs.ErrNotImplemented } diff --git a/api/compose/api.go b/api/compose/api.go index 417edd699..b572c4a58 100644 --- a/api/compose/api.go +++ b/api/compose/api.go @@ -52,7 +52,7 @@ type Service interface { // Kill executes the equivalent to a `compose kill` Kill(ctx context.Context, project *types.Project, options KillOptions) error // RunOneOffContainer creates a service oneoff container and starts its dependencies - RunOneOffContainer(ctx context.Context, project *types.Project, opts RunOptions) error + RunOneOffContainer(ctx context.Context, project *types.Project, opts RunOptions) (int, error) } // CreateOptions group options of the Create API diff --git a/cli/cmd/compose/run.go b/cli/cmd/compose/run.go index 1fd6ef74c..b94bbc948 100644 --- a/cli/cmd/compose/run.go +++ b/cli/cmd/compose/run.go @@ -26,6 +26,7 @@ import ( "github.com/docker/compose-cli/api/client" "github.com/docker/compose-cli/api/compose" "github.com/docker/compose-cli/api/progress" + "github.com/docker/compose-cli/cli/cmd" ) type runOptions struct { @@ -85,7 +86,11 @@ func runRun(ctx context.Context, opts runOptions) error { Writer: os.Stdout, Reader: os.Stdin, } - return c.ComposeService().RunOneOffContainer(ctx, project, runOpts) + exitCode, err := c.ComposeService().RunOneOffContainer(ctx, project, runOpts) + if exitCode != 0 { + return cmd.ExitCodeError{ExitCode: exitCode} + } + return err } func startDependencies(ctx context.Context, c *client.Client, project types.Project, requestedServiceName string) error { diff --git a/ecs/local/compose.go b/ecs/local/compose.go index ce5710a17..c285e02cc 100644 --- a/ecs/local/compose.go +++ b/ecs/local/compose.go @@ -172,6 +172,6 @@ func (e ecsLocalSimulation) Ps(ctx context.Context, projectName string, options func (e ecsLocalSimulation) List(ctx context.Context) ([]compose.Stack, error) { return e.compose.List(ctx) } -func (e ecsLocalSimulation) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) error { - return errors.Wrap(errdefs.ErrNotImplemented, "use docker-compose run") +func (e ecsLocalSimulation) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) (int, error) { + return 0, errors.Wrap(errdefs.ErrNotImplemented, "use docker-compose run") } diff --git a/ecs/run.go b/ecs/run.go index 4699ec411..f2e7811a1 100644 --- a/ecs/run.go +++ b/ecs/run.go @@ -25,6 +25,6 @@ import ( "github.com/docker/compose-cli/api/errdefs" ) -func (b *ecsAPIService) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) error { - return errdefs.ErrNotImplemented +func (b *ecsAPIService) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) (int, error) { + return 0, errdefs.ErrNotImplemented } diff --git a/kube/compose.go b/kube/compose.go index f6bf9e90f..a31c8c221 100644 --- a/kube/compose.go +++ b/kube/compose.go @@ -194,6 +194,6 @@ func (s *composeService) Kill(ctx context.Context, project *types.Project, optio } // RunOneOffContainer creates a service oneoff container and starts its dependencies -func (s *composeService) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) error { - return errdefs.ErrNotImplemented +func (s *composeService) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) (int, error) { + return 0, errdefs.ErrNotImplemented } diff --git a/local/compose/run.go b/local/compose/run.go index 8c98bc3c5..bd9c7b9f4 100644 --- a/local/compose/run.go +++ b/local/compose/run.go @@ -20,17 +20,16 @@ import ( "context" "fmt" - "github.com/compose-spec/compose-go/types" - apitypes "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/filters" - "golang.org/x/sync/errgroup" - "github.com/docker/compose-cli/api/compose" + "github.com/compose-spec/compose-go/types" + apitypes "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/filters" moby "github.com/docker/docker/pkg/stringid" ) -func (s *composeService) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) error { +func (s *composeService) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) (int, error) { originalServices := project.Services var requestedService types.ServiceConfig for _, service := range originalServices { @@ -53,23 +52,23 @@ func (s *composeService) RunOneOffContainer(ctx context.Context, project *types. requestedService.Labels = requestedService.Labels.Add(oneoffLabel, "True") if err := s.ensureImagesExists(ctx, project); err != nil { // all dependencies already checked, but might miss requestedService img - return err + return 0, err } if err := s.waitDependencies(ctx, project, requestedService); err != nil { - return err + return 0, err } if err := s.createContainer(ctx, project, requestedService, requestedService.ContainerName, 1, opts.AutoRemove); err != nil { - return err + return 0, err } containerID := requestedService.ContainerName if opts.Detach { err := s.apiClient.ContainerStart(ctx, containerID, apitypes.ContainerStartOptions{}) if err != nil { - return err + return 0, err } fmt.Fprintln(opts.Writer, containerID) - return nil + return 0, nil } containers, err := s.apiClient.ContainerList(ctx, apitypes.ContainerListOptions{ @@ -79,16 +78,25 @@ func (s *composeService) RunOneOffContainer(ctx context.Context, project *types. All: true, }) if err != nil { - return err + return 0, err } oneoffContainer := containers[0] - eg := errgroup.Group{} - eg.Go(func() error { - return s.attachContainerStreams(ctx, oneoffContainer, true, opts.Reader, opts.Writer) - }) - - if err = s.apiClient.ContainerStart(ctx, containerID, apitypes.ContainerStartOptions{}); err != nil { - return err + err = s.attachContainerStreams(ctx, oneoffContainer, true, opts.Reader, opts.Writer) + if err != nil { + return 0, err } - return eg.Wait() + + err = s.apiClient.ContainerStart(ctx, containerID, apitypes.ContainerStartOptions{}) + if err != nil { + return 0, err + } + + statusC, errC := s.apiClient.ContainerWait(context.Background(), oneoffContainer.ID, container.WaitConditionNotRunning) + select { + case status := <-statusC: + return int(status.StatusCode), nil + case err := <-errC: + return 0, err + } + }