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  }