github.com/rumpl/bof@v23.0.0-rc.2+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  	for _, tC := range testCases {
   128  		tC := tC
   129  		t.Run(tC.desc, func(t *testing.T) {
   130  			t.Parallel()
   131  			tty := tC.tty
   132  			id := container.Run(ctx, t, client,
   133  				container.WithCmd("sh", "-c", "echo -n this is fine; echo -n accidents happen >&2"),
   134  				container.WithTty(tty),
   135  				container.WithLogDriver(logDriver),
   136  			)
   137  			defer client.ContainerRemove(ctx, id, types.ContainerRemoveOptions{Force: true})
   138  
   139  			poll.WaitOn(t, container.IsStopped(ctx, client, id), poll.WithDelay(time.Millisecond*100))
   140  
   141  			logs, err := client.ContainerLogs(ctx, id, tC.logOps)
   142  			assert.NilError(t, err)
   143  			defer logs.Close()
   144  
   145  			var stdout, stderr bytes.Buffer
   146  			if tty {
   147  				// TTY, only one output stream
   148  				_, err = io.Copy(&stdout, logs)
   149  			} else {
   150  				_, err = stdcopy.StdCopy(&stdout, &stderr, logs)
   151  			}
   152  			assert.NilError(t, err)
   153  
   154  			stdoutStr := stdout.String()
   155  
   156  			if tty && testEnv.OSType == "windows" {
   157  				stdoutStr = stripEscapeCodes(t, stdoutStr)
   158  
   159  				// Special case for Windows Server 2019
   160  				// Check only that the raw output stream contains strings
   161  				// that were printed to container's stdout and stderr.
   162  				// This is a workaround for the backspace being outputted in an unexpected place
   163  				// which breaks the parsed output: https://github.com/moby/moby/issues/43710
   164  				if strings.Contains(testEnv.DaemonInfo.OperatingSystem, "Windows Server Version 1809") {
   165  					if tC.logOps.ShowStdout {
   166  						assert.Check(t, cmp.Contains(stdout.String(), "this is fine"))
   167  						assert.Check(t, cmp.Contains(stdout.String(), "accidents happen"))
   168  					} else {
   169  						assert.DeepEqual(t, stdoutStr, "")
   170  					}
   171  					return
   172  				}
   173  			}
   174  
   175  			assert.DeepEqual(t, stdoutStr, tC.expectedOut)
   176  			assert.DeepEqual(t, stderr.String(), tC.expectedErr)
   177  		})
   178  	}
   179  }
   180  
   181  // This hack strips the escape codes that appear in the Windows TTY output and don't have
   182  // any effect on the text content.
   183  // This doesn't handle all escape sequences, only ones that were encountered during testing.
   184  func stripEscapeCodes(t *testing.T, input string) string {
   185  	t.Logf("Stripping: %q\n", input)
   186  	output, err := termtest.StripANSICommands(input)
   187  	assert.NilError(t, err)
   188  	return output
   189  }