github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/engine/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 53 l.c.L.Lock() 54 l.logs = append(l.logs, &msg) 55 l.c.L.Unlock() 56 l.c.Broadcast() 57 } 58 59 }() 60 return nil 61 } 62 63 func (l *mockLoggingPlugin) StopLogging(file string) error { 64 l.c.L.Lock() 65 if l.err == nil { 66 l.err = io.EOF 67 } 68 l.c.L.Unlock() 69 l.c.Broadcast() 70 return nil 71 } 72 73 func (l *mockLoggingPlugin) Capabilities() (cap Capability, err error) { 74 return Capability{ReadLogs: true}, nil 75 } 76 77 func (l *mockLoggingPlugin) ReadLogs(info Info, config ReadConfig) (io.ReadCloser, error) { 78 r, w := io.Pipe() 79 80 go func() { 81 var idx int 82 enc := logdriver.NewLogEntryEncoder(w) 83 84 l.c.L.Lock() 85 defer l.c.L.Unlock() 86 for { 87 if l.err != nil { 88 w.Close() 89 return 90 } 91 92 if idx >= len(l.logs) { 93 if !config.Follow { 94 w.Close() 95 return 96 } 97 98 l.c.Wait() 99 continue 100 } 101 102 if err := enc.Encode(l.logs[idx]); err != nil { 103 w.CloseWithError(err) 104 return 105 } 106 idx++ 107 } 108 }() 109 110 return r, nil 111 } 112 113 func (l *mockLoggingPlugin) waitLen(i int) { 114 l.c.L.Lock() 115 defer l.c.L.Unlock() 116 for len(l.logs) < i { 117 l.c.Wait() 118 } 119 } 120 121 func (l *mockLoggingPlugin) check(t *testing.T) { 122 if l.err != nil && l.err != io.EOF { 123 t.Fatal(l.err) 124 } 125 } 126 127 func newMockPluginAdapter(plugin *mockLoggingPlugin) Logger { 128 enc := logdriver.NewLogEntryEncoder(plugin) 129 a := &pluginAdapterWithRead{ 130 &pluginAdapter{ 131 plugin: plugin, 132 stream: plugin, 133 enc: enc, 134 }, 135 } 136 a.plugin.StartLogging("", Info{}) 137 return a 138 } 139 140 func TestAdapterReadLogs(t *testing.T) { 141 plugin := newMockLoggingPlugin() 142 l := newMockPluginAdapter(plugin) 143 144 testMsg := []Message{ 145 {Line: []byte("Are you the keymaker?"), Timestamp: time.Now()}, 146 {Line: []byte("Follow the white rabbit"), Timestamp: time.Now()}, 147 } 148 for _, msg := range testMsg { 149 m := msg.copy() 150 assert.Check(t, l.Log(m)) 151 } 152 153 // Wait until messages are read into plugin 154 plugin.waitLen(len(testMsg)) 155 156 lr, ok := l.(LogReader) 157 assert.Check(t, ok, "Logger does not implement LogReader") 158 159 lw := lr.ReadLogs(ReadConfig{}) 160 161 for _, x := range testMsg { 162 select { 163 case msg := <-lw.Msg: 164 testMessageEqual(t, &x, msg) 165 case <-time.After(10 * time.Second): 166 t.Fatal("timeout reading logs") 167 } 168 } 169 170 select { 171 case _, ok := <-lw.Msg: 172 assert.Check(t, !ok, "expected message channel to be closed") 173 case <-time.After(10 * time.Second): 174 t.Fatal("timeout waiting for message channel to close") 175 176 } 177 lw.ProducerGone() 178 179 lw = lr.ReadLogs(ReadConfig{Follow: true}) 180 for _, x := range testMsg { 181 select { 182 case msg := <-lw.Msg: 183 testMessageEqual(t, &x, msg) 184 case <-time.After(10 * time.Second): 185 t.Fatal("timeout reading logs") 186 } 187 } 188 189 x := Message{Line: []byte("Too infinity and beyond!"), Timestamp: time.Now()} 190 assert.Check(t, l.Log(x.copy())) 191 192 select { 193 case msg, ok := <-lw.Msg: 194 assert.Check(t, ok, "message channel unexpectedly closed") 195 testMessageEqual(t, &x, msg) 196 case <-time.After(10 * time.Second): 197 t.Fatal("timeout reading logs") 198 } 199 200 l.Close() 201 select { 202 case msg, ok := <-lw.Msg: 203 assert.Check(t, !ok, "expected message channel to be closed") 204 assert.Check(t, is.Nil(msg)) 205 case <-time.After(10 * time.Second): 206 t.Fatal("timeout waiting for logger to close") 207 } 208 209 plugin.check(t) 210 } 211 212 func testMessageEqual(t *testing.T, a, b *Message) { 213 assert.Check(t, is.DeepEqual(a.Line, b.Line)) 214 assert.Check(t, is.DeepEqual(a.Timestamp.UnixNano(), b.Timestamp.UnixNano())) 215 assert.Check(t, is.Equal(a.Source, b.Source)) 216 }