...

Source file src/github.com/opencontainers/runc/libcontainer/logs/logs_linux_test.go

Documentation: github.com/opencontainers/runc/libcontainer/logs

     1  package logs
     2  
     3  import (
     4  	"bytes"
     5  	"io"
     6  	"os"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/sirupsen/logrus"
    11  )
    12  
    13  const msgErr = `"level":"error"`
    14  
    15  func TestLoggingToFile(t *testing.T) {
    16  	l := runLogForwarding(t)
    17  
    18  	msg := `"level":"info","msg":"kitten"`
    19  	logToLogWriter(t, l, msg)
    20  	finish(t, l)
    21  	check(t, l, msg, msgErr)
    22  }
    23  
    24  func TestLogForwardingDoesNotStopOnJsonDecodeErr(t *testing.T) {
    25  	l := runLogForwarding(t)
    26  
    27  	logToLogWriter(t, l, `"invalid-json-with-kitten"`)
    28  	checkWait(t, l, msgErr, "")
    29  
    30  	truncateLogFile(t, l.file)
    31  
    32  	msg := `"level":"info","msg":"puppy"`
    33  	logToLogWriter(t, l, msg)
    34  	finish(t, l)
    35  	check(t, l, msg, msgErr)
    36  }
    37  
    38  func TestLogForwardingDoesNotStopOnLogLevelParsingErr(t *testing.T) {
    39  	l := runLogForwarding(t)
    40  
    41  	msg := `"level":"alert","msg":"puppy"`
    42  	logToLogWriter(t, l, msg)
    43  	checkWait(t, l, msgErr, msg)
    44  
    45  	truncateLogFile(t, l.file)
    46  
    47  	msg = `"level":"info","msg":"puppy"`
    48  	logToLogWriter(t, l, msg)
    49  	finish(t, l)
    50  	check(t, l, msg, msgErr)
    51  }
    52  
    53  func TestLogForwardingStopsAfterClosingTheWriter(t *testing.T) {
    54  	l := runLogForwarding(t)
    55  
    56  	msg := `"level":"info","msg":"sync"`
    57  	logToLogWriter(t, l, msg)
    58  
    59  	// Do not use finish() here as we check done pipe ourselves.
    60  	l.w.Close()
    61  	select {
    62  	case <-l.done:
    63  	case <-time.After(10 * time.Second):
    64  		t.Fatal("log forwarding did not stop after closing the pipe")
    65  	}
    66  
    67  	check(t, l, msg, msgErr)
    68  }
    69  
    70  func logToLogWriter(t *testing.T, l *log, message string) {
    71  	t.Helper()
    72  	_, err := l.w.Write([]byte("{" + message + "}\n"))
    73  	if err != nil {
    74  		t.Fatalf("failed to write %q to log writer: %v", message, err)
    75  	}
    76  }
    77  
    78  type log struct {
    79  	w    io.WriteCloser
    80  	file *os.File
    81  	done chan error
    82  }
    83  
    84  func runLogForwarding(t *testing.T) *log {
    85  	t.Helper()
    86  	logR, logW, err := os.Pipe()
    87  	if err != nil {
    88  		t.Fatal(err)
    89  	}
    90  	t.Cleanup(func() {
    91  		logR.Close()
    92  		logW.Close()
    93  	})
    94  
    95  	tempFile, err := os.CreateTemp("", "")
    96  	if err != nil {
    97  		t.Fatal(err)
    98  	}
    99  	t.Cleanup(func() {
   100  		tempFile.Close()
   101  		os.Remove(tempFile.Name())
   102  	})
   103  
   104  	logrus.SetOutput(tempFile)
   105  	logrus.SetFormatter(&logrus.JSONFormatter{})
   106  	doneForwarding := ForwardLogs(logR)
   107  
   108  	return &log{w: logW, done: doneForwarding, file: tempFile}
   109  }
   110  
   111  func finish(t *testing.T, l *log) {
   112  	t.Helper()
   113  	l.w.Close()
   114  	if err := <-l.done; err != nil {
   115  		t.Fatalf("ForwardLogs: %v", err)
   116  	}
   117  }
   118  
   119  func truncateLogFile(t *testing.T, file *os.File) {
   120  	t.Helper()
   121  
   122  	err := file.Truncate(0)
   123  	if err != nil {
   124  		t.Fatalf("failed to truncate log file: %v", err)
   125  	}
   126  }
   127  
   128  // check checks that the file contains txt and does not contain notxt.
   129  func check(t *testing.T, l *log, txt, notxt string) {
   130  	t.Helper()
   131  	contents, err := os.ReadFile(l.file.Name())
   132  	if err != nil {
   133  		t.Fatal(err)
   134  	}
   135  	if txt != "" && !bytes.Contains(contents, []byte(txt)) {
   136  		t.Fatalf("%s does not contain %s", contents, txt)
   137  	}
   138  	if notxt != "" && bytes.Contains(contents, []byte(notxt)) {
   139  		t.Fatalf("%s does contain %s", contents, notxt)
   140  	}
   141  }
   142  
   143  // checkWait is like check, but if the file is empty,
   144  // it waits until it's not.
   145  func checkWait(t *testing.T, l *log, txt string, notxt string) {
   146  	t.Helper()
   147  	const (
   148  		delay = 100 * time.Millisecond
   149  		iter  = 3
   150  	)
   151  	for i := 0; ; i++ {
   152  		st, err := l.file.Stat()
   153  		if err != nil {
   154  			t.Fatal(err)
   155  		}
   156  		if st.Size() > 0 {
   157  			break
   158  		}
   159  		if i == iter {
   160  			t.Fatalf("waited %s for file %s to be non-empty but it still is", iter*delay, l.file.Name())
   161  		}
   162  		time.Sleep(delay)
   163  	}
   164  
   165  	check(t, l, txt, notxt)
   166  }
   167  

View as plain text