github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+incompatible/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  }