From 241c0811892aa2f7b3441508196b3bb1354d7641 Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Wed, 3 Sep 2025 16:20:13 +0200 Subject: [PATCH] let monitor know we are restarting a container Signed-off-by: Nicolas De Loof --- pkg/compose/compose.go | 2 ++ pkg/compose/monitor.go | 28 +++++++++++++++++++++------- pkg/compose/restart.go | 5 +++++ pkg/compose/up.go | 1 + 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/pkg/compose/compose.go b/pkg/compose/compose.go index 10c32255493..58a327d46aa 100644 --- a/pkg/compose/compose.go +++ b/pkg/compose/compose.go @@ -70,6 +70,8 @@ type composeService struct { clock clockwork.Clock maxConcurrency int dryRun bool + + Monitor *monitor } // Close releases any connections/resources held by the underlying clients. diff --git a/pkg/compose/monitor.go b/pkg/compose/monitor.go index 6952b4e6816..7806cc19d81 100644 --- a/pkg/compose/monitor.go +++ b/pkg/compose/monitor.go @@ -35,15 +35,17 @@ type monitor struct { api client.APIClient project string // services tells us which service to consider and those we can ignore, maybe ran by a concurrent compose command - services map[string]bool - listeners []api.ContainerEventListener + services map[string]bool + listeners []api.ContainerEventListener + restarting chan string } func newMonitor(api client.APIClient, project string) *monitor { return &monitor{ - api: api, - project: project, - services: map[string]bool{}, + api: api, + project: project, + services: map[string]bool{}, + restarting: make(chan string), } } @@ -91,6 +93,8 @@ func (c *monitor) Start(ctx context.Context) error { select { case err := <-errCh: return err + case ctr := <-c.restarting: + restarting.Add(ctr) case event := <-evtCh: if len(c.services) > 0 && !c.services[event.Actor.Attributes[api.ServiceLabel]] { continue @@ -100,6 +104,8 @@ func (c *monitor) Start(ctx context.Context) error { return err } + logrus.Debugf("event: Container %s %s", ctr.Name, event.Action) + switch event.Action { case events.ActionCreate: if len(c.services) == 0 || c.services[ctr.Labels[api.ServiceLabel]] { @@ -140,7 +146,9 @@ func (c *monitor) Start(ctx context.Context) error { // when a container is in restarting phase, and we stop the application (abort-on-container-exit) // we won't get any additional start+die events, just this stop as a proof container is down logrus.Debugf("container %s stopped", ctr.Name) - containers.Remove(ctr.ID) + if !restarting.Has(ctr.ID) { + containers.Remove(ctr.ID) + } case events.ActionDie: logrus.Debugf("container %s exited with code %d", ctr.Name, ctr.ExitCode) inspect, err := c.api.ContainerInspect(ctx, event.Actor.ID) @@ -165,7 +173,9 @@ func (c *monitor) Start(ctx context.Context) error { for _, listener := range c.listeners { listener(newContainerEvent(event.TimeNano, ctr, api.ContainerEventExited)) } - containers.Remove(ctr.ID) + if !restarting.Has(ctr.ID) { + containers.Remove(ctr.ID) + } } } } @@ -216,3 +226,7 @@ func (c *monitor) getContainerSummary(event events.Message) (*api.ContainerSumma func (c *monitor) withListener(listener api.ContainerEventListener) { c.listeners = append(c.listeners, listener) } + +func (c *monitor) Restarting(id string) { + c.restarting <- id +} diff --git a/pkg/compose/restart.go b/pkg/compose/restart.go index 4de9622310f..011edf492dd 100644 --- a/pkg/compose/restart.go +++ b/pkg/compose/restart.go @@ -89,6 +89,11 @@ func (s *composeService) restart(ctx context.Context, projectName string, option eventName := getContainerProgressName(ctr) w.Event(progress.RestartingEvent(eventName)) timeout := utils.DurationSecondToInt(options.Timeout) + if s.Monitor != nil { + // Let monitor know we are restarting container, as there's no way to guess based on engine events + s.Monitor.Restarting(ctr.ID) + } + err = s.apiClient().ContainerRestart(ctx, ctr.ID, container.StopOptions{Timeout: timeout}) if err != nil { return err diff --git a/pkg/compose/up.go b/pkg/compose/up.go index b3c4373e2c6..ea6d5f26772 100644 --- a/pkg/compose/up.go +++ b/pkg/compose/up.go @@ -271,6 +271,7 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options }) }) + s.Monitor = monitor eg.Go(func() error { err := monitor.Start(globalCtx) // cancel the global context to terminate signal-handler goroutines