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