github.com/containerd/nerdctl@v1.7.7/pkg/logging/cri_logger_test.go (about) 1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 /* 18 Forked from https://github.com/kubernetes/kubernetes/blob/a66aad2d80dacc70025f95a8f97d2549ebd3208c/pkg/kubelet/kuberuntime/logs/logs_test.go 19 Copyright The Kubernetes Authors. 20 Licensed under the Apache License, Version 2.0 21 */ 22 23 package logging 24 25 import ( 26 "bufio" 27 "bytes" 28 "fmt" 29 "io" 30 "os" 31 "reflect" 32 "testing" 33 "time" 34 ) 35 36 func TestReadLogs(t *testing.T) { 37 file, err := os.CreateTemp("", "TestFollowLogs") 38 if err != nil { 39 t.Fatalf("unable to create temp file") 40 } 41 defer os.Remove(file.Name()) 42 file.WriteString(`2016-10-06T00:17:09.669794202Z stdout F line1` + "\n") 43 file.WriteString(`2016-10-06T00:17:10.669794202Z stdout F line2` + "\n") 44 file.WriteString(`2016-10-06T00:17:11.669794202Z stdout F line3` + "\n") 45 46 stopChan := make(chan os.Signal) 47 testCases := []struct { 48 name string 49 logViewOptions LogViewOptions 50 expected string 51 }{ 52 { 53 name: "default log options should output all lines", 54 logViewOptions: LogViewOptions{ 55 LogPath: file.Name(), 56 Tail: 0, 57 }, 58 expected: "line1\nline2\nline3\n", 59 }, 60 { 61 name: "using Tail 2 should output last 2 lines", 62 logViewOptions: LogViewOptions{ 63 LogPath: file.Name(), 64 Tail: 2, 65 }, 66 expected: "line2\nline3\n", 67 }, 68 { 69 name: "using Tail 4 should output all lines when the log has less than 4 lines", 70 logViewOptions: LogViewOptions{ 71 LogPath: file.Name(), 72 Tail: 4, 73 }, 74 expected: "line1\nline2\nline3\n", 75 }, 76 { 77 name: "using Tail 0 should output all", 78 logViewOptions: LogViewOptions{ 79 LogPath: file.Name(), 80 Tail: 0, 81 }, 82 expected: "line1\nline2\nline3\n", 83 }, 84 } 85 for _, tc := range testCases { 86 t.Run(tc.name, func(t *testing.T) { 87 stdoutBuf := bytes.NewBuffer(nil) 88 stderrBuf := bytes.NewBuffer(nil) 89 err = ReadLogs(&tc.logViewOptions, stdoutBuf, stderrBuf, stopChan) 90 91 if err != nil { 92 t.Fatal(err.Error()) 93 } 94 if stderrBuf.Len() > 0 { 95 t.Fatalf("Stderr: %v", stderrBuf.String()) 96 } 97 if actual := stdoutBuf.String(); tc.expected != actual { 98 t.Fatalf("Actual output does not match expected.\nActual: %v\nExpected: %v\n", actual, tc.expected) 99 } 100 }) 101 } 102 } 103 104 func TestParseLog(t *testing.T) { 105 timestamp, err := time.Parse(time.RFC3339Nano, "2016-10-20T18:39:20.57606443Z") 106 107 if err != nil { 108 t.Fatalf("Parse Time err %s", err.Error()) 109 } 110 logmsg := &logMessage{} 111 for c, test := range []struct { 112 line string 113 msg *logMessage 114 err bool 115 }{ 116 { // CRI log format stdout 117 line: "2016-10-20T18:39:20.57606443Z stdout F cri stdout test log\n", 118 msg: &logMessage{ 119 timestamp: timestamp, 120 stream: Stdout, 121 log: []byte("cri stdout test log\n"), 122 }, 123 }, 124 { // CRI log format stderr 125 line: "2016-10-20T18:39:20.57606443Z stderr F cri stderr test log\n", 126 msg: &logMessage{ 127 timestamp: timestamp, 128 stream: Stderr, 129 log: []byte("cri stderr test log\n"), 130 }, 131 }, 132 { // Unsupported Log format 133 line: "unsupported log format test log\n", 134 msg: &logMessage{}, 135 err: true, 136 }, 137 { // Partial CRI log line 138 line: "2016-10-20T18:39:20.57606443Z stdout P cri stdout partial test log\n", 139 msg: &logMessage{ 140 timestamp: timestamp, 141 stream: Stdout, 142 log: []byte("cri stdout partial test log"), 143 }, 144 }, 145 { // Partial CRI log line with multiple log tags. 146 line: "2016-10-20T18:39:20.57606443Z stdout P:TAG1:TAG2 cri stdout partial test log\n", 147 msg: &logMessage{ 148 timestamp: timestamp, 149 stream: Stdout, 150 log: []byte("cri stdout partial test log"), 151 }, 152 }, 153 } { 154 t.Logf("TestCase #%d: %+v", c, test) 155 156 err = ParseCRILog([]byte(test.line), logmsg) 157 if err != nil { 158 if test.err { 159 continue 160 } 161 t.Errorf("ParseCRILog err %s ", err.Error()) 162 } 163 164 if !reflect.DeepEqual(test.msg, logmsg) { 165 t.Errorf("ParseCRILog failed, msg is %#v,test.msg is %#v", logmsg, test.msg) 166 } 167 168 } 169 } 170 171 func TestReadLogsLimitsWithTimestamps(t *testing.T) { 172 logLineFmt := "2022-10-29T16:10:22.592603036-05:00 stdout P %v\n" 173 logLineNewLine := "2022-10-29T16:10:22.592603036-05:00 stdout F \n" 174 175 tmpfile, err := os.CreateTemp("", "log.*.txt") 176 if err != nil { 177 t.Fatalf("unable to create temp file") 178 } 179 180 stopChan := make(chan os.Signal) 181 182 count := 10000 183 184 for i := 0; i < count; i++ { 185 tmpfile.WriteString(fmt.Sprintf(logLineFmt, i)) 186 } 187 tmpfile.WriteString(logLineNewLine) 188 189 for i := 0; i < count; i++ { 190 tmpfile.WriteString(fmt.Sprintf(logLineFmt, i)) 191 } 192 tmpfile.WriteString(logLineNewLine) 193 194 // two lines are in the buffer 195 196 defer os.Remove(tmpfile.Name()) // clean up 197 198 tmpfile.Close() 199 200 var buf bytes.Buffer 201 w := io.MultiWriter(&buf) 202 203 err = ReadLogs(&LogViewOptions{LogPath: tmpfile.Name(), Tail: 0, Timestamps: true}, w, w, stopChan) 204 if err != nil { 205 t.Errorf("ReadLogs file %s failed %s", tmpfile.Name(), err.Error()) 206 } 207 208 lineCount := 0 209 scanner := bufio.NewScanner(bytes.NewReader(buf.Bytes())) 210 for scanner.Scan() { 211 lineCount++ 212 213 // Split the line 214 ts, logline, _ := bytes.Cut(scanner.Bytes(), []byte(" ")) 215 216 // Verification 217 // 1. The timestamp should exist 218 // 2. The last item in the log should be 9999 219 _, err = time.Parse(time.RFC3339, string(ts)) 220 if err != nil { 221 t.Errorf("timestamp not found, err: %s", err.Error()) 222 } 223 224 if !bytes.HasSuffix(logline, []byte("9999")) { 225 t.Errorf("the complete log found, err: %s", err.Error()) 226 } 227 } 228 229 if lineCount != 2 { 230 t.Errorf("should have two lines, lineCount= %d", lineCount) 231 } 232 }