Skip to content

Commit 9c03797

Browse files
jhrotkoglours
authored andcommitted
initial sync files that modified after image creation
Signed-off-by: Joana Hrotko <joana.hrotko@docker.com>
1 parent 485c0eb commit 9c03797

3 files changed

Lines changed: 83 additions & 24 deletions

File tree

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ require (
99
github.com/Microsoft/go-winio v0.6.2
1010
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
1111
github.com/buger/goterm v1.0.4
12-
github.com/compose-spec/compose-go/v2 v2.1.5
12+
github.com/compose-spec/compose-go/v2 v2.1.6
1313
github.com/containerd/containerd v1.7.20
1414
github.com/containerd/platforms v0.2.1
1515
github.com/davecgh/go-spew v1.1.1

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+g
8585
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
8686
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE=
8787
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
88-
github.com/compose-spec/compose-go/v2 v2.1.5 h1:6YoC9ik3NXdSYtgRn51EMZ2DxzGPyGjZ8M0B7mXTXeQ=
89-
github.com/compose-spec/compose-go/v2 v2.1.5/go.mod h1:lFN0DrMxIncJGYAXTfWuajfwj5haBJqrBkarHcnjJKc=
88+
github.com/compose-spec/compose-go/v2 v2.1.6 h1:d0Cs0DffmOwmSzs0YPHwKCskknGq2jfGg4uGowlEpps=
89+
github.com/compose-spec/compose-go/v2 v2.1.6/go.mod h1:lFN0DrMxIncJGYAXTfWuajfwj5haBJqrBkarHcnjJKc=
9090
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
9191
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
9292
github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro=

pkg/compose/watch.go

Lines changed: 80 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,10 @@ func (s *composeService) watch(ctx context.Context, syncChannel chan bool, proje
163163
success, err := trigger.Extensions.Get("x-initialSync", &initialSync)
164164
if err == nil && success && initialSync && (trigger.Action == types.WatchActionSync || trigger.Action == types.WatchActionSyncRestart) {
165165
// Need to check initial files are in container that are meant to be synched from watch action
166-
s.initialSync(ctx, service, trigger, ignore, syncer)
166+
err := s.initialSync(ctx, project, service, trigger, ignore, syncer)
167+
if err != nil {
168+
return err
169+
}
167170
}
168171
}
169172
paths = append(paths, trigger.Path)
@@ -583,29 +586,43 @@ func (s *composeService) pruneDanglingImagesOnRebuild(ctx context.Context, proje
583586
}
584587
}
585588

586-
func (s *composeService) initialSync(ctx context.Context, service types.ServiceConfig, trigger types.Trigger, ignore watch.PathMatcher, syncer sync.Syncer) error {
587-
dockerFileIgnore, _ := watch.NewDockerPatternMatcher("/", []string{"Dockerfile", "*compose*.y*ml"})
588-
triggerIgnore, _ := watch.NewDockerPatternMatcher("/", trigger.Ignore)
589+
// Walks develop.watch.path and checks which files should be copied inside the container
590+
// ignores develop.watch.ignore, Dockerfile, compose files, bind mounted paths and .git
591+
func (s *composeService) initialSync(ctx context.Context, project *types.Project, service types.ServiceConfig, trigger types.Trigger, ignore watch.PathMatcher, syncer sync.Syncer) error {
592+
dockerFileIgnore, err := watch.NewDockerPatternMatcher("/", []string{"Dockerfile", "*compose*.y*ml"})
593+
if err != nil {
594+
return err
595+
}
596+
triggerIgnore, err := watch.NewDockerPatternMatcher("/", trigger.Ignore)
597+
if err != nil {
598+
return err
599+
}
589600
ignoreInitialSync := watch.NewCompositeMatcher(ignore, dockerFileIgnore, triggerIgnore)
590601

591-
pathsToCopy, err := initialSyncFiles(service, trigger, ignoreInitialSync)
602+
pathsToCopy, err := s.initialSyncFiles(ctx, project, service, trigger, ignoreInitialSync)
592603
if err != nil {
593604
return err
594605
}
595606

596607
return syncer.Sync(ctx, service, pathsToCopy)
597608
}
598609

599-
func initialSyncFiles(service types.ServiceConfig, trigger types.Trigger, ignore watch.PathMatcher) ([]sync.PathMapping, error) {
610+
// Syncs files from develop.watch.path if thy have been modified after the image has been created
611+
//
612+
//nolint:gocyclo
613+
func (s *composeService) initialSyncFiles(ctx context.Context, project *types.Project, service types.ServiceConfig, trigger types.Trigger, ignore watch.PathMatcher) ([]sync.PathMapping, error) {
600614
fi, err := os.Stat(trigger.Path)
601615
if err != nil {
602616
return nil, err
603617
}
604-
618+
timeImageCreated, err := s.imageCreatedTime(ctx, project, service.Name)
619+
if err != nil {
620+
return nil, err
621+
}
605622
var pathsToCopy []sync.PathMapping
606623
switch mode := fi.Mode(); {
607624
case mode.IsDir():
608-
// do directory stuff
625+
// process directory
609626
err = filepath.WalkDir(trigger.Path, func(path string, d fs.DirEntry, err error) error {
610627
if err != nil {
611628
// handle possible path err, just in case...
@@ -615,34 +632,76 @@ func initialSyncFiles(service types.ServiceConfig, trigger types.Trigger, ignore
615632
// walk starts at the root directory
616633
return nil
617634
}
618-
rel, _ := filepath.Rel(trigger.Path, path)
619-
if shouldIgnoreOrSkip(filepath.Base(path), ignore) || checkIfPathAlreadyBindMounted(path, service.Volumes) {
635+
if shouldIgnore(filepath.Base(path), ignore) || checkIfPathAlreadyBindMounted(path, service.Volumes) {
620636
// By definition sync ignores bind mounted paths
621637
if d.IsDir() {
622-
return fs.SkipDir // ignore or skip folder
638+
// skip folder
639+
return fs.SkipDir
623640
}
624-
return nil // ignore or skip file
641+
return nil // skip file
642+
}
643+
info, err := d.Info()
644+
if err != nil {
645+
return err
646+
}
647+
if !d.IsDir() {
648+
if info.ModTime().Before(timeImageCreated) {
649+
// skip file if it was modified before image creation
650+
return nil
651+
}
652+
rel, err := filepath.Rel(trigger.Path, path)
653+
if err != nil {
654+
return err
655+
}
656+
// only copy files (and not full directories)
657+
pathsToCopy = append(pathsToCopy, sync.PathMapping{
658+
HostPath: path,
659+
ContainerPath: filepath.Join(trigger.Target, rel),
660+
})
625661
}
626-
pathsToCopy = append(pathsToCopy, sync.PathMapping{
627-
HostPath: path,
628-
ContainerPath: filepath.Join(trigger.Target, rel),
629-
})
630662
return nil
631663
})
632664
case mode.IsRegular():
633-
// do file stuff
634-
if !shouldIgnoreOrSkip(filepath.Base(trigger.Path), ignore) && !checkIfPathAlreadyBindMounted(trigger.Path, service.Volumes) {
665+
// process file
666+
if fi.ModTime().After(timeImageCreated) && !shouldIgnore(filepath.Base(trigger.Path), ignore) && !checkIfPathAlreadyBindMounted(trigger.Path, service.Volumes) {
635667
pathsToCopy = append(pathsToCopy, sync.PathMapping{
636668
HostPath: trigger.Path,
637669
ContainerPath: trigger.Target,
638670
})
639671
}
640672
}
641-
return pathsToCopy, nil
673+
return pathsToCopy, err
642674
}
643675

644-
func shouldIgnoreOrSkip(rel string, ignore watch.PathMatcher) bool {
645-
shouldIgnore, _ := ignore.Matches(rel)
676+
func shouldIgnore(name string, ignore watch.PathMatcher) bool {
677+
shouldIgnore, _ := ignore.Matches(name)
646678
// ignore files that match any ignore pattern
647679
return shouldIgnore
648680
}
681+
682+
// gets the image creation time for a service
683+
func (s *composeService) imageCreatedTime(ctx context.Context, project *types.Project, serviceName string) (time.Time, error) {
684+
containers, err := s.apiClient().ContainerList(ctx, container.ListOptions{
685+
All: true,
686+
Filters: filters.NewArgs(
687+
filters.Arg("label", fmt.Sprintf("%s=%s", api.ProjectLabel, project.Name)),
688+
filters.Arg("label", fmt.Sprintf("%s=%s", api.ServiceLabel, serviceName))),
689+
})
690+
if err != nil {
691+
return time.Now(), err
692+
}
693+
if len(containers) == 0 {
694+
return time.Now(), fmt.Errorf("Could not get created time for service's image")
695+
}
696+
697+
img, _, err := s.apiClient().ImageInspectWithRaw(ctx, containers[0].ImageID)
698+
if err != nil {
699+
return time.Now(), err
700+
}
701+
// Need to get oldest one?
702+
timeCreated, err := time.Parse(time.RFC3339Nano, img.Created)
703+
if err != nil {
704+
return time.Now(), err
705+
}
706+
return timeCreated, nil
707+
}

0 commit comments

Comments
 (0)