logs_test.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. /*
  2. Copyright 2017 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package logs
  14. import (
  15. "bytes"
  16. "testing"
  17. "time"
  18. "github.com/stretchr/testify/assert"
  19. "k8s.io/api/core/v1"
  20. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  21. runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
  22. )
  23. func TestLogOptions(t *testing.T) {
  24. var (
  25. line = int64(8)
  26. bytes = int64(64)
  27. timestamp = metav1.Now()
  28. sinceseconds = int64(10)
  29. )
  30. for c, test := range []struct {
  31. apiOpts *v1.PodLogOptions
  32. expect *LogOptions
  33. }{
  34. { // empty options
  35. apiOpts: &v1.PodLogOptions{},
  36. expect: &LogOptions{tail: -1, bytes: -1},
  37. },
  38. { // test tail lines
  39. apiOpts: &v1.PodLogOptions{TailLines: &line},
  40. expect: &LogOptions{tail: line, bytes: -1},
  41. },
  42. { // test limit bytes
  43. apiOpts: &v1.PodLogOptions{LimitBytes: &bytes},
  44. expect: &LogOptions{tail: -1, bytes: bytes},
  45. },
  46. { // test since timestamp
  47. apiOpts: &v1.PodLogOptions{SinceTime: &timestamp},
  48. expect: &LogOptions{tail: -1, bytes: -1, since: timestamp.Time},
  49. },
  50. { // test since seconds
  51. apiOpts: &v1.PodLogOptions{SinceSeconds: &sinceseconds},
  52. expect: &LogOptions{tail: -1, bytes: -1, since: timestamp.Add(-10 * time.Second)},
  53. },
  54. } {
  55. t.Logf("TestCase #%d: %+v", c, test)
  56. opts := NewLogOptions(test.apiOpts, timestamp.Time)
  57. assert.Equal(t, test.expect, opts)
  58. }
  59. }
  60. func TestParseLog(t *testing.T) {
  61. timestamp, err := time.Parse(timeFormat, "2016-10-20T18:39:20.57606443Z")
  62. assert.NoError(t, err)
  63. msg := &logMessage{}
  64. for c, test := range []struct {
  65. line string
  66. msg *logMessage
  67. err bool
  68. }{
  69. { // Docker log format stdout
  70. line: `{"log":"docker stdout test log","stream":"stdout","time":"2016-10-20T18:39:20.57606443Z"}` + "\n",
  71. msg: &logMessage{
  72. timestamp: timestamp,
  73. stream: runtimeapi.Stdout,
  74. log: []byte("docker stdout test log"),
  75. },
  76. },
  77. { // Docker log format stderr
  78. line: `{"log":"docker stderr test log","stream":"stderr","time":"2016-10-20T18:39:20.57606443Z"}` + "\n",
  79. msg: &logMessage{
  80. timestamp: timestamp,
  81. stream: runtimeapi.Stderr,
  82. log: []byte("docker stderr test log"),
  83. },
  84. },
  85. { // CRI log format stdout
  86. line: "2016-10-20T18:39:20.57606443Z stdout F cri stdout test log\n",
  87. msg: &logMessage{
  88. timestamp: timestamp,
  89. stream: runtimeapi.Stdout,
  90. log: []byte("cri stdout test log\n"),
  91. },
  92. },
  93. { // CRI log format stderr
  94. line: "2016-10-20T18:39:20.57606443Z stderr F cri stderr test log\n",
  95. msg: &logMessage{
  96. timestamp: timestamp,
  97. stream: runtimeapi.Stderr,
  98. log: []byte("cri stderr test log\n"),
  99. },
  100. },
  101. { // Unsupported Log format
  102. line: "unsupported log format test log\n",
  103. msg: &logMessage{},
  104. err: true,
  105. },
  106. { // Partial CRI log line
  107. line: "2016-10-20T18:39:20.57606443Z stdout P cri stdout partial test log\n",
  108. msg: &logMessage{
  109. timestamp: timestamp,
  110. stream: runtimeapi.Stdout,
  111. log: []byte("cri stdout partial test log"),
  112. },
  113. },
  114. { // Partial CRI log line with multiple log tags.
  115. line: "2016-10-20T18:39:20.57606443Z stdout P:TAG1:TAG2 cri stdout partial test log\n",
  116. msg: &logMessage{
  117. timestamp: timestamp,
  118. stream: runtimeapi.Stdout,
  119. log: []byte("cri stdout partial test log"),
  120. },
  121. },
  122. } {
  123. t.Logf("TestCase #%d: %+v", c, test)
  124. parse, err := getParseFunc([]byte(test.line))
  125. if test.err {
  126. assert.Error(t, err)
  127. continue
  128. }
  129. assert.NoError(t, err)
  130. err = parse([]byte(test.line), msg)
  131. assert.NoError(t, err)
  132. assert.Equal(t, test.msg, msg)
  133. }
  134. }
  135. func TestWriteLogs(t *testing.T) {
  136. timestamp := time.Unix(1234, 4321)
  137. log := "abcdefg\n"
  138. for c, test := range []struct {
  139. stream runtimeapi.LogStreamType
  140. since time.Time
  141. timestamp bool
  142. expectStdout string
  143. expectStderr string
  144. }{
  145. { // stderr log
  146. stream: runtimeapi.Stderr,
  147. expectStderr: log,
  148. },
  149. { // stdout log
  150. stream: runtimeapi.Stdout,
  151. expectStdout: log,
  152. },
  153. { // since is after timestamp
  154. stream: runtimeapi.Stdout,
  155. since: timestamp.Add(1 * time.Second),
  156. },
  157. { // timestamp enabled
  158. stream: runtimeapi.Stderr,
  159. timestamp: true,
  160. expectStderr: timestamp.Format(timeFormat) + " " + log,
  161. },
  162. } {
  163. t.Logf("TestCase #%d: %+v", c, test)
  164. msg := &logMessage{
  165. timestamp: timestamp,
  166. stream: test.stream,
  167. log: []byte(log),
  168. }
  169. stdoutBuf := bytes.NewBuffer(nil)
  170. stderrBuf := bytes.NewBuffer(nil)
  171. w := newLogWriter(stdoutBuf, stderrBuf, &LogOptions{since: test.since, timestamp: test.timestamp, bytes: -1})
  172. err := w.write(msg)
  173. assert.NoError(t, err)
  174. assert.Equal(t, test.expectStdout, stdoutBuf.String())
  175. assert.Equal(t, test.expectStderr, stderrBuf.String())
  176. }
  177. }
  178. func TestWriteLogsWithBytesLimit(t *testing.T) {
  179. timestamp := time.Unix(1234, 4321)
  180. timestampStr := timestamp.Format(timeFormat)
  181. log := "abcdefg\n"
  182. for c, test := range []struct {
  183. stdoutLines int
  184. stderrLines int
  185. bytes int
  186. timestamp bool
  187. expectStdout string
  188. expectStderr string
  189. }{
  190. { // limit bytes less than one line
  191. stdoutLines: 3,
  192. bytes: 3,
  193. expectStdout: "abc",
  194. },
  195. { // limit bytes across lines
  196. stdoutLines: 3,
  197. bytes: len(log) + 3,
  198. expectStdout: "abcdefg\nabc",
  199. },
  200. { // limit bytes more than all lines
  201. stdoutLines: 3,
  202. bytes: 3 * len(log),
  203. expectStdout: "abcdefg\nabcdefg\nabcdefg\n",
  204. },
  205. { // limit bytes for stderr
  206. stderrLines: 3,
  207. bytes: len(log) + 3,
  208. expectStderr: "abcdefg\nabc",
  209. },
  210. { // limit bytes for both stdout and stderr, stdout first.
  211. stdoutLines: 1,
  212. stderrLines: 2,
  213. bytes: len(log) + 3,
  214. expectStdout: "abcdefg\n",
  215. expectStderr: "abc",
  216. },
  217. { // limit bytes with timestamp
  218. stdoutLines: 3,
  219. timestamp: true,
  220. bytes: len(timestampStr) + 1 + len(log) + 2,
  221. expectStdout: timestampStr + " " + log + timestampStr[:2],
  222. },
  223. } {
  224. t.Logf("TestCase #%d: %+v", c, test)
  225. msg := &logMessage{
  226. timestamp: timestamp,
  227. log: []byte(log),
  228. }
  229. stdoutBuf := bytes.NewBuffer(nil)
  230. stderrBuf := bytes.NewBuffer(nil)
  231. w := newLogWriter(stdoutBuf, stderrBuf, &LogOptions{timestamp: test.timestamp, bytes: int64(test.bytes)})
  232. for i := 0; i < test.stdoutLines; i++ {
  233. msg.stream = runtimeapi.Stdout
  234. if err := w.write(msg); err != nil {
  235. assert.EqualError(t, err, errMaximumWrite.Error())
  236. }
  237. }
  238. for i := 0; i < test.stderrLines; i++ {
  239. msg.stream = runtimeapi.Stderr
  240. if err := w.write(msg); err != nil {
  241. assert.EqualError(t, err, errMaximumWrite.Error())
  242. }
  243. }
  244. assert.Equal(t, test.expectStdout, stdoutBuf.String())
  245. assert.Equal(t, test.expectStderr, stderrBuf.String())
  246. }
  247. }