@@ -21,6 +21,7 @@ import (
21
21
"fmt"
22
22
"os"
23
23
"os/signal"
24
+ "slices"
24
25
"sync/atomic"
25
26
"syscall"
26
27
@@ -34,6 +35,7 @@ import (
34
35
"github.com/eiannone/keyboard"
35
36
"github.com/hashicorp/go-multierror"
36
37
"github.com/sirupsen/logrus"
38
+ "golang.org/x/sync/errgroup"
37
39
)
38
40
39
41
func (s * composeService ) Up (ctx context.Context , project * types.Project , options api.UpOptions ) error { //nolint:gocyclo
@@ -205,29 +207,44 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options
205
207
})
206
208
}
207
209
210
+ // use an independent context tied to the errgroup for background attach operations
211
+ // the primary context is still used for other operations
212
+ // this means that once any attach operation fails, all other attaches are cancelled,
213
+ // but an attach failing won't interfere with the rest of the start
214
+ _ , attachCtx := errgroup .WithContext (ctx )
215
+ containers , err := s .attach (attachCtx , project , printer .HandleEvent , options .Start .AttachTo )
216
+ if err != nil {
217
+ return err
218
+ }
219
+ attached := make ([]string , len (containers ))
220
+ for i , ctr := range containers {
221
+ attached [i ] = ctr .ID
222
+ }
223
+
208
224
monitor .withListener (func (event api.ContainerEvent ) {
209
225
if event .Type != api .ContainerEventStarted {
210
226
return
211
227
}
212
- if event .Restarting || event .Container .Labels [api .ContainerReplaceLabel ] != "" {
213
- eg .Go (func () error {
214
- ctr , err := s .apiClient ().ContainerInspect (ctx , event .ID )
215
- if err != nil {
216
- return err
217
- }
218
-
219
- err = s .doLogContainer (ctx , options .Start .Attach , event .Source , ctr , api.LogOptions {
220
- Follow : true ,
221
- Since : ctr .State .StartedAt ,
222
- })
223
- if errdefs .IsNotImplemented (err ) {
224
- // container may be configured with logging_driver: none
225
- // as container already started, we might miss the very first logs. But still better than none
226
- return s .doAttachContainer (ctx , event .Service , event .ID , event .Source , printer .HandleEvent )
227
- }
228
+ if slices .Contains (attached , event .ID ) {
229
+ return
230
+ }
231
+ eg .Go (func () error {
232
+ ctr , err := s .apiClient ().ContainerInspect (ctx , event .ID )
233
+ if err != nil {
228
234
return err
235
+ }
236
+
237
+ err = s .doLogContainer (ctx , options .Start .Attach , event .Source , ctr , api.LogOptions {
238
+ Follow : true ,
239
+ Since : ctr .State .StartedAt ,
229
240
})
230
- }
241
+ if errdefs .IsNotImplemented (err ) {
242
+ // container may be configured with logging_driver: none
243
+ // as container already started, we might miss the very first logs. But still better than none
244
+ return s .doAttachContainer (ctx , event .Service , event .ID , event .Source , printer .HandleEvent )
245
+ }
246
+ return err
247
+ })
231
248
})
232
249
233
250
eg .Go (func () error {
0 commit comments