2018-03-01 17:51:11 -05:00
|
|
|
package container // import "github.com/docker/docker/integration/container"
|
2018-02-13 15:03:56 -05:00
|
|
|
|
|
|
|
import (
|
2022-06-10 03:26:17 -04:00
|
|
|
"bytes"
|
2018-02-13 15:03:56 -05:00
|
|
|
"context"
|
2021-08-24 06:10:50 -04:00
|
|
|
"io"
|
2022-06-10 03:26:17 -04:00
|
|
|
"strings"
|
2018-02-13 15:03:56 -05:00
|
|
|
"testing"
|
2022-06-10 03:26:17 -04:00
|
|
|
"time"
|
2018-02-13 15:03:56 -05:00
|
|
|
|
|
|
|
"github.com/docker/docker/api/types"
|
2022-06-10 03:26:17 -04:00
|
|
|
"github.com/docker/docker/daemon/logger/jsonfilelog"
|
|
|
|
"github.com/docker/docker/daemon/logger/local"
|
2018-02-13 15:03:56 -05:00
|
|
|
"github.com/docker/docker/integration/internal/container"
|
2022-06-10 03:26:17 -04:00
|
|
|
"github.com/docker/docker/integration/internal/termtest"
|
2018-02-13 15:03:56 -05:00
|
|
|
"github.com/docker/docker/pkg/stdcopy"
|
2020-02-07 08:39:24 -05:00
|
|
|
"gotest.tools/v3/assert"
|
2022-06-10 03:26:17 -04:00
|
|
|
"gotest.tools/v3/assert/cmp"
|
|
|
|
"gotest.tools/v3/poll"
|
2020-02-07 08:39:24 -05:00
|
|
|
"gotest.tools/v3/skip"
|
2018-02-13 15:03:56 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
// Regression test for #35370
|
|
|
|
// Makes sure that when following we don't get an EOF error when there are no logs
|
|
|
|
func TestLogsFollowTailEmpty(t *testing.T) {
|
2018-03-27 12:13:47 -04:00
|
|
|
// FIXME(vdemeester) fails on a e2e run on linux...
|
2019-01-03 06:19:15 -05:00
|
|
|
skip.If(t, testEnv.IsRemoteDaemon)
|
2018-02-13 15:03:56 -05:00
|
|
|
defer setupTest(t)()
|
2019-01-02 08:16:25 -05:00
|
|
|
client := testEnv.APIClient()
|
2018-02-13 15:03:56 -05:00
|
|
|
ctx := context.Background()
|
|
|
|
|
2019-06-06 07:15:31 -04:00
|
|
|
id := container.Run(ctx, t, client, container.WithCmd("sleep", "100000"))
|
2018-02-13 15:03:56 -05:00
|
|
|
|
|
|
|
logs, err := client.ContainerLogs(ctx, id, types.ContainerLogsOptions{ShowStdout: true, Tail: "2"})
|
|
|
|
if logs != nil {
|
|
|
|
defer logs.Close()
|
|
|
|
}
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.Check(t, err)
|
2018-02-13 15:03:56 -05:00
|
|
|
|
2021-08-24 06:10:50 -04:00
|
|
|
_, err = stdcopy.StdCopy(io.Discard, io.Discard, logs)
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.Check(t, err)
|
2018-02-13 15:03:56 -05:00
|
|
|
}
|
2022-06-10 03:26:17 -04:00
|
|
|
|
|
|
|
func TestLogs(t *testing.T) {
|
|
|
|
drivers := []string{local.Name, jsonfilelog.Name}
|
|
|
|
|
|
|
|
for _, logDriver := range drivers {
|
|
|
|
t.Run("driver "+logDriver, func(t *testing.T) {
|
|
|
|
testLogs(t, logDriver)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func testLogs(t *testing.T, logDriver string) {
|
|
|
|
defer setupTest(t)()
|
|
|
|
client := testEnv.APIClient()
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
testCases := []struct {
|
|
|
|
desc string
|
|
|
|
logOps types.ContainerLogsOptions
|
|
|
|
expectedOut string
|
|
|
|
expectedErr string
|
|
|
|
tty bool
|
|
|
|
}{
|
|
|
|
// TTY, only one output stream
|
|
|
|
{
|
|
|
|
desc: "tty/stdout and stderr",
|
|
|
|
tty: true,
|
|
|
|
logOps: types.ContainerLogsOptions{
|
|
|
|
ShowStdout: true,
|
|
|
|
ShowStderr: true,
|
|
|
|
},
|
|
|
|
expectedOut: "this is fineaccidents happen",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
desc: "tty/only stdout",
|
|
|
|
tty: true,
|
|
|
|
logOps: types.ContainerLogsOptions{
|
|
|
|
ShowStdout: true,
|
|
|
|
ShowStderr: false,
|
|
|
|
},
|
|
|
|
expectedOut: "this is fineaccidents happen",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
desc: "tty/only stderr",
|
|
|
|
tty: true,
|
|
|
|
logOps: types.ContainerLogsOptions{
|
|
|
|
ShowStdout: false,
|
|
|
|
ShowStderr: true,
|
|
|
|
},
|
|
|
|
expectedOut: "",
|
|
|
|
},
|
|
|
|
// Without TTY, both stdout and stderr
|
|
|
|
{
|
|
|
|
desc: "without tty/stdout and stderr",
|
|
|
|
tty: false,
|
|
|
|
logOps: types.ContainerLogsOptions{
|
|
|
|
ShowStdout: true,
|
|
|
|
ShowStderr: true,
|
|
|
|
},
|
|
|
|
expectedOut: "this is fine",
|
|
|
|
expectedErr: "accidents happen",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
desc: "without tty/only stdout",
|
|
|
|
tty: false,
|
|
|
|
logOps: types.ContainerLogsOptions{
|
|
|
|
ShowStdout: true,
|
|
|
|
ShowStderr: false,
|
|
|
|
},
|
|
|
|
expectedOut: "this is fine",
|
|
|
|
expectedErr: "",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
desc: "without tty/only stderr",
|
|
|
|
tty: false,
|
|
|
|
logOps: types.ContainerLogsOptions{
|
|
|
|
ShowStdout: false,
|
|
|
|
ShowStderr: true,
|
|
|
|
},
|
|
|
|
expectedOut: "",
|
|
|
|
expectedErr: "accidents happen",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tC := range testCases {
|
|
|
|
tC := tC
|
|
|
|
t.Run(tC.desc, func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
tty := tC.tty
|
|
|
|
id := container.Run(ctx, t, client,
|
|
|
|
container.WithCmd("sh", "-c", "echo -n this is fine; echo -n accidents happen >&2"),
|
|
|
|
container.WithTty(tty),
|
|
|
|
container.WithLogDriver(logDriver),
|
|
|
|
)
|
|
|
|
defer client.ContainerRemove(ctx, id, types.ContainerRemoveOptions{Force: true})
|
|
|
|
|
|
|
|
poll.WaitOn(t, container.IsStopped(ctx, client, id), poll.WithDelay(time.Millisecond*100))
|
|
|
|
|
|
|
|
logs, err := client.ContainerLogs(ctx, id, tC.logOps)
|
|
|
|
assert.NilError(t, err)
|
|
|
|
defer logs.Close()
|
|
|
|
|
|
|
|
var stdout, stderr bytes.Buffer
|
|
|
|
if tty {
|
|
|
|
// TTY, only one output stream
|
|
|
|
_, err = io.Copy(&stdout, logs)
|
|
|
|
} else {
|
|
|
|
_, err = stdcopy.StdCopy(&stdout, &stderr, logs)
|
|
|
|
}
|
|
|
|
assert.NilError(t, err)
|
|
|
|
|
|
|
|
stdoutStr := stdout.String()
|
|
|
|
|
|
|
|
if tty && testEnv.OSType == "windows" {
|
|
|
|
stdoutStr = stripEscapeCodes(t, stdoutStr)
|
|
|
|
|
|
|
|
// Special case for Windows Server 2019
|
|
|
|
// Check only that the raw output stream contains strings
|
|
|
|
// that were printed to container's stdout and stderr.
|
|
|
|
// This is a workaround for the backspace being outputted in an unexpected place
|
|
|
|
// which breaks the parsed output: https://github.com/moby/moby/issues/43710
|
|
|
|
if strings.Contains(testEnv.DaemonInfo.OperatingSystem, "Windows Server Version 1809") {
|
|
|
|
if tC.logOps.ShowStdout {
|
|
|
|
assert.Check(t, cmp.Contains(stdout.String(), "this is fine"))
|
|
|
|
assert.Check(t, cmp.Contains(stdout.String(), "accidents happen"))
|
|
|
|
} else {
|
|
|
|
assert.DeepEqual(t, stdoutStr, "")
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
assert.DeepEqual(t, stdoutStr, tC.expectedOut)
|
|
|
|
assert.DeepEqual(t, stderr.String(), tC.expectedErr)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// This hack strips the escape codes that appear in the Windows TTY output and don't have
|
|
|
|
// any effect on the text content.
|
|
|
|
// This doesn't handle all escape sequences, only ones that were encountered during testing.
|
|
|
|
func stripEscapeCodes(t *testing.T, input string) string {
|
|
|
|
t.Logf("Stripping: %q\n", input)
|
|
|
|
output, err := termtest.StripANSICommands(input)
|
|
|
|
assert.NilError(t, err)
|
|
|
|
return output
|
|
|
|
}
|