Explorar o código

Merge pull request #10622 from ndeloof/logs_follow

fix `compose -p x logs -f` detect new services started after command
Guillaume Lours %!s(int64=2) %!d(string=hai) anos
pai
achega
7c3fe359b7

+ 2 - 3
pkg/compose/events.go

@@ -25,7 +25,6 @@ import (
 	"github.com/docker/docker/api/types/filters"
 
 	"github.com/docker/compose/v2/pkg/api"
-
 	"github.com/docker/compose/v2/pkg/utils"
 )
 
@@ -67,8 +66,8 @@ func (s *composeService) Events(ctx context.Context, projectName string, options
 			err := options.Consumer(api.Event{
 				Timestamp:  timestamp,
 				Service:    service,
-				Container:  event.ID,
-				Status:     event.Status,
+				Container:  event.Actor.ID,
+				Status:     event.Action,
 				Attributes: attributes,
 			})
 			if err != nil {

+ 4 - 11
pkg/compose/logs.go

@@ -45,19 +45,12 @@ func (s *composeService) Logs(
 		return err
 	}
 
-	project := options.Project
-	if project == nil {
-		project, err = s.getProjectWithResources(ctx, containers, projectName)
-		if err != nil {
-			return err
-		}
-	}
-
-	if len(options.Services) == 0 {
-		options.Services = project.ServiceNames()
+	if options.Project != nil && len(options.Services) == 0 {
+		// we run with an explicit compose.yaml, so only consider services defined in this file
+		options.Services = options.Project.ServiceNames()
+		containers = containers.filter(isService(options.Services...))
 	}
 
-	containers = containers.filter(isService(options.Services...))
 	eg, ctx := errgroup.WithContext(ctx)
 	for _, c := range containers {
 		c := c

+ 24 - 1
pkg/compose/start.go

@@ -155,13 +155,31 @@ func (s *composeService) watchContainers(ctx context.Context, //nolint:gocyclo
 		required = services
 	}
 
+	// predicate to tell if a container we receive event for should be considered or ignored
+	ofInterest := func(c moby.Container) bool {
+		if len(services) > 0 {
+			// we only watch some services
+			return utils.Contains(services, c.Labels[api.ServiceLabel])
+		}
+		return true
+	}
+
+	// predicate to tell if a container we receive event for should be watched until termination
+	isRequired := func(c moby.Container) bool {
+		if len(services) > 0 && len(required) > 0 {
+			// we only watch some services
+			return utils.Contains(required, c.Labels[api.ServiceLabel])
+		}
+		return true
+	}
+
 	var (
 		expected []string
 		watched  = map[string]int{}
 		replaced []string
 	)
 	for _, c := range containers {
-		if utils.Contains(required, c.Labels[api.ServiceLabel]) {
+		if isRequired(c) {
 			expected = append(expected, c.ID)
 		}
 		watched[c.ID] = 0
@@ -265,6 +283,11 @@ func (s *composeService) watchContainers(ctx context.Context, //nolint:gocyclo
 					if utils.Contains(expected, id) {
 						expected = append(expected, container.ID)
 					}
+				} else if ofInterest(container) {
+					watched[container.ID] = 1
+					if isRequired(container) {
+						expected = append(expected, container.ID)
+					}
 				}
 			}
 			if len(expected) == 0 {

+ 1 - 1
pkg/e2e/fixtures/logs-test/compose.yaml

@@ -1,7 +1,7 @@
 services:
   ping:
     image: alpine
-    command: ping localhost -c 1
+    command: ping localhost -c ${REPEAT:-1}
   hello:
     image: alpine
     command: echo hello

+ 42 - 0
pkg/e2e/logs_test.go

@@ -17,10 +17,13 @@
 package e2e
 
 import (
+	"fmt"
 	"strings"
 	"testing"
+	"time"
 
 	"gotest.tools/v3/assert"
+	"gotest.tools/v3/poll"
 
 	"gotest.tools/v3/icmd"
 )
@@ -56,3 +59,42 @@ func TestLocalComposeLogs(t *testing.T) {
 		_ = c.RunDockerComposeCmd(t, "--project-name", projectName, "down")
 	})
 }
+
+func TestLocalComposeLogsFollow(t *testing.T) {
+	c := NewCLI(t, WithEnv("REPEAT=20"))
+	const projectName = "compose-e2e-logs"
+	t.Cleanup(func() {
+		c.RunDockerComposeCmd(t, "--project-name", projectName, "down")
+	})
+
+	c.RunDockerComposeCmd(t, "-f", "./fixtures/logs-test/compose.yaml", "--project-name", projectName, "up", "-d", "ping")
+
+	cmd := c.NewDockerComposeCmd(t, "--project-name", projectName, "logs", "-f")
+	res := icmd.StartCmd(cmd)
+	t.Cleanup(func() {
+		_ = res.Cmd.Process.Kill()
+	})
+
+	expected := fmt.Sprintf("%s-ping-1 ", projectName)
+	poll.WaitOn(t, expectOutput(res, expected), poll.WithDelay(100*time.Millisecond), poll.WithTimeout(1*time.Second))
+
+	c.RunDockerComposeCmd(t, "-f", "./fixtures/logs-test/compose.yaml", "--project-name", projectName, "up", "-d")
+
+	expected = fmt.Sprintf("%s-hello-1 ", projectName)
+	poll.WaitOn(t, expectOutput(res, expected), poll.WithDelay(100*time.Millisecond), poll.WithTimeout(1*time.Second))
+
+	c.RunDockerComposeCmd(t, "-f", "./fixtures/logs-test/compose.yaml", "--project-name", projectName, "up", "-d", "--scale", "ping=2", "ping")
+
+	expected = fmt.Sprintf("%s-ping-2 ", projectName)
+	poll.WaitOn(t, expectOutput(res, expected), poll.WithDelay(100*time.Millisecond), poll.WithTimeout(20*time.Second))
+}
+
+func expectOutput(res *icmd.Result, expected string) func(t poll.LogT) poll.Result {
+	return func(t poll.LogT) poll.Result {
+		if strings.Contains(res.Stdout(), expected) {
+			return poll.Success()
+		}
+		return poll.Continue("condition not met")
+
+	}
+}