713c7d49a1
This removes various skips that accounted for running the integration tests
against older versions of the daemon before 20.10 (API version v1.41). Those
versions are EOL, and we don't run tests against them.
This reverts most of e440831802
, and similar
PRs.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
229 lines
6.6 KiB
Go
229 lines
6.6 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/docker/docker/api/types/container"
|
|
"github.com/docker/docker/client"
|
|
"github.com/docker/docker/integration-cli/cli"
|
|
"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 := cli.DockerCmd(c, "run", "-d", "-t", "busybox", "/bin/sh", "-c", "while true; do echo hello; sleep 1; done").Stdout()
|
|
id := strings.TrimSpace(out)
|
|
cli.WaitRun(c, 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) {
|
|
const name = "logs_test"
|
|
cli.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, container.LogsOptions{})
|
|
assert.ErrorContains(c, err, "Bad parameters: you must choose at least one stream")
|
|
}
|
|
|
|
// Regression test for #12704
|
|
func (s *DockerAPISuite) TestLogsAPIFollowEmptyOutput(c *testing.T) {
|
|
const name = "logs_test"
|
|
t0 := time.Now()
|
|
cli.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)
|
|
const name = "logsuntilfuturefollow"
|
|
cli.DockerCmd(c, "run", "-d", "--name", name, "busybox", "/bin/sh", "-c", "while true; do date +%s; sleep 1; done")
|
|
cli.WaitRun(c, name)
|
|
|
|
untilSecs := 5
|
|
untilDur, err := time.ParseDuration(fmt.Sprintf("%ds", untilSecs))
|
|
assert.NilError(c, err)
|
|
until := daemonTime(c).Add(untilDur)
|
|
|
|
apiClient, err := client.NewClientWithOpts(client.FromEnv)
|
|
if err != nil {
|
|
c.Fatal(err)
|
|
}
|
|
|
|
reader, err := apiClient.ContainerLogs(testutil.GetContext(c), name, container.LogsOptions{
|
|
Until: until.Format(time.RFC3339Nano),
|
|
Follow: true,
|
|
ShowStdout: true,
|
|
Timestamps: true,
|
|
})
|
|
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) {
|
|
const name = "logsuntil"
|
|
cli.DockerCmd(c, "run", "--name", name, "busybox", "/bin/sh", "-c", "for i in $(seq 1 3); do echo log$i; sleep 1; done")
|
|
|
|
apiClient, err := client.NewClientWithOpts(client.FromEnv)
|
|
if err != nil {
|
|
c.Fatal(err)
|
|
}
|
|
|
|
extractBody := func(c *testing.T, cfg container.LogsOptions) []string {
|
|
reader, err := apiClient.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, container.LogsOptions{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, container.LogsOptions{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) {
|
|
const name = "logsuntildefaultval"
|
|
cli.DockerCmd(c, "run", "--name", name, "busybox", "/bin/sh", "-c", "for i in $(seq 1 3); do echo log$i; done")
|
|
|
|
apiClient, err := client.NewClientWithOpts(client.FromEnv)
|
|
if err != nil {
|
|
c.Fatal(err)
|
|
}
|
|
|
|
extractBody := func(c *testing.T, cfg container.LogsOptions) []string {
|
|
reader, err := apiClient.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, container.LogsOptions{Timestamps: true, ShowStdout: true})
|
|
|
|
// Test with default value specified and parameter omitted
|
|
defaultLogs := extractBody(c, container.LogsOptions{Timestamps: true, ShowStdout: true, Until: "0"})
|
|
assert.DeepEqual(c, defaultLogs, allLogs)
|
|
}
|