1
1
/*
2
-
3
2
Copyright 2020 Docker Compose CLI authors
3
+
4
4
Licensed under the Apache License, Version 2.0 (the "License");
5
5
you may not use this file except in compliance with the License.
6
6
You may obtain a copy of the License at
@@ -17,13 +17,13 @@ package compose
17
17
import (
18
18
"context"
19
19
"fmt"
20
- "io/fs"
21
- "os"
22
20
"path"
23
21
"path/filepath"
24
22
"strings"
25
23
"time"
26
24
25
+ "github.com/docker/compose/v2/internal/sync"
26
+
27
27
"github.com/compose-spec/compose-go/types"
28
28
"github.com/jonboulle/clockwork"
29
29
"github.com/mitchellh/mapstructure"
@@ -54,11 +54,8 @@ type Trigger struct {
54
54
55
55
const quietPeriod = 2 * time .Second
56
56
57
- // fileMapping contains the Compose service and modified host system path.
58
- //
59
- // For file sync, the container path is also included.
60
- // For rebuild, there is no container path, so it is always empty.
61
- type fileMapping struct {
57
+ // fileEvent contains the Compose service and modified host system path.
58
+ type fileEvent struct {
62
59
// Service that the file event is for.
63
60
Service string
64
61
// HostPath that was created/modified/deleted outside the container.
@@ -67,17 +64,11 @@ type fileMapping struct {
67
64
// - C:\Users\moby\Documents\hello-world\main.go
68
65
// - /Users/moby/Documents/hello-world/main.go
69
66
HostPath string
70
- // ContainerPath for the target file inside the container (only populated
71
- // for sync events, not rebuild).
72
- //
73
- // This is the path as used in Docker CLI commands, e.g.
74
- // - /workdir/main.go
75
- ContainerPath string
76
67
}
77
68
78
69
func (s * composeService ) Watch (ctx context.Context , project * types.Project , services []string , _ api.WatchOptions ) error { //nolint: gocyclo
79
- needRebuild := make (chan fileMapping )
80
- needSync := make (chan fileMapping )
70
+ needRebuild := make (chan fileEvent )
71
+ needSync := make (chan sync. PathMapping )
81
72
82
73
_ , err := s .prepareProjectForBuild (project , nil )
83
74
if err != nil {
@@ -175,7 +166,7 @@ func (s *composeService) Watch(ctx context.Context, project *types.Project, serv
175
166
return eg .Wait ()
176
167
}
177
168
178
- func (s * composeService ) watch (ctx context.Context , name string , watcher watch.Notify , triggers []Trigger , needSync chan fileMapping , needRebuild chan fileMapping ) error {
169
+ func (s * composeService ) watch (ctx context.Context , name string , watcher watch.Notify , triggers []Trigger , needSync chan sync. PathMapping , needRebuild chan fileEvent ) error {
179
170
ignores := make ([]watch.PathMatcher , len (triggers ))
180
171
for i , trigger := range triggers {
181
172
ignore , err := watch .NewDockerPatternMatcher (trigger .Path , trigger .Ignore )
@@ -209,24 +200,25 @@ WATCH:
209
200
210
201
fmt .Fprintf (s .stdinfo (), "change detected on %s\n " , hostPath )
211
202
212
- f := fileMapping {
213
- HostPath : hostPath ,
214
- Service : name ,
215
- }
216
-
217
203
switch trigger .Action {
218
204
case WatchActionSync :
219
205
logrus .Debugf ("modified file %s triggered sync" , hostPath )
220
206
rel , err := filepath .Rel (trigger .Path , hostPath )
221
207
if err != nil {
222
208
return err
223
209
}
224
- // always use Unix-style paths for inside the container
225
- f .ContainerPath = path .Join (trigger .Target , rel )
226
- needSync <- f
210
+ needSync <- sync.PathMapping {
211
+ Service : name ,
212
+ HostPath : hostPath ,
213
+ // always use Unix-style paths for inside the container
214
+ ContainerPath : path .Join (trigger .Target , rel ),
215
+ }
227
216
case WatchActionRebuild :
228
217
logrus .Debugf ("modified file %s requires image to be rebuilt" , hostPath )
229
- needRebuild <- f
218
+ needRebuild <- fileEvent {
219
+ HostPath : hostPath ,
220
+ Service : name ,
221
+ }
230
222
default :
231
223
return fmt .Errorf ("watch action %q is not supported" , trigger )
232
224
}
@@ -304,57 +296,25 @@ func (s *composeService) makeRebuildFn(ctx context.Context, project *types.Proje
304
296
}
305
297
}
306
298
307
- func (s * composeService ) makeSyncFn (ctx context.Context , project * types.Project , needSync <- chan fileMapping ) func () error {
299
+ func (s * composeService ) makeSyncFn (
300
+ ctx context.Context ,
301
+ project * types.Project ,
302
+ needSync <- chan sync.PathMapping ,
303
+ ) func () error {
304
+ syncer := sync .NewDockerCopy (project .Name , s , s .stdinfo ())
305
+
308
306
return func () error {
309
307
for {
310
308
select {
311
309
case <- ctx .Done ():
312
310
return nil
313
- case opt := <- needSync :
314
- service , err := project .GetService (opt .Service )
311
+ case pathMapping := <- needSync :
312
+ service , err := project .GetService (pathMapping .Service )
315
313
if err != nil {
316
314
return err
317
315
}
318
- scale := 1
319
- if service .Deploy != nil && service .Deploy .Replicas != nil {
320
- scale = int (* service .Deploy .Replicas )
321
- }
322
-
323
- if fi , statErr := os .Stat (opt .HostPath ); statErr == nil {
324
- if fi .IsDir () {
325
- for i := 1 ; i <= scale ; i ++ {
326
- _ , err := s .Exec (ctx , project .Name , api.RunOptions {
327
- Service : opt .Service ,
328
- Command : []string {"mkdir" , "-p" , opt .ContainerPath },
329
- Index : i ,
330
- })
331
- if err != nil {
332
- logrus .Warnf ("failed to create %q from %s: %v" , opt .ContainerPath , opt .Service , err )
333
- }
334
- }
335
- fmt .Fprintf (s .stdinfo (), "%s created\n " , opt .ContainerPath )
336
- } else {
337
- err := s .Copy (ctx , project .Name , api.CopyOptions {
338
- Source : opt .HostPath ,
339
- Destination : fmt .Sprintf ("%s:%s" , opt .Service , opt .ContainerPath ),
340
- })
341
- if err != nil {
342
- return err
343
- }
344
- fmt .Fprintf (s .stdinfo (), "%s updated\n " , opt .ContainerPath )
345
- }
346
- } else if errors .Is (statErr , fs .ErrNotExist ) {
347
- for i := 1 ; i <= scale ; i ++ {
348
- _ , err := s .Exec (ctx , project .Name , api.RunOptions {
349
- Service : opt .Service ,
350
- Command : []string {"rm" , "-rf" , opt .ContainerPath },
351
- Index : i ,
352
- })
353
- if err != nil {
354
- logrus .Warnf ("failed to delete %q from %s: %v" , opt .ContainerPath , opt .Service , err )
355
- }
356
- }
357
- fmt .Fprintf (s .stdinfo (), "%s deleted from service\n " , opt .ContainerPath )
316
+ if err := syncer .Sync (ctx , service , []sync.PathMapping {pathMapping }); err != nil {
317
+ return err
358
318
}
359
319
}
360
320
}
@@ -363,7 +323,7 @@ func (s *composeService) makeSyncFn(ctx context.Context, project *types.Project,
363
323
364
324
type rebuildServices map [string ]utils.Set [string ]
365
325
366
- func debounce (ctx context.Context , clock clockwork.Clock , delay time.Duration , input <- chan fileMapping , fn func (services rebuildServices )) {
326
+ func debounce (ctx context.Context , clock clockwork.Clock , delay time.Duration , input <- chan fileEvent , fn func (services rebuildServices )) {
367
327
services := make (rebuildServices )
368
328
t := clock .NewTimer (delay )
369
329
defer t .Stop ()
0 commit comments