diff --git a/cli/cmd/compose/start.go b/cli/cmd/compose/start.go index 94d994c0..97792146 100644 --- a/cli/cmd/compose/start.go +++ b/cli/cmd/compose/start.go @@ -76,7 +76,7 @@ func runStart(ctx context.Context, opts startOptions, services []string) error { return err } - _, err = printer.run(ctx, false, func() error { + _, err = printer.run(ctx, false, "", func() error { ctx := context.Background() _, err := progress.Run(ctx, func(ctx context.Context) (string, error) { return "", c.ComposeService().Stop(ctx, project) diff --git a/cli/cmd/compose/up.go b/cli/cmd/compose/up.go index 60e74cc4..6585c8e9 100644 --- a/cli/cmd/compose/up.go +++ b/cli/cmd/compose/up.go @@ -52,6 +52,7 @@ type upOptions struct { noRecreate bool noStart bool cascadeStop bool + exitCodeFrom string } func (o upOptions) recreateStrategy() string { @@ -76,6 +77,9 @@ func upCommand(p *projectOptions, contextType string) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { switch contextType { case store.LocalContextType, store.DefaultContextType, store.EcsLocalSimulationContextType: + if opts.exitCodeFrom != "" { + opts.cascadeStop = true + } if opts.cascadeStop && opts.Detach { return fmt.Errorf("--abort-on-container-exit and --detach are incompatible") } @@ -102,6 +106,7 @@ func upCommand(p *projectOptions, contextType string) *cobra.Command { flags.BoolVar(&opts.noRecreate, "no-recreate", false, "If containers already exist, don't recreate them. Incompatible with --force-recreate.") flags.BoolVar(&opts.noStart, "no-start", false, "Don't start the services after creating them.") flags.BoolVar(&opts.cascadeStop, "abort-on-container-exit", false, "Stops all containers if any container was stopped. Incompatible with -d") + flags.StringVar(&opts.exitCodeFrom, "exit-code-from", "", "Return the exit code of the selected service container. Implies --abort-on-container-exit") } return upCmd @@ -179,7 +184,7 @@ func runCreateStart(ctx context.Context, opts upOptions, services []string) erro return err } - _, err = printer.run(ctx, opts.cascadeStop, stopFunc) + _, err = printer.run(ctx, opts.cascadeStop, opts.exitCodeFrom, stopFunc) // FIXME os.Exit return err } @@ -229,21 +234,32 @@ type printer struct { queue chan compose.ContainerEvent } -func (p printer) run(ctx context.Context, cascadeStop bool, stopFn func() error) (int, error) { //nolint:unparam +func (p printer) run(ctx context.Context, cascadeStop bool, exitCodeFrom string, stopFn func() error) (int, error) { //nolint:unparam consumer := formatter.NewLogConsumer(ctx, os.Stdout) + var aborting bool for { event := <-p.queue switch event.Type { case compose.ContainerEventExit: - consumer.Status(event.Service, event.Source, fmt.Sprintf("exited with code %d", event.ExitCode)) - if cascadeStop { + if !aborting { + consumer.Status(event.Service, event.Source, fmt.Sprintf("exited with code %d", event.ExitCode)) + } + if cascadeStop && !aborting { + aborting = true fmt.Println("Aborting on container exit...") err := stopFn() + if err != nil { + return 0, err + } + } + if exitCodeFrom == "" || exitCodeFrom == event.Service { logrus.Error(event.ExitCode) - return event.ExitCode, err + return event.ExitCode, nil } case compose.ContainerEventLog: - consumer.Log(event.Service, event.Source, event.Line) + if !aborting { + consumer.Log(event.Service, event.Source, event.Line) + } } } } diff --git a/local/compose/start.go b/local/compose/start.go index 7c4b2f07..83ecfeab 100644 --- a/local/compose/start.go +++ b/local/compose/start.go @@ -56,6 +56,7 @@ func (s *composeService) Start(ctx context.Context, project *types.Project, opti options.Attach <- compose.ContainerEvent{ Type: compose.ContainerEventExit, Source: getCanonicalContainerName(c), + Service: c.Labels[serviceLabel], ExitCode: int(status.StatusCode), } case err := <-errC: