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  }