Fix #11710: Avoid to try to close channel twice after hitting Ctrl-C on compose up (#11719)

Ensure done channel is closed only once

Signed-off-by: Jaime Soriano Pastor <jaime.soriano@elastic.co>
This commit is contained in:
Jaime Soriano Pastor 2024-04-20 08:09:26 +02:00 committed by GitHub
parent fd532a37e7
commit 5682480726
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 13 additions and 13 deletions

View File

@ -21,6 +21,7 @@ import (
"fmt"
"os"
"os/signal"
"sync/atomic"
"syscall"
"github.com/compose-spec/compose-go/v2/types"
@ -65,9 +66,10 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options
// we might miss a signal while setting up the second channel read
// (this is also why signal.Notify is used vs signal.NotifyContext)
signalChan := make(chan os.Signal, 2)
signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
defer close(signalChan)
var isTerminated bool
signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
defer signal.Stop(signalChan)
var isTerminated atomic.Bool
printer := newLogPrinter(options.Start.Attach)
doneCh := make(chan bool)
@ -78,12 +80,11 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options
formatter.ClearLine()
fmt.Fprintln(s.stdinfo(), "Gracefully stopping... (press Ctrl+C again to force)")
eg.Go(func() error {
err := s.Stop(context.Background(), project.Name, api.StopOptions{
err := s.Stop(context.WithoutCancel(ctx), project.Name, api.StopOptions{
Services: options.Create.Services,
Project: project,
})
isTerminated = true
close(doneCh)
isTerminated.Store(true)
return err
})
first = false
@ -120,7 +121,7 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options
break
}
eg.Go(func() error {
err := s.kill(context.Background(), project.Name, api.KillOptions{
err := s.kill(context.WithoutCancel(ctx), project.Name, api.KillOptions{
Services: options.Create.Services,
Project: project,
All: true,
@ -165,18 +166,17 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options
})
}
// We don't use parent (cancelable) context as we manage sigterm to stop the stack
err = s.start(context.Background(), project.Name, options.Start, printer.HandleEvent)
if err != nil && !isTerminated { // Ignore error if the process is terminated
// We use the parent context without cancelation as we manage sigterm to stop the stack
err = s.start(context.WithoutCancel(ctx), project.Name, options.Start, printer.HandleEvent)
if err != nil && !isTerminated.Load() { // Ignore error if the process is terminated
return err
}
// Signal for the signal-handler goroutines to stop
close(doneCh)
printer.Stop()
if !isTerminated {
// signal for the signal-handler goroutines to stop
close(doneCh)
}
err = eg.Wait().ErrorOrNil()
if exitCode != 0 {
errMsg := ""