github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/integration/container/logs_test.go (about)

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