github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/daemon/logger/adapter_test.go (about) 1 package logger // import "github.com/docker/docker/daemon/logger" 2 3 import ( 4 "encoding/binary" 5 "io" 6 "sync" 7 "testing" 8 "time" 9 10 "github.com/docker/docker/api/types/plugins/logdriver" 11 protoio "github.com/gogo/protobuf/io" 12 "gotest.tools/v3/assert" 13 is "gotest.tools/v3/assert/cmp" 14 ) 15 16 // mockLoggingPlugin implements the loggingPlugin interface for testing purposes 17 // it only supports a single log stream 18 type mockLoggingPlugin struct { 19 io.WriteCloser 20 inStream io.Reader 21 logs []*logdriver.LogEntry 22 c *sync.Cond 23 err error 24 } 25 26 func newMockLoggingPlugin() *mockLoggingPlugin { 27 r, w := io.Pipe() 28 return &mockLoggingPlugin{ 29 WriteCloser: w, 30 inStream: r, 31 logs: []*logdriver.LogEntry{}, 32 c: sync.NewCond(new(sync.Mutex)), 33 } 34 } 35 36 func (l *mockLoggingPlugin) StartLogging(file string, info Info) error { 37 go func() { 38 dec := protoio.NewUint32DelimitedReader(l.inStream, binary.BigEndian, 1e6) 39 for { 40 var msg logdriver.LogEntry 41 if err := dec.ReadMsg(&msg); err != nil { 42 l.c.L.Lock() 43 if l.err == nil { 44 l.err = err 45 } 46 l.c.L.Unlock() 47 48 l.c.Broadcast() 49 return 50 } 51 52 l.c.L.Lock() 53 l.logs = append(l.logs, &msg) 54 l.c.L.Unlock() 55 l.c.Broadcast() 56 } 57 }() 58 return nil 59 } 60 61 func (l *mockLoggingPlugin) StopLogging(file string) error { 62 l.c.L.Lock() 63 if l.err == nil { 64 l.err = io.EOF 65 } 66 l.c.L.Unlock() 67 l.c.Broadcast() 68 return nil 69 } 70 71 func (l *mockLoggingPlugin) Capabilities() (cap Capability, err error) { 72 return Capability{ReadLogs: true}, nil 73 } 74 75 func (l *mockLoggingPlugin) ReadLogs(info Info, config ReadConfig) (io.ReadCloser, error) { 76 r, w := io.Pipe() 77 78 go func() { 79 var idx int 80 enc := logdriver.NewLogEntryEncoder(w) 81 82 l.c.L.Lock() 83 defer l.c.L.Unlock() 84 for { 85 if l.err != nil { 86 w.Close() 87 return 88 } 89 90 if idx >= len(l.logs) { 91 if !config.Follow { 92 w.Close() 93 return 94 } 95 96 l.c.Wait() 97 continue 98 } 99 100 if err := enc.Encode(l.logs[idx]); err != nil { 101 w.CloseWithError(err) 102 return 103 } 104 idx++ 105 } 106 }() 107 108 return r, nil 109 } 110 111 func (l *mockLoggingPlugin) waitLen(i int) { 112 l.c.L.Lock() 113 defer l.c.L.Unlock() 114 for len(l.logs) < i { 115 l.c.Wait() 116 } 117 } 118 119 func (l *mockLoggingPlugin) check(t *testing.T) { 120 if l.err != nil && l.err != io.EOF { 121 t.Fatal(l.err) 122 } 123 } 124 125 func newMockPluginAdapter(plugin *mockLoggingPlugin) Logger { 126 enc := logdriver.NewLogEntryEncoder(plugin) 127 a := &pluginAdapterWithRead{ 128 &pluginAdapter{ 129 plugin: plugin, 130 stream: plugin, 131 enc: enc, 132 }, 133 } 134 a.plugin.StartLogging("", Info{}) 135 return a 136 } 137 138 func TestAdapterReadLogs(t *testing.T) { 139 plugin := newMockLoggingPlugin() 140 l := newMockPluginAdapter(plugin) 141 142 testMsg := []Message{ 143 {Line: []byte("Are you the keymaker?"), Timestamp: time.Now()}, 144 {Line: []byte("Follow the white rabbit"), Timestamp: time.Now()}, 145 } 146 for _, msg := range testMsg { 147 m := msg.copy() 148 assert.Check(t, l.Log(m)) 149 } 150 151 // Wait until messages are read into plugin 152 plugin.waitLen(len(testMsg)) 153 154 lr, ok := l.(LogReader) 155 assert.Check(t, ok, "Logger does not implement LogReader") 156 157 lw := lr.ReadLogs(ReadConfig{}) 158 159 for _, x := range testMsg { 160 select { 161 case msg := <-lw.Msg: 162 testMessageEqual(t, &x, msg) 163 case <-time.After(10 * time.Second): 164 t.Fatal("timeout reading logs") 165 } 166 } 167 168 select { 169 case _, ok := <-lw.Msg: 170 assert.Check(t, !ok, "expected message channel to be closed") 171 case <-time.After(10 * time.Second): 172 t.Fatal("timeout waiting for message channel to close") 173 } 174 lw.ConsumerGone() 175 176 lw = lr.ReadLogs(ReadConfig{Follow: true}) 177 for _, x := range testMsg { 178 select { 179 case msg := <-lw.Msg: 180 testMessageEqual(t, &x, msg) 181 case <-time.After(10 * time.Second): 182 t.Fatal("timeout reading logs") 183 } 184 } 185 186 x := Message{Line: []byte("Too infinity and beyond!"), Timestamp: time.Now()} 187 assert.Check(t, l.Log(x.copy())) 188 189 select { 190 case msg, ok := <-lw.Msg: 191 assert.Check(t, ok, "message channel unexpectedly closed") 192 testMessageEqual(t, &x, msg) 193 case <-time.After(10 * time.Second): 194 t.Fatal("timeout reading logs") 195 } 196 197 l.Close() 198 select { 199 case msg, ok := <-lw.Msg: 200 assert.Check(t, !ok, "expected message channel to be closed") 201 assert.Check(t, is.Nil(msg)) 202 case <-time.After(10 * time.Second): 203 t.Fatal("timeout waiting for logger to close") 204 } 205 206 plugin.check(t) 207 } 208 209 func testMessageEqual(t *testing.T, a, b *Message) { 210 assert.Check(t, is.DeepEqual(a.Line, b.Line)) 211 assert.Check(t, is.DeepEqual(a.Timestamp.UnixNano(), b.Timestamp.UnixNano())) 212 assert.Check(t, is.Equal(a.Source, b.Source)) 213 }