@@ -79,26 +79,38 @@ func (s *composeService) runPreStart(ctx context.Context, project *types.Project
7979 logrus .Warnf ("service %q: failed to remove stale pre_start hook containers: %v" , service .Name , err )
8080 }
8181 for i , hook := range service .PreStart {
82- if err := s .runPreStartHook (ctx , project , service , ctr , i , hook , listener ); err != nil {
82+ created , err := s .createPreStartContainer (ctx , project , service , ctr , hook )
83+ if err != nil {
84+ return err
85+ }
86+ if err := s .execPreStartHook (ctx , service , i , created .ID , listener ); err != nil {
8387 return err
8488 }
89+ // Success: remove the hook container, mirroring the old AutoRemove behaviour
90+ // (including its anonymous volumes). A removal failure is logged but does not
91+ // gate service start — the hook already succeeded.
92+ if _ , removeErr := s .apiClient ().ContainerRemove (ctx , created .ID , client.ContainerRemoveOptions {RemoveVolumes : true }); removeErr != nil {
93+ logrus .Warnf ("service %q pre_start[%d]: failed to remove hook container %s: %v" , service .Name , i , created .ID , removeErr )
94+ }
8595 }
8696 return nil
8797}
8898
89- func (s * composeService ) runPreStartHook (
90- ctx context.Context , project * types.Project , service types.ServiceConfig ,
91- ctr container.Summary , index int , hook types.ServiceHook , listener api.ContainerEventListener ,
99+ // execPreStartHook starts an already-created hook container, streams its logs
100+ // and waits for its exit. It owns only execution-failure handling: a container
101+ // that never started or a run cancelled by the user is removed, a genuinely
102+ // failed hook is retained for post-mortem inspection. Removing the container
103+ // after a successful run is the caller's job — the container's lifecycle
104+ // belongs to whoever created it (the imperative runPreStart loop today, the
105+ // reconciliation plan once the executor runs hook nodes).
106+ func (s * composeService ) execPreStartHook (
107+ ctx context.Context , service types.ServiceConfig ,
108+ index int , containerID string , listener api.ContainerEventListener ,
92109) error {
93- created , err := s .createPreStartContainer (ctx , project , service , ctr , hook )
94- if err != nil {
95- return err
96- }
97-
98110 // Subscribe to wait before start to avoid missing the exit event for short-lived hooks.
99111 // WaitConditionNotRunning would match immediately because the container is still in
100112 // "created" state, so use WaitConditionNextExit to block until the run actually finishes.
101- waitRes := s .apiClient ().ContainerWait (ctx , created . ID , client.ContainerWaitOptions {
113+ waitRes := s .apiClient ().ContainerWait (ctx , containerID , client.ContainerWaitOptions {
102114 Condition : container .WaitConditionNextExit ,
103115 })
104116
@@ -108,13 +120,13 @@ func (s *composeService) runPreStartHook(
108120 // open cannot deadlock `<-logsDone`.
109121 logCtx , cancelLogs := context .WithCancel (ctx )
110122 defer cancelLogs ()
111- logsDone , getTail := s .streamPreStartLogs (logCtx , created . ID , service , index , listener )
123+ logsDone , getTail := s .streamPreStartLogs (logCtx , containerID , service , index , listener )
112124
113- if _ , err := s .apiClient ().ContainerStart (ctx , created . ID , client.ContainerStartOptions {}); err != nil {
125+ if _ , err := s .apiClient ().ContainerStart (ctx , containerID , client.ContainerStartOptions {}); err != nil {
114126 // AutoRemove is false, so we must remove the never-started container
115127 // explicitly. A failed removal is logged so the orphan is visible.
116- if _ , removeErr := s .apiClient ().ContainerRemove (ctx , created . ID , client.ContainerRemoveOptions {Force : true , RemoveVolumes : true }); removeErr != nil {
117- logrus .Warnf ("service %q pre_start[%d]: failed to remove orphan hook container %s: %v" , service .Name , index , created . ID , removeErr )
128+ if _ , removeErr := s .apiClient ().ContainerRemove (ctx , containerID , client.ContainerRemoveOptions {Force : true , RemoveVolumes : true }); removeErr != nil {
129+ logrus .Warnf ("service %q pre_start[%d]: failed to remove orphan hook container %s: %v" , service .Name , index , containerID , removeErr )
118130 }
119131 // Drain waitRes so the client's wait goroutine exits without having to
120132 // wait for the parent context to be canceled.
@@ -136,15 +148,15 @@ func (s *composeService) runPreStartHook(
136148 // and return the raw context error without decorating it with the tail or
137149 // retaining the container for post-mortem inspection.
138150 if ctx .Err () != nil {
139- if _ , removeErr := s .apiClient ().ContainerRemove (context .Background (), created . ID , client.ContainerRemoveOptions {Force : true , RemoveVolumes : true }); removeErr != nil {
140- logrus .Warnf ("service %q pre_start[%d]: failed to remove hook container %s after cancellation: %v" , service .Name , index , created . ID , removeErr )
151+ if _ , removeErr := s .apiClient ().ContainerRemove (context .Background (), containerID , client.ContainerRemoveOptions {Force : true , RemoveVolumes : true }); removeErr != nil {
152+ logrus .Warnf ("service %q pre_start[%d]: failed to remove hook container %s after cancellation: %v" , service .Name , index , containerID , removeErr )
141153 }
142154 return waitErr
143155 }
144156 // Genuine hook failure: retain the container so the operator can run
145157 // `docker logs <id>` and `docker inspect <id>` to diagnose the failure.
146158 // Include the short container ID in the error to make it actionable.
147- shortID := created . ID
159+ shortID := containerID
148160 if len (shortID ) > 12 {
149161 shortID = shortID [:12 ]
150162 }
@@ -153,12 +165,6 @@ func (s *composeService) runPreStartHook(
153165 }
154166 return fmt .Errorf ("%w (hook container %s retained for inspection)" , waitErr , shortID )
155167 }
156- // Success: remove the hook container, mirroring the old AutoRemove behaviour
157- // (including its anonymous volumes). A removal failure is logged but does not
158- // gate service start — the hook already succeeded.
159- if _ , removeErr := s .apiClient ().ContainerRemove (ctx , created .ID , client.ContainerRemoveOptions {RemoveVolumes : true }); removeErr != nil {
160- logrus .Warnf ("service %q pre_start[%d]: failed to remove hook container %s: %v" , service .Name , index , created .ID , removeErr )
161- }
162168 return nil
163169}
164170
0 commit comments