revisit `run` implementation as create, attach, start, wait

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
Nicolas De Loof 2021-02-12 17:33:59 +01:00
parent d56745cba4
commit 4462f12249
No known key found for this signature in database
GPG Key ID: 9858809D6F8F6E7E
8 changed files with 45 additions and 32 deletions

View File

@ -207,6 +207,6 @@ func (cs *aciComposeService) Kill(ctx context.Context, project *types.Project, o
return errdefs.ErrNotImplemented return errdefs.ErrNotImplemented
} }
func (cs *aciComposeService) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) error { func (cs *aciComposeService) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) (int, error) {
return errdefs.ErrNotImplemented return 0, errdefs.ErrNotImplemented
} }

View File

@ -80,6 +80,6 @@ func (c *composeService) Kill(ctx context.Context, project *types.Project, optio
return errdefs.ErrNotImplemented return errdefs.ErrNotImplemented
} }
func (c *composeService) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) error { func (c *composeService) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) (int, error) {
return errdefs.ErrNotImplemented return 0, errdefs.ErrNotImplemented
} }

View File

@ -52,7 +52,7 @@ type Service interface {
// Kill executes the equivalent to a `compose kill` // Kill executes the equivalent to a `compose kill`
Kill(ctx context.Context, project *types.Project, options KillOptions) error Kill(ctx context.Context, project *types.Project, options KillOptions) error
// RunOneOffContainer creates a service oneoff container and starts its dependencies // 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 // CreateOptions group options of the Create API

View File

@ -26,6 +26,7 @@ import (
"github.com/docker/compose-cli/api/client" "github.com/docker/compose-cli/api/client"
"github.com/docker/compose-cli/api/compose" "github.com/docker/compose-cli/api/compose"
"github.com/docker/compose-cli/api/progress" "github.com/docker/compose-cli/api/progress"
"github.com/docker/compose-cli/cli/cmd"
) )
type runOptions struct { type runOptions struct {
@ -85,7 +86,11 @@ func runRun(ctx context.Context, opts runOptions) error {
Writer: os.Stdout, Writer: os.Stdout,
Reader: os.Stdin, 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 { func startDependencies(ctx context.Context, c *client.Client, project types.Project, requestedServiceName string) error {

View File

@ -172,6 +172,6 @@ func (e ecsLocalSimulation) Ps(ctx context.Context, projectName string, options
func (e ecsLocalSimulation) List(ctx context.Context) ([]compose.Stack, error) { func (e ecsLocalSimulation) List(ctx context.Context) ([]compose.Stack, error) {
return e.compose.List(ctx) return e.compose.List(ctx)
} }
func (e ecsLocalSimulation) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) error { func (e ecsLocalSimulation) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) (int, error) {
return errors.Wrap(errdefs.ErrNotImplemented, "use docker-compose run") return 0, errors.Wrap(errdefs.ErrNotImplemented, "use docker-compose run")
} }

View File

@ -25,6 +25,6 @@ import (
"github.com/docker/compose-cli/api/errdefs" "github.com/docker/compose-cli/api/errdefs"
) )
func (b *ecsAPIService) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) error { func (b *ecsAPIService) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) (int, error) {
return errdefs.ErrNotImplemented return 0, errdefs.ErrNotImplemented
} }

View File

@ -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 // RunOneOffContainer creates a service oneoff container and starts its dependencies
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) {
return errdefs.ErrNotImplemented return 0, errdefs.ErrNotImplemented
} }

View File

@ -20,17 +20,16 @@ import (
"context" "context"
"fmt" "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/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" 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 originalServices := project.Services
var requestedService types.ServiceConfig var requestedService types.ServiceConfig
for _, service := range originalServices { for _, service := range originalServices {
@ -53,23 +52,23 @@ func (s *composeService) RunOneOffContainer(ctx context.Context, project *types.
requestedService.Labels = requestedService.Labels.Add(oneoffLabel, "True") requestedService.Labels = requestedService.Labels.Add(oneoffLabel, "True")
if err := s.ensureImagesExists(ctx, project); err != nil { // all dependencies already checked, but might miss requestedService img 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 { 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 { if err := s.createContainer(ctx, project, requestedService, requestedService.ContainerName, 1, opts.AutoRemove); err != nil {
return err return 0, err
} }
containerID := requestedService.ContainerName containerID := requestedService.ContainerName
if opts.Detach { if opts.Detach {
err := s.apiClient.ContainerStart(ctx, containerID, apitypes.ContainerStartOptions{}) err := s.apiClient.ContainerStart(ctx, containerID, apitypes.ContainerStartOptions{})
if err != nil { if err != nil {
return err return 0, err
} }
fmt.Fprintln(opts.Writer, containerID) fmt.Fprintln(opts.Writer, containerID)
return nil return 0, nil
} }
containers, err := s.apiClient.ContainerList(ctx, apitypes.ContainerListOptions{ containers, err := s.apiClient.ContainerList(ctx, apitypes.ContainerListOptions{
@ -79,16 +78,25 @@ func (s *composeService) RunOneOffContainer(ctx context.Context, project *types.
All: true, All: true,
}) })
if err != nil { if err != nil {
return err return 0, err
} }
oneoffContainer := containers[0] oneoffContainer := containers[0]
eg := errgroup.Group{} err = s.attachContainerStreams(ctx, oneoffContainer, true, opts.Reader, opts.Writer)
eg.Go(func() error { if err != nil {
return s.attachContainerStreams(ctx, oneoffContainer, true, opts.Reader, opts.Writer) return 0, err
})
if err = s.apiClient.ContainerStart(ctx, containerID, apitypes.ContainerStartOptions{}); err != nil {
return 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
}
} }