Explorar o código

Merge pull request #9173 from KoditkarVedant/8768-avoid-pulling-same-image-multiple-times

Avoid pulling same images multiple times ⚡️
Laura Brehm %!s(int64=3) %!d(string=hai) anos
pai
achega
f880b4129c

+ 26 - 2
pkg/compose/pull.go

@@ -21,6 +21,7 @@ import (
 	"encoding/base64"
 	"encoding/json"
 	"errors"
+	"fmt"
 	"io"
 	"strings"
 
@@ -64,13 +65,16 @@ func (s *composeService) pull(ctx context.Context, project *types.Project, opts
 	eg, ctx := errgroup.WithContext(ctx)
 
 	var mustBuild []string
+
+	imagesBeingPulled := map[string]string{}
+
 	for _, service := range project.Services {
 		service := service
 		if service.Image == "" {
 			w.Event(progress.Event{
 				ID:     service.Name,
 				Status: progress.Done,
-				Text:   "Skipped",
+				Text:   "Skipped - No image to be pulled",
 			})
 			continue
 		}
@@ -88,12 +92,32 @@ func (s *composeService) pull(ctx context.Context, project *types.Project, opts
 				w.Event(progress.Event{
 					ID:     service.Name,
 					Status: progress.Done,
-					Text:   "Exists",
+					Text:   "Skipped - Image is already present locally",
+				})
+				continue
+			}
+		default:
+			if _, ok := images[service.Image]; ok {
+				w.Event(progress.Event{
+					ID:     service.Name,
+					Status: progress.Done,
+					Text:   "Skipped - Image is already present locally",
 				})
 				continue
 			}
 		}
 
+		if s, ok := imagesBeingPulled[service.Image]; ok {
+			w.Event(progress.Event{
+				ID:     service.Name,
+				Status: progress.Done,
+				Text:   fmt.Sprintf("Skipped - Image is already being pulled by %v", s),
+			})
+			continue
+		}
+
+		imagesBeingPulled[service.Image] = service.Name
+
 		eg.Go(func() error {
 			_, err := s.pullServiceImage(ctx, service, info, s.configFile(), w, false)
 			if err != nil {

+ 42 - 4
pkg/e2e/compose_test.go

@@ -122,11 +122,49 @@ func TestLocalComposeUp(t *testing.T) {
 func TestComposePull(t *testing.T) {
 	c := NewParallelCLI(t)
 
-	res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/simple-composefile", "pull")
-	output := res.Combined()
+	t.Run("Verify image pulled", func(t *testing.T) {
+		// cleanup existing images
+		c.RunDockerComposeCmd(t, "--project-directory", "fixtures/compose-pull/simple", "down", "--rmi", "all")
 
-	assert.Assert(t, strings.Contains(output, "simple Pulled"))
-	assert.Assert(t, strings.Contains(output, "another Pulled"))
+		res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/compose-pull/simple", "pull")
+		output := res.Combined()
+
+		assert.Assert(t, strings.Contains(output, "simple Pulled"))
+		assert.Assert(t, strings.Contains(output, "another Pulled"))
+	})
+
+	t.Run("Verify a image is pulled once", func(t *testing.T) {
+		// cleanup existing images
+		c.RunDockerComposeCmd(t, "--project-directory", "fixtures/compose-pull/duplicate-images", "down", "--rmi", "all")
+
+		res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/compose-pull/duplicate-images", "pull")
+		output := res.Combined()
+
+		if strings.Contains(output, "another Pulled") {
+			assert.Assert(t, strings.Contains(output, "another Pulled"))
+			assert.Assert(t, strings.Contains(output, "Skipped - Image is already being pulled by another"))
+		} else {
+			assert.Assert(t, strings.Contains(output, "simple Pulled"))
+			assert.Assert(t, strings.Contains(output, "Skipped - Image is already being pulled by simple"))
+		}
+	})
+
+	t.Run("Verify skipped pull if image is already present locally", func(t *testing.T) {
+		// make sure the required image is present
+		c.RunDockerComposeCmd(t, "--project-directory", "fixtures/compose-pull/image-present-locally", "pull")
+
+		res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/compose-pull/image-present-locally", "pull")
+		output := res.Combined()
+
+		assert.Assert(t, strings.Contains(output, "Skipped - Image is already present locally"))
+	})
+
+	t.Run("Verify skipped no image to be pulled", func(t *testing.T) {
+		res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/compose-pull/no-image-name-given", "pull")
+		output := res.Combined()
+
+		assert.Assert(t, strings.Contains(output, "Skipped - No image to be pulled"))
+	})
 }
 
 func TestDownComposefileInParentFolder(t *testing.T) {

+ 7 - 0
pkg/e2e/fixtures/compose-pull/duplicate-images/docker-compose.yaml

@@ -0,0 +1,7 @@
+services:
+  simple:
+    image: alpine:3.13
+    command: top
+  another:
+    image: alpine:3.13
+    command: top

+ 4 - 0
pkg/e2e/fixtures/compose-pull/image-present-locally/docker-compose.yaml

@@ -0,0 +1,4 @@
+services:
+  simple:
+    image: alpine:3.13.12
+    command: top

+ 3 - 0
pkg/e2e/fixtures/compose-pull/no-image-name-given/docker-compose.yaml

@@ -0,0 +1,3 @@
+services:
+  no-image-service:
+    build: .

+ 7 - 0
pkg/e2e/fixtures/compose-pull/simple/docker-compose.yaml

@@ -0,0 +1,7 @@
+services:
+  simple:
+    image: alpine:3.14
+    command: top
+  another:
+    image: alpine:3.15
+    command: top