diff --git a/api/server/router/swarm/cluster_routes.go b/api/server/router/swarm/cluster_routes.go index 10250da24f..cb4ae3a7b3 100644 --- a/api/server/router/swarm/cluster_routes.go +++ b/api/server/router/swarm/cluster_routes.go @@ -237,10 +237,6 @@ func (sr *swarmRouter) getServiceLogs(ctx context.Context, w http.ResponseWriter OutStream: w, } - if !logsConfig.Follow { - return fmt.Errorf("Bad parameters: Only follow mode is currently supported") - } - if logsConfig.Details { return fmt.Errorf("Bad parameters: details is not currently supported") } diff --git a/daemon/cluster/cluster.go b/daemon/cluster/cluster.go index 6b8b9c3697..f9fe0706de 100644 --- a/daemon/cluster/cluster.go +++ b/daemon/cluster/cluster.go @@ -1275,7 +1275,7 @@ func (c *Cluster) ServiceLogs(ctx context.Context, input string, config *backend ServiceIDs: []string{service.ID}, }, Options: &swarmapi.LogSubscriptionOptions{ - Follow: true, + Follow: config.Follow, }, }) if err != nil { diff --git a/daemon/cluster/executor/container/adapter.go b/daemon/cluster/executor/container/adapter.go index 2e0306c772..010c18c5b3 100644 --- a/daemon/cluster/executor/container/adapter.go +++ b/daemon/cluster/executor/container/adapter.go @@ -419,7 +419,11 @@ func (c *containerAdapter) logs(ctx context.Context, options api.LogSubscription } chStarted := make(chan struct{}) - go c.backend.ContainerLogs(ctx, c.container.name(), apiOptions, chStarted) + go func() { + defer writer.Close() + c.backend.ContainerLogs(ctx, c.container.name(), apiOptions, chStarted) + }() + return reader, nil } diff --git a/integration-cli/docker_cli_service_logs_experimental_test.go b/integration-cli/docker_cli_service_logs_experimental_test.go index 2bb91c1d7d..e5003282e6 100644 --- a/integration-cli/docker_cli_service_logs_experimental_test.go +++ b/integration-cli/docker_cli_service_logs_experimental_test.go @@ -4,6 +4,7 @@ package main import ( "bufio" + "fmt" "io" "os/exec" "strings" @@ -24,7 +25,27 @@ func (s *DockerSwarmSuite) TestServiceLogs(c *check.C) { name := "TestServiceLogs" - out, err := d.Cmd("service", "create", "--name", name, "busybox", "sh", "-c", "while true; do echo log test; sleep 1; done") + out, err := d.Cmd("service", "create", "--name", name, "--restart-condition", "none", "busybox", "sh", "-c", "echo hello world") + c.Assert(err, checker.IsNil) + c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "") + + // make sure task has been deployed. + waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 1) + + out, err = d.Cmd("service", "logs", name) + fmt.Println(out) + c.Assert(err, checker.IsNil) + c.Assert(out, checker.Contains, "hello world") +} + +func (s *DockerSwarmSuite) TestServiceLogsFollow(c *check.C) { + testRequires(c, ExperimentalDaemon) + + d := s.AddDaemon(c, true, true) + + name := "TestServiceLogsFollow" + + out, err := d.Cmd("service", "create", "--name", name, "busybox", "sh", "-c", "while true; do echo log test; sleep 0.1; done") c.Assert(err, checker.IsNil) c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "") @@ -40,16 +61,26 @@ func (s *DockerSwarmSuite) TestServiceLogs(c *check.C) { // Make sure pipe is written to ch := make(chan *logMessage) + done := make(chan struct{}) go func() { reader := bufio.NewReader(r) - msg := &logMessage{} - msg.data, _, msg.err = reader.ReadLine() - ch <- msg + for { + msg := &logMessage{} + msg.data, _, msg.err = reader.ReadLine() + select { + case ch <- msg: + case <-done: + return + } + } }() - msg := <-ch - c.Assert(msg.err, checker.IsNil) - c.Assert(string(msg.data), checker.Contains, "log test") + for i := 0; i < 3; i++ { + msg := <-ch + c.Assert(msg.err, checker.IsNil) + c.Assert(string(msg.data), checker.Contains, "log test") + } + close(done) c.Assert(cmd.Process.Kill(), checker.IsNil) }