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 }