Skip to content

Commit f880b41

Browse files
authored
Merge pull request docker#9173 from KoditkarVedant/8768-avoid-pulling-same-image-multiple-times
Avoid pulling same images multiple times ⚡️
2 parents 832eee0 + a1d1911 commit f880b41

File tree

6 files changed

+89
-6
lines changed

6 files changed

+89
-6
lines changed

pkg/compose/pull.go

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"encoding/base64"
2222
"encoding/json"
2323
"errors"
24+
"fmt"
2425
"io"
2526
"strings"
2627

@@ -64,13 +65,16 @@ func (s *composeService) pull(ctx context.Context, project *types.Project, opts
6465
eg, ctx := errgroup.WithContext(ctx)
6566

6667
var mustBuild []string
68+
69+
imagesBeingPulled := map[string]string{}
70+
6771
for _, service := range project.Services {
6872
service := service
6973
if service.Image == "" {
7074
w.Event(progress.Event{
7175
ID: service.Name,
7276
Status: progress.Done,
73-
Text: "Skipped",
77+
Text: "Skipped - No image to be pulled",
7478
})
7579
continue
7680
}
@@ -88,12 +92,32 @@ func (s *composeService) pull(ctx context.Context, project *types.Project, opts
8892
w.Event(progress.Event{
8993
ID: service.Name,
9094
Status: progress.Done,
91-
Text: "Exists",
95+
Text: "Skipped - Image is already present locally",
96+
})
97+
continue
98+
}
99+
default:
100+
if _, ok := images[service.Image]; ok {
101+
w.Event(progress.Event{
102+
ID: service.Name,
103+
Status: progress.Done,
104+
Text: "Skipped - Image is already present locally",
92105
})
93106
continue
94107
}
95108
}
96109

110+
if s, ok := imagesBeingPulled[service.Image]; ok {
111+
w.Event(progress.Event{
112+
ID: service.Name,
113+
Status: progress.Done,
114+
Text: fmt.Sprintf("Skipped - Image is already being pulled by %v", s),
115+
})
116+
continue
117+
}
118+
119+
imagesBeingPulled[service.Image] = service.Name
120+
97121
eg.Go(func() error {
98122
_, err := s.pullServiceImage(ctx, service, info, s.configFile(), w, false)
99123
if err != nil {

pkg/e2e/compose_test.go

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,49 @@ func TestLocalComposeUp(t *testing.T) {
122122
func TestComposePull(t *testing.T) {
123123
c := NewParallelCLI(t)
124124

125-
res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/simple-composefile", "pull")
126-
output := res.Combined()
125+
t.Run("Verify image pulled", func(t *testing.T) {
126+
// cleanup existing images
127+
c.RunDockerComposeCmd(t, "--project-directory", "fixtures/compose-pull/simple", "down", "--rmi", "all")
127128

128-
assert.Assert(t, strings.Contains(output, "simple Pulled"))
129-
assert.Assert(t, strings.Contains(output, "another Pulled"))
129+
res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/compose-pull/simple", "pull")
130+
output := res.Combined()
131+
132+
assert.Assert(t, strings.Contains(output, "simple Pulled"))
133+
assert.Assert(t, strings.Contains(output, "another Pulled"))
134+
})
135+
136+
t.Run("Verify a image is pulled once", func(t *testing.T) {
137+
// cleanup existing images
138+
c.RunDockerComposeCmd(t, "--project-directory", "fixtures/compose-pull/duplicate-images", "down", "--rmi", "all")
139+
140+
res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/compose-pull/duplicate-images", "pull")
141+
output := res.Combined()
142+
143+
if strings.Contains(output, "another Pulled") {
144+
assert.Assert(t, strings.Contains(output, "another Pulled"))
145+
assert.Assert(t, strings.Contains(output, "Skipped - Image is already being pulled by another"))
146+
} else {
147+
assert.Assert(t, strings.Contains(output, "simple Pulled"))
148+
assert.Assert(t, strings.Contains(output, "Skipped - Image is already being pulled by simple"))
149+
}
150+
})
151+
152+
t.Run("Verify skipped pull if image is already present locally", func(t *testing.T) {
153+
// make sure the required image is present
154+
c.RunDockerComposeCmd(t, "--project-directory", "fixtures/compose-pull/image-present-locally", "pull")
155+
156+
res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/compose-pull/image-present-locally", "pull")
157+
output := res.Combined()
158+
159+
assert.Assert(t, strings.Contains(output, "Skipped - Image is already present locally"))
160+
})
161+
162+
t.Run("Verify skipped no image to be pulled", func(t *testing.T) {
163+
res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/compose-pull/no-image-name-given", "pull")
164+
output := res.Combined()
165+
166+
assert.Assert(t, strings.Contains(output, "Skipped - No image to be pulled"))
167+
})
130168
}
131169

132170
func TestDownComposefileInParentFolder(t *testing.T) {
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
services:
2+
simple:
3+
image: alpine:3.13
4+
command: top
5+
another:
6+
image: alpine:3.13
7+
command: top
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
services:
2+
simple:
3+
image: alpine:3.13.12
4+
command: top
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
services:
2+
no-image-service:
3+
build: .
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
services:
2+
simple:
3+
image: alpine:3.14
4+
command: top
5+
another:
6+
image: alpine:3.15
7+
command: top

0 commit comments

Comments
 (0)