github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/apiserver/debuglog_db_internal_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package apiserver
     5  
     6  import (
     7  	"fmt"
     8  	"time"
     9  
    10  	"github.com/juju/loggo"
    11  	jc "github.com/juju/testing/checkers"
    12  	gc "gopkg.in/check.v1"
    13  
    14  	"github.com/juju/juju/state"
    15  	coretesting "github.com/juju/juju/testing"
    16  )
    17  
    18  type debugLogDBIntSuite struct {
    19  	coretesting.BaseSuite
    20  	sock *fakeDebugLogSocket
    21  }
    22  
    23  var _ = gc.Suite(&debugLogDBIntSuite{})
    24  
    25  func (s *debugLogDBIntSuite) SetUpTest(c *gc.C) {
    26  	s.BaseSuite.SetUpTest(c)
    27  	s.sock = newFakeDebugLogSocket()
    28  }
    29  
    30  func (s *debugLogDBIntSuite) TestParamConversion(c *gc.C) {
    31  	reqParams := &debugLogParams{
    32  		fromTheStart:  false,
    33  		noTail:        true,
    34  		backlog:       11,
    35  		filterLevel:   loggo.INFO,
    36  		includeEntity: []string{"foo"},
    37  		includeModule: []string{"bar"},
    38  		excludeEntity: []string{"baz"},
    39  		excludeModule: []string{"qux"},
    40  	}
    41  
    42  	called := false
    43  	s.PatchValue(&newLogTailer, func(_ state.LoggingState, params *state.LogTailerParams) (state.LogTailer, error) {
    44  		called = true
    45  
    46  		// Start time will be used once the client is extended to send
    47  		// time range arguments.
    48  		c.Assert(params.StartTime.IsZero(), jc.IsTrue)
    49  		c.Assert(params.NoTail, jc.IsTrue)
    50  		c.Assert(params.MinLevel, gc.Equals, loggo.INFO)
    51  		c.Assert(params.InitialLines, gc.Equals, 11)
    52  		c.Assert(params.IncludeEntity, jc.DeepEquals, []string{"foo"})
    53  		c.Assert(params.IncludeModule, jc.DeepEquals, []string{"bar"})
    54  		c.Assert(params.ExcludeEntity, jc.DeepEquals, []string{"baz"})
    55  		c.Assert(params.ExcludeModule, jc.DeepEquals, []string{"qux"})
    56  
    57  		return newFakeLogTailer(), nil
    58  	})
    59  
    60  	stop := make(chan struct{})
    61  	close(stop) // Stop the request immediately.
    62  	err := handleDebugLogDBRequest(nil, reqParams, s.sock, stop)
    63  	c.Assert(err, jc.ErrorIsNil)
    64  	c.Assert(called, jc.IsTrue)
    65  }
    66  
    67  func (s *debugLogDBIntSuite) TestParamConversionReplay(c *gc.C) {
    68  	reqParams := &debugLogParams{
    69  		fromTheStart: true,
    70  		backlog:      123,
    71  	}
    72  
    73  	called := false
    74  	s.PatchValue(&newLogTailer, func(_ state.LoggingState, params *state.LogTailerParams) (state.LogTailer, error) {
    75  		called = true
    76  
    77  		c.Assert(params.StartTime.IsZero(), jc.IsTrue)
    78  		c.Assert(params.InitialLines, gc.Equals, 0)
    79  
    80  		return newFakeLogTailer(), nil
    81  	})
    82  
    83  	stop := make(chan struct{})
    84  	close(stop) // Stop the request immediately.
    85  	err := handleDebugLogDBRequest(nil, reqParams, s.sock, stop)
    86  	c.Assert(err, jc.ErrorIsNil)
    87  	c.Assert(called, jc.IsTrue)
    88  }
    89  
    90  func (s *debugLogDBIntSuite) TestFullRequest(c *gc.C) {
    91  	// Set up a fake log tailer with a 2 log records ready to send.
    92  	tailer := newFakeLogTailer()
    93  	tailer.logsCh <- &state.LogRecord{
    94  		Time:     time.Date(2015, 6, 19, 15, 34, 37, 0, time.UTC),
    95  		Entity:   "machine-99",
    96  		Module:   "some.where",
    97  		Location: "code.go:42",
    98  		Level:    loggo.INFO,
    99  		Message:  "stuff happened",
   100  	}
   101  	tailer.logsCh <- &state.LogRecord{
   102  		Time:     time.Date(2015, 6, 19, 15, 36, 40, 0, time.UTC),
   103  		Entity:   "unit-foo-2",
   104  		Module:   "else.where",
   105  		Location: "go.go:22",
   106  		Level:    loggo.ERROR,
   107  		Message:  "whoops",
   108  	}
   109  	s.PatchValue(&newLogTailer, func(_ state.LoggingState, params *state.LogTailerParams) (state.LogTailer, error) {
   110  		return tailer, nil
   111  	})
   112  
   113  	stop := make(chan struct{})
   114  	done := s.runRequest(&debugLogParams{}, stop)
   115  
   116  	s.assertOutput(c, []string{
   117  		"ok", // sendOk() call needs to happen first.
   118  		"machine-99: 2015-06-19 15:34:37 INFO some.where code.go:42 stuff happened\n",
   119  		"unit-foo-2: 2015-06-19 15:36:40 ERROR else.where go.go:22 whoops\n",
   120  	})
   121  
   122  	// Check the request stops when requested.
   123  	close(stop)
   124  	s.assertStops(c, done, tailer)
   125  }
   126  
   127  func (s *debugLogDBIntSuite) TestRequestStopsWhenTailerStops(c *gc.C) {
   128  	tailer := newFakeLogTailer()
   129  	s.PatchValue(&newLogTailer, func(_ state.LoggingState, params *state.LogTailerParams) (state.LogTailer, error) {
   130  		close(tailer.logsCh) // make the request stop immediately
   131  		return tailer, nil
   132  	})
   133  
   134  	err := handleDebugLogDBRequest(nil, &debugLogParams{}, s.sock, nil)
   135  	c.Assert(err, jc.ErrorIsNil)
   136  	c.Assert(tailer.stopped, jc.IsTrue)
   137  }
   138  
   139  func (s *debugLogDBIntSuite) TestMaxLines(c *gc.C) {
   140  	// Set up a fake log tailer with a 5 log records ready to send.
   141  	tailer := newFakeLogTailer()
   142  	for i := 0; i < 5; i++ {
   143  		tailer.logsCh <- &state.LogRecord{
   144  			Time:     time.Date(2015, 6, 19, 15, 34, 37, 0, time.UTC),
   145  			Entity:   "machine-99",
   146  			Module:   "some.where",
   147  			Location: "code.go:42",
   148  			Level:    loggo.INFO,
   149  			Message:  "stuff happened",
   150  		}
   151  	}
   152  	s.PatchValue(&newLogTailer, func(_ state.LoggingState, params *state.LogTailerParams) (state.LogTailer, error) {
   153  		return tailer, nil
   154  	})
   155  
   156  	done := s.runRequest(&debugLogParams{maxLines: 3}, nil)
   157  
   158  	s.assertOutput(c, []string{
   159  		"ok", // sendOk() call needs to happen first.
   160  		"machine-99: 2015-06-19 15:34:37 INFO some.where code.go:42 stuff happened\n",
   161  		"machine-99: 2015-06-19 15:34:37 INFO some.where code.go:42 stuff happened\n",
   162  		"machine-99: 2015-06-19 15:34:37 INFO some.where code.go:42 stuff happened\n",
   163  	})
   164  
   165  	// The tailer should now stop by itself after the line limit was reached.
   166  	s.assertStops(c, done, tailer)
   167  }
   168  
   169  func (s *debugLogDBIntSuite) runRequest(params *debugLogParams, stop chan struct{}) chan error {
   170  	done := make(chan error)
   171  	go func() {
   172  		done <- handleDebugLogDBRequest(&fakeState{}, params, s.sock, stop)
   173  	}()
   174  	return done
   175  }
   176  
   177  func (s *debugLogDBIntSuite) assertOutput(c *gc.C, expectedWrites []string) {
   178  	timeout := time.After(coretesting.LongWait)
   179  	for i, expectedWrite := range expectedWrites {
   180  		select {
   181  		case actualWrite := <-s.sock.writes:
   182  			c.Assert(actualWrite, gc.Equals, expectedWrite)
   183  		case <-timeout:
   184  			c.Fatalf("timed out waiting for socket write (received %d)", i)
   185  		}
   186  	}
   187  }
   188  
   189  func (s *debugLogDBIntSuite) assertStops(c *gc.C, done chan error, tailer *fakeLogTailer) {
   190  	select {
   191  	case err := <-done:
   192  		c.Assert(err, jc.ErrorIsNil)
   193  		c.Assert(tailer.stopped, jc.IsTrue)
   194  	case <-time.After(coretesting.LongWait):
   195  		c.Fatal("timed out waiting for request handler to stop")
   196  	}
   197  }
   198  
   199  type fakeState struct {
   200  	state.LoggingState
   201  }
   202  
   203  func newFakeLogTailer() *fakeLogTailer {
   204  	return &fakeLogTailer{
   205  		logsCh: make(chan *state.LogRecord, 10),
   206  	}
   207  }
   208  
   209  type fakeLogTailer struct {
   210  	state.LogTailer
   211  	logsCh  chan *state.LogRecord
   212  	stopped bool
   213  }
   214  
   215  func (t *fakeLogTailer) Logs() <-chan *state.LogRecord {
   216  	return t.logsCh
   217  }
   218  
   219  func (t *fakeLogTailer) Stop() error {
   220  	t.stopped = true
   221  	return nil
   222  }
   223  
   224  func (t *fakeLogTailer) Err() error {
   225  	return nil
   226  }
   227  
   228  func newFakeDebugLogSocket() *fakeDebugLogSocket {
   229  	return &fakeDebugLogSocket{
   230  		writes: make(chan string, 10),
   231  	}
   232  }
   233  
   234  type fakeDebugLogSocket struct {
   235  	writes chan string
   236  }
   237  
   238  func (s *fakeDebugLogSocket) sendOk() {
   239  	s.writes <- "ok"
   240  }
   241  
   242  func (s *fakeDebugLogSocket) sendError(err error) {
   243  	s.writes <- fmt.Sprintf("err: %v", err)
   244  }
   245  
   246  func (s *fakeDebugLogSocket) Write(buf []byte) (int, error) {
   247  	s.writes <- string(buf)
   248  	return len(buf), nil
   249  }