diff --git a/pkg/compose/events.go b/pkg/compose/events.go index 172e3eec2..e664133be 100644 --- a/pkg/compose/events.go +++ b/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 { diff --git a/pkg/compose/logs.go b/pkg/compose/logs.go index 6a67d0143..f5a20b16a 100644 --- a/pkg/compose/logs.go +++ b/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 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...)) } - if len(options.Services) == 0 { - options.Services = project.ServiceNames() - } - - containers = containers.filter(isService(options.Services...)) eg, ctx := errgroup.WithContext(ctx) for _, c := range containers { c := c diff --git a/pkg/compose/start.go b/pkg/compose/start.go index bd2ebcacd..66520cd84 100644 --- a/pkg/compose/start.go +++ b/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 { diff --git a/pkg/e2e/fixtures/logs-test/compose.yaml b/pkg/e2e/fixtures/logs-test/compose.yaml index 2b2c4c3bb..f7440a238 100644 --- a/pkg/e2e/fixtures/logs-test/compose.yaml +++ b/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 diff --git a/pkg/e2e/logs_test.go b/pkg/e2e/logs_test.go index 42d47d050..e4e6f41d4 100644 --- a/pkg/e2e/logs_test.go +++ b/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") + + } +}