github.com/rawahars/moby@v24.0.4+incompatible/integration/container/logs_test.go (about)

     1  package container // import "github.com/docker/docker/integration/container"
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"io"
     7  	"strings"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/docker/docker/api/types"
    12  	"github.com/docker/docker/daemon/logger/jsonfilelog"
    13  	"github.com/docker/docker/daemon/logger/local"
    14  	"github.com/docker/docker/integration/internal/container"
    15  	"github.com/docker/docker/integration/internal/termtest"
    16  	"github.com/docker/docker/pkg/stdcopy"
    17  	"gotest.tools/v3/assert"
    18  	"gotest.tools/v3/assert/cmp"
    19  	"gotest.tools/v3/poll"
    20  	"gotest.tools/v3/skip"
    21  )
    22  
    23  // Regression test for #35370
    24  // Makes sure that when following we don't get an EOF error when there are no logs
    25  func TestLogsFollowTailEmpty(t *testing.T) {
    26  	// FIXME(vdemeester) fails on a e2e run on linux...
    27  	skip.If(t, testEnv.IsRemoteDaemon)
    28  	defer setupTest(t)()
    29  	client := testEnv.APIClient()
    30  	ctx := context.Background()
    31  
    32  	id := container.Run(ctx, t, client, container.WithCmd("sleep", "100000"))
    33  
    34  	logs, err := client.ContainerLogs(ctx, id, types.ContainerLogsOptions{ShowStdout: true, Tail: "2"})
    35  	if logs != nil {
    36  		defer logs.Close()
    37  	}
    38  	assert.Check(t, err)
    39  
    40  	_, err = stdcopy.StdCopy(io.Discard, io.Discard, logs)
    41  	assert.Check(t, err)
    42  }
    43  
    44  func TestLogs(t *testing.T) {
    45  	drivers := []string{local.Name, jsonfilelog.Name}
    46  
    47  	for _, logDriver := range drivers {
    48  		t.Run("driver "+logDriver, func(t *testing.T) {
    49  			testLogs(t, logDriver)
    50  		})
    51  	}
    52  }
    53  
    54  func testLogs(t *testing.T, logDriver string) {
    55  	defer setupTest(t)()
    56  	client := testEnv.APIClient()
    57  	ctx := context.Background()
    58  
    59  	testCases := []struct {
    60  		desc        string
    61  		logOps      types.ContainerLogsOptions
    62  		expectedOut string
    63  		expectedErr string
    64  		tty         bool
    65  	}{
    66  		// TTY, only one output stream
    67  		{
    68  			desc: "tty/stdout and stderr",
    69  			tty:  true,
    70  			logOps: types.ContainerLogsOptions{
    71  				ShowStdout: true,
    72  				ShowStderr: true,
    73  			},
    74  			expectedOut: "this is fineaccidents happen",
    75  		},
    76  		{
    77  			desc: "tty/only stdout",
    78  			tty:  true,
    79  			logOps: types.ContainerLogsOptions{
    80  				ShowStdout: true,
    81  				ShowStderr: false,
    82  			},
    83  			expectedOut: "this is fineaccidents happen",
    84  		},
    85  		{
    86  			desc: "tty/only stderr",
    87  			tty:  true,
    88  			logOps: types.ContainerLogsOptions{
    89  				ShowStdout: false,
    90  				ShowStderr: true,
    91  			},
    92  			expectedOut: "",
    93  		},
    94  		// Without TTY, both stdout and stderr
    95  		{
    96  			desc: "without tty/stdout and stderr",
    97  			tty:  false,
    98  			logOps: types.ContainerLogsOptions{
    99  				ShowStdout: true,
   100  				ShowStderr: true,
   101  			},
   102  			expectedOut: "this is fine",
   103  			expectedErr: "accidents happen",
   104  		},
   105  		{
   106  			desc: "without tty/only stdout",
   107  			tty:  false,
   108  			logOps: types.ContainerLogsOptions{
   109  				ShowStdout: true,
   110  				ShowStderr: false,
   111  			},
   112  			expectedOut: "this is fine",
   113  			expectedErr: "",
   114  		},
   115  		{
   116  			desc: "without tty/only stderr",
   117  			tty:  false,
   118  			logOps: types.ContainerLogsOptions{
   119  				ShowStdout: false,
   120  				ShowStderr: true,
   121  			},
   122  			expectedOut: "",
   123  			expectedErr: "accidents happen",
   124  		},
   125  	}
   126  
   127  	pollTimeout := time.Second * 10
   128  	if testEnv.OSType == "windows" {
   129  		pollTimeout = StopContainerWindowsPollTimeout
   130  	}
   131  
   132  	for _, tC := range testCases {
   133  		tC := tC
   134  		t.Run(tC.desc, func(t *testing.T) {
   135  			t.Parallel()
   136  			tty := tC.tty
   137  			id := container.Run(ctx, t, client,
   138  				container.WithCmd("sh", "-c", "echo -n this is fine; echo -n accidents happen >&2"),
   139  				container.WithTty(tty),
   140  				container.WithLogDriver(logDriver),
   141  			)
   142  			defer client.ContainerRemove(ctx, id, types.ContainerRemoveOptions{Force: true})
   143  
   144  			poll.WaitOn(t, container.IsStopped(ctx, client, id),
   145  				poll.WithDelay(time.Millisecond*100),
   146  				poll.WithTimeout(pollTimeout))
   147  
   148  			logs, err := client.ContainerLogs(ctx, id, tC.logOps)
   149  			assert.NilError(t, err)
   150  			defer logs.Close()
   151  
   152  			var stdout, stderr bytes.Buffer
   153  			if tty {
   154  				// TTY, only one output stream
   155  				_, err = io.Copy(&stdout, logs)
   156  			} else {
   157  				_, err = stdcopy.StdCopy(&stdout, &stderr, logs)
   158  			}
   159  			assert.NilError(t, err)
   160  
   161  			stdoutStr := stdout.String()
   162  
   163  			if tty && testEnv.OSType == "windows" {
   164  				stdoutStr = stripEscapeCodes(t, stdoutStr)
   165  
   166  				// Special case for Windows Server 2019
   167  				// Check only that the raw output stream contains strings
   168  				// that were printed to container's stdout and stderr.
   169  				// This is a workaround for the backspace being outputted in an unexpected place
   170  				// which breaks the parsed output: https://github.com/moby/moby/issues/43710
   171  				if strings.Contains(testEnv.DaemonInfo.OperatingSystem, "Windows Server Version 1809") {
   172  					if tC.logOps.ShowStdout {
   173  						assert.Check(t, cmp.Contains(stdout.String(), "this is fine"))
   174  						assert.Check(t, cmp.Contains(stdout.String(), "accidents happen"))
   175  					} else {
   176  						assert.DeepEqual(t, stdoutStr, "")
   177  					}
   178  					return
   179  				}
   180  			}
   181  
   182  			assert.DeepEqual(t, stdoutStr, tC.expectedOut)
   183  			assert.DeepEqual(t, stderr.String(), tC.expectedErr)
   184  		})
   185  	}
   186  }
   187  
   188  // This hack strips the escape codes that appear in the Windows TTY output and don't have
   189  // any effect on the text content.
   190  // This doesn't handle all escape sequences, only ones that were encountered during testing.
   191  func stripEscapeCodes(t *testing.T, input string) string {
   192  	t.Logf("Stripping: %q\n", input)
   193  	output, err := termtest.StripANSICommands(input)
   194  	assert.NilError(t, err)
   195  	return output
   196  }