e8dc902781
Integration tests will now configure clients to propagate traces as well as create spans for all tests. Some extra changes were needed (or desired for trace propagation) in the test helpers to pass through tracing spans via context. Signed-off-by: Brian Goff <cpuguy83@gmail.com>
225 lines
6.6 KiB
Go
225 lines
6.6 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/docker/docker/api/types"
|
|
"github.com/docker/docker/client"
|
|
"github.com/docker/docker/pkg/stdcopy"
|
|
"github.com/docker/docker/testutil"
|
|
"github.com/docker/docker/testutil/request"
|
|
"gotest.tools/v3/assert"
|
|
)
|
|
|
|
func (s *DockerAPISuite) TestLogsAPIWithStdout(c *testing.T) {
|
|
out, _ := dockerCmd(c, "run", "-d", "-t", "busybox", "/bin/sh", "-c", "while true; do echo hello; sleep 1; done")
|
|
id := strings.TrimSpace(out)
|
|
assert.NilError(c, waitRun(id))
|
|
|
|
type logOut struct {
|
|
out string
|
|
err error
|
|
}
|
|
|
|
chLog := make(chan logOut, 1)
|
|
res, body, err := request.Get(testutil.GetContext(c), fmt.Sprintf("/containers/%s/logs?follow=1&stdout=1×tamps=1", id))
|
|
assert.NilError(c, err)
|
|
assert.Equal(c, res.StatusCode, http.StatusOK)
|
|
|
|
go func() {
|
|
defer body.Close()
|
|
out, err := bufio.NewReader(body).ReadString('\n')
|
|
if err != nil {
|
|
chLog <- logOut{"", err}
|
|
return
|
|
}
|
|
chLog <- logOut{strings.TrimSpace(out), err}
|
|
}()
|
|
|
|
select {
|
|
case l := <-chLog:
|
|
assert.NilError(c, l.err)
|
|
if !strings.HasSuffix(l.out, "hello") {
|
|
c.Fatalf("expected log output to container 'hello', but it does not")
|
|
}
|
|
case <-time.After(30 * time.Second):
|
|
c.Fatal("timeout waiting for logs to exit")
|
|
}
|
|
}
|
|
|
|
func (s *DockerAPISuite) TestLogsAPINoStdoutNorStderr(c *testing.T) {
|
|
name := "logs_test"
|
|
dockerCmd(c, "run", "-d", "-t", "--name", name, "busybox", "/bin/sh")
|
|
apiClient, err := client.NewClientWithOpts(client.FromEnv)
|
|
assert.NilError(c, err)
|
|
defer apiClient.Close()
|
|
|
|
_, err = apiClient.ContainerLogs(testutil.GetContext(c), name, types.ContainerLogsOptions{})
|
|
assert.ErrorContains(c, err, "Bad parameters: you must choose at least one stream")
|
|
}
|
|
|
|
// Regression test for #12704
|
|
func (s *DockerAPISuite) TestLogsAPIFollowEmptyOutput(c *testing.T) {
|
|
name := "logs_test"
|
|
t0 := time.Now()
|
|
dockerCmd(c, "run", "-d", "-t", "--name", name, "busybox", "sleep", "10")
|
|
|
|
_, body, err := request.Get(testutil.GetContext(c), fmt.Sprintf("/containers/%s/logs?follow=1&stdout=1&stderr=1&tail=all", name))
|
|
t1 := time.Now()
|
|
assert.NilError(c, err)
|
|
body.Close()
|
|
elapsed := t1.Sub(t0).Seconds()
|
|
if elapsed > 20.0 {
|
|
c.Fatalf("HTTP response was not immediate (elapsed %.1fs)", elapsed)
|
|
}
|
|
}
|
|
|
|
func (s *DockerAPISuite) TestLogsAPIContainerNotFound(c *testing.T) {
|
|
name := "nonExistentContainer"
|
|
resp, _, err := request.Get(testutil.GetContext(c), fmt.Sprintf("/containers/%s/logs?follow=1&stdout=1&stderr=1&tail=all", name))
|
|
assert.NilError(c, err)
|
|
assert.Equal(c, resp.StatusCode, http.StatusNotFound)
|
|
}
|
|
|
|
func (s *DockerAPISuite) TestLogsAPIUntilFutureFollow(c *testing.T) {
|
|
testRequires(c, DaemonIsLinux)
|
|
name := "logsuntilfuturefollow"
|
|
dockerCmd(c, "run", "-d", "--name", name, "busybox", "/bin/sh", "-c", "while true; do date +%s; sleep 1; done")
|
|
assert.NilError(c, waitRun(name))
|
|
|
|
untilSecs := 5
|
|
untilDur, err := time.ParseDuration(fmt.Sprintf("%ds", untilSecs))
|
|
assert.NilError(c, err)
|
|
until := daemonTime(c).Add(untilDur)
|
|
|
|
client, err := client.NewClientWithOpts(client.FromEnv)
|
|
if err != nil {
|
|
c.Fatal(err)
|
|
}
|
|
|
|
cfg := types.ContainerLogsOptions{Until: until.Format(time.RFC3339Nano), Follow: true, ShowStdout: true, Timestamps: true}
|
|
reader, err := client.ContainerLogs(testutil.GetContext(c), name, cfg)
|
|
assert.NilError(c, err)
|
|
|
|
type logOut struct {
|
|
out string
|
|
err error
|
|
}
|
|
|
|
chLog := make(chan logOut)
|
|
stop := make(chan struct{})
|
|
defer close(stop)
|
|
|
|
go func() {
|
|
bufReader := bufio.NewReader(reader)
|
|
defer reader.Close()
|
|
for i := 0; i < untilSecs; i++ {
|
|
out, _, err := bufReader.ReadLine()
|
|
if err != nil {
|
|
if err == io.EOF {
|
|
return
|
|
}
|
|
select {
|
|
case <-stop:
|
|
return
|
|
case chLog <- logOut{"", err}:
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
select {
|
|
case <-stop:
|
|
return
|
|
case chLog <- logOut{strings.TrimSpace(string(out)), err}:
|
|
}
|
|
}
|
|
}()
|
|
|
|
for i := 0; i < untilSecs; i++ {
|
|
select {
|
|
case l := <-chLog:
|
|
assert.NilError(c, l.err)
|
|
i, err := strconv.ParseInt(strings.Split(l.out, " ")[1], 10, 64)
|
|
assert.NilError(c, err)
|
|
assert.Assert(c, time.Unix(i, 0).UnixNano() <= until.UnixNano())
|
|
case <-time.After(20 * time.Second):
|
|
c.Fatal("timeout waiting for logs to exit")
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *DockerAPISuite) TestLogsAPIUntil(c *testing.T) {
|
|
testRequires(c, MinimumAPIVersion("1.34"))
|
|
name := "logsuntil"
|
|
dockerCmd(c, "run", "--name", name, "busybox", "/bin/sh", "-c", "for i in $(seq 1 3); do echo log$i; sleep 1; done")
|
|
|
|
client, err := client.NewClientWithOpts(client.FromEnv)
|
|
if err != nil {
|
|
c.Fatal(err)
|
|
}
|
|
|
|
extractBody := func(c *testing.T, cfg types.ContainerLogsOptions) []string {
|
|
reader, err := client.ContainerLogs(testutil.GetContext(c), name, cfg)
|
|
assert.NilError(c, err)
|
|
|
|
actualStdout := new(bytes.Buffer)
|
|
actualStderr := io.Discard
|
|
_, err = stdcopy.StdCopy(actualStdout, actualStderr, reader)
|
|
assert.NilError(c, err)
|
|
|
|
return strings.Split(actualStdout.String(), "\n")
|
|
}
|
|
|
|
// Get timestamp of second log line
|
|
allLogs := extractBody(c, types.ContainerLogsOptions{Timestamps: true, ShowStdout: true})
|
|
assert.Assert(c, len(allLogs) >= 3)
|
|
|
|
t, err := time.Parse(time.RFC3339Nano, strings.Split(allLogs[1], " ")[0])
|
|
assert.NilError(c, err)
|
|
until := t.Format(time.RFC3339Nano)
|
|
|
|
// Get logs until the timestamp of second line, i.e. first two lines
|
|
logs := extractBody(c, types.ContainerLogsOptions{Timestamps: true, ShowStdout: true, Until: until})
|
|
|
|
// Ensure log lines after cut-off are excluded
|
|
logsString := strings.Join(logs, "\n")
|
|
assert.Assert(c, !strings.Contains(logsString, "log3"), "unexpected log message returned, until=%v", until)
|
|
}
|
|
|
|
func (s *DockerAPISuite) TestLogsAPIUntilDefaultValue(c *testing.T) {
|
|
name := "logsuntildefaultval"
|
|
dockerCmd(c, "run", "--name", name, "busybox", "/bin/sh", "-c", "for i in $(seq 1 3); do echo log$i; done")
|
|
|
|
client, err := client.NewClientWithOpts(client.FromEnv)
|
|
if err != nil {
|
|
c.Fatal(err)
|
|
}
|
|
|
|
extractBody := func(c *testing.T, cfg types.ContainerLogsOptions) []string {
|
|
reader, err := client.ContainerLogs(testutil.GetContext(c), name, cfg)
|
|
assert.NilError(c, err)
|
|
|
|
actualStdout := new(bytes.Buffer)
|
|
actualStderr := io.Discard
|
|
_, err = stdcopy.StdCopy(actualStdout, actualStderr, reader)
|
|
assert.NilError(c, err)
|
|
|
|
return strings.Split(actualStdout.String(), "\n")
|
|
}
|
|
|
|
// Get timestamp of second log line
|
|
allLogs := extractBody(c, types.ContainerLogsOptions{Timestamps: true, ShowStdout: true})
|
|
|
|
// Test with default value specified and parameter omitted
|
|
defaultLogs := extractBody(c, types.ContainerLogsOptions{Timestamps: true, ShowStdout: true, Until: "0"})
|
|
assert.DeepEqual(c, defaultLogs, allLogs)
|
|
}
|