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 }