github.com/devdivbcp/moby@v17.12.0-ce-rc1.0.20200726071732-2d4bfdc789ad+incompatible/daemon/logger/loggerutils/logfile_test.go (about) 1 package loggerutils 2 3 import ( 4 "bufio" 5 "context" 6 "io" 7 "io/ioutil" 8 "os" 9 "strings" 10 "testing" 11 "time" 12 13 "github.com/docker/docker/daemon/logger" 14 "github.com/docker/docker/pkg/pubsub" 15 "github.com/docker/docker/pkg/tailfile" 16 "gotest.tools/assert" 17 ) 18 19 func TestTailFiles(t *testing.T) { 20 s1 := strings.NewReader("Hello.\nMy name is Inigo Montoya.\n") 21 s2 := strings.NewReader("I'm serious.\nDon't call me Shirley!\n") 22 s3 := strings.NewReader("Roads?\nWhere we're going we don't need roads.\n") 23 24 files := []SizeReaderAt{s1, s2, s3} 25 watcher := logger.NewLogWatcher() 26 createDecoder := func(r io.Reader) func() (*logger.Message, error) { 27 scanner := bufio.NewScanner(r) 28 return func() (*logger.Message, error) { 29 if !scanner.Scan() { 30 return nil, scanner.Err() 31 } 32 // some comment 33 return &logger.Message{Line: scanner.Bytes(), Timestamp: time.Now()}, nil 34 } 35 } 36 37 tailReader := func(ctx context.Context, r SizeReaderAt, lines int) (io.Reader, int, error) { 38 return tailfile.NewTailReader(ctx, r, lines) 39 } 40 41 for desc, config := range map[string]logger.ReadConfig{} { 42 t.Run(desc, func(t *testing.T) { 43 started := make(chan struct{}) 44 go func() { 45 close(started) 46 tailFiles(files, watcher, createDecoder, tailReader, config) 47 }() 48 <-started 49 }) 50 } 51 52 config := logger.ReadConfig{Tail: 2} 53 started := make(chan struct{}) 54 go func() { 55 close(started) 56 tailFiles(files, watcher, createDecoder, tailReader, config) 57 }() 58 <-started 59 60 select { 61 case <-time.After(60 * time.Second): 62 t.Fatal("timeout waiting for tail line") 63 case err := <-watcher.Err: 64 assert.NilError(t, err) 65 case msg := <-watcher.Msg: 66 assert.Assert(t, msg != nil) 67 assert.Assert(t, string(msg.Line) == "Roads?", string(msg.Line)) 68 } 69 70 select { 71 case <-time.After(60 * time.Second): 72 t.Fatal("timeout waiting for tail line") 73 case err := <-watcher.Err: 74 assert.NilError(t, err) 75 case msg := <-watcher.Msg: 76 assert.Assert(t, msg != nil) 77 assert.Assert(t, string(msg.Line) == "Where we're going we don't need roads.", string(msg.Line)) 78 } 79 } 80 81 func TestFollowLogsConsumerGone(t *testing.T) { 82 lw := logger.NewLogWatcher() 83 84 f, err := ioutil.TempFile("", t.Name()) 85 assert.NilError(t, err) 86 defer func() { 87 f.Close() 88 os.Remove(f.Name()) 89 }() 90 91 makeDecoder := func(rdr io.Reader) func() (*logger.Message, error) { 92 return func() (*logger.Message, error) { 93 return &logger.Message{}, nil 94 } 95 } 96 97 followLogsDone := make(chan struct{}) 98 var since, until time.Time 99 go func() { 100 followLogs(f, lw, make(chan interface{}), makeDecoder, since, until) 101 close(followLogsDone) 102 }() 103 104 select { 105 case <-lw.Msg: 106 case err := <-lw.Err: 107 assert.NilError(t, err) 108 case <-followLogsDone: 109 t.Fatal("follow logs finished unexpectedly") 110 case <-time.After(10 * time.Second): 111 t.Fatal("timeout waiting for log message") 112 } 113 114 lw.ConsumerGone() 115 select { 116 case <-followLogsDone: 117 case <-time.After(20 * time.Second): 118 t.Fatal("timeout waiting for followLogs() to finish") 119 } 120 } 121 122 func TestFollowLogsProducerGone(t *testing.T) { 123 lw := logger.NewLogWatcher() 124 125 f, err := ioutil.TempFile("", t.Name()) 126 assert.NilError(t, err) 127 defer os.Remove(f.Name()) 128 129 var sent, received, closed int 130 makeDecoder := func(rdr io.Reader) func() (*logger.Message, error) { 131 return func() (*logger.Message, error) { 132 if closed == 1 { 133 closed++ 134 t.Logf("logDecode() closed after sending %d messages\n", sent) 135 return nil, io.EOF 136 } else if closed > 1 { 137 t.Fatal("logDecode() called after closing!") 138 return nil, io.EOF 139 } 140 sent++ 141 return &logger.Message{}, nil 142 } 143 } 144 var since, until time.Time 145 146 followLogsDone := make(chan struct{}) 147 go func() { 148 followLogs(f, lw, make(chan interface{}), makeDecoder, since, until) 149 close(followLogsDone) 150 }() 151 152 // read 1 message 153 select { 154 case <-lw.Msg: 155 received++ 156 case err := <-lw.Err: 157 assert.NilError(t, err) 158 case <-followLogsDone: 159 t.Fatal("followLogs() finished unexpectedly") 160 case <-time.After(10 * time.Second): 161 t.Fatal("timeout waiting for log message") 162 } 163 164 // "stop" the "container" 165 closed = 1 166 lw.ProducerGone() 167 168 // should receive all the messages sent 169 readDone := make(chan struct{}) 170 go func() { 171 defer close(readDone) 172 for { 173 select { 174 case <-lw.Msg: 175 received++ 176 if received == sent { 177 return 178 } 179 case err := <-lw.Err: 180 assert.NilError(t, err) 181 } 182 } 183 }() 184 select { 185 case <-readDone: 186 case <-time.After(30 * time.Second): 187 t.Fatalf("timeout waiting for log messages to be read (sent: %d, received: %d", sent, received) 188 } 189 190 t.Logf("messages sent: %d, received: %d", sent, received) 191 192 // followLogs() should be done by now 193 select { 194 case <-followLogsDone: 195 case <-time.After(30 * time.Second): 196 t.Fatal("timeout waiting for followLogs() to finish") 197 } 198 199 select { 200 case <-lw.WatchConsumerGone(): 201 t.Fatal("consumer should not have exited") 202 default: 203 } 204 } 205 206 func TestCheckCapacityAndRotate(t *testing.T) { 207 dir, err := ioutil.TempDir("", t.Name()) 208 assert.NilError(t, err) 209 defer os.RemoveAll(dir) 210 211 f, err := ioutil.TempFile(dir, "log") 212 assert.NilError(t, err) 213 214 l := &LogFile{ 215 f: f, 216 capacity: 5, 217 maxFiles: 3, 218 compress: true, 219 notifyRotate: pubsub.NewPublisher(0, 1), 220 perms: 0600, 221 marshal: func(msg *logger.Message) ([]byte, error) { 222 return msg.Line, nil 223 }, 224 } 225 defer l.Close() 226 227 assert.NilError(t, l.WriteLogEntry(&logger.Message{Line: []byte("hello world!")})) 228 229 dStringer := dirStringer{dir} 230 231 _, err = os.Stat(f.Name() + ".1") 232 assert.Assert(t, os.IsNotExist(err), dStringer) 233 234 assert.NilError(t, l.WriteLogEntry(&logger.Message{Line: []byte("hello world!")})) 235 _, err = os.Stat(f.Name() + ".1") 236 assert.NilError(t, err, dStringer) 237 238 assert.NilError(t, l.WriteLogEntry(&logger.Message{Line: []byte("hello world!")})) 239 _, err = os.Stat(f.Name() + ".1") 240 assert.NilError(t, err, dStringer) 241 _, err = os.Stat(f.Name() + ".2.gz") 242 assert.NilError(t, err, dStringer) 243 244 // Now let's simulate a failed rotation where the file was able to be closed but something else happened elsewhere 245 // down the line. 246 // We want to make sure that we can recover in the case that `l.f` was closed while attempting a rotation. 247 l.f.Close() 248 assert.NilError(t, l.WriteLogEntry(&logger.Message{Line: []byte("hello world!")})) 249 } 250 251 type dirStringer struct { 252 d string 253 } 254 255 func (d dirStringer) String() string { 256 ls, err := ioutil.ReadDir(d.d) 257 if err != nil { 258 return "" 259 } 260 var s strings.Builder 261 s.WriteString("\n") 262 263 for _, fi := range ls { 264 s.WriteString(fi.Name() + "\n") 265 } 266 return s.String() 267 }