github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/worker/logforwarder/logforwarder_test.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package logforwarder_test 5 6 import ( 7 "time" 8 9 "github.com/juju/errors" 10 "github.com/juju/loggo" 11 "github.com/juju/testing" 12 jc "github.com/juju/testing/checkers" 13 gc "gopkg.in/check.v1" 14 15 "github.com/juju/juju/api/base" 16 "github.com/juju/juju/apiserver/params" 17 "github.com/juju/juju/logfwd" 18 "github.com/juju/juju/logfwd/syslog" 19 coretesting "github.com/juju/juju/testing" 20 "github.com/juju/juju/version" 21 "github.com/juju/juju/watcher" 22 "github.com/juju/juju/worker" 23 "github.com/juju/juju/worker/logforwarder" 24 "github.com/juju/juju/worker/workertest" 25 ) 26 27 type LogForwarderSuite struct { 28 testing.IsolationSuite 29 30 stub *testing.Stub 31 stream *stubStream 32 sender *stubSender 33 rec logfwd.Record 34 } 35 36 var _ = gc.Suite(&LogForwarderSuite{}) 37 38 func (s *LogForwarderSuite) SetUpTest(c *gc.C) { 39 s.IsolationSuite.SetUpTest(c) 40 41 s.stub = &testing.Stub{} 42 s.stream = newStubStream(s.stub) 43 s.sender = newStubSender(s.stub) 44 s.rec = logfwd.Record{ 45 Origin: logfwd.Origin{ 46 ControllerUUID: "feebdaed-2f18-4fd2-967d-db9663db7bea", 47 ModelUUID: "deadbeef-2f18-4fd2-967d-db9663db7bea", 48 Hostname: "machine-99.deadbeef-2f18-4fd2-967d-db9663db7bea", 49 Type: logfwd.OriginTypeMachine, 50 Name: "99", 51 Software: logfwd.Software{ 52 PrivateEnterpriseNumber: 28978, 53 Name: "jujud-machine-agent", 54 Version: version.Current, 55 }, 56 }, 57 ID: 10, 58 Timestamp: time.Now(), 59 Level: loggo.INFO, 60 Location: logfwd.SourceLocation{ 61 Module: "api.logstream.test", 62 Filename: "test.go", 63 Line: 42, 64 }, 65 Message: "send to 10.0.0.1", 66 } 67 } 68 69 func (s *LogForwarderSuite) checkNext(c *gc.C, rec logfwd.Record) { 70 s.stream.waitBeforeNext(c) 71 s.stream.waitAfterNext(c) 72 s.sender.waitAfterSend(c) 73 s.stub.CheckCallNames(c, "Next", "Send") 74 s.stub.CheckCall(c, 1, "Send", []logfwd.Record{rec}) 75 s.stub.ResetCalls() 76 } 77 78 func (s *LogForwarderSuite) checkClose(c *gc.C, lf worker.Worker, expected error) { 79 go func() { 80 s.sender.waitBeforeClose(c) 81 }() 82 var err error 83 if expected == nil { 84 workertest.CleanKill(c, lf) 85 } else { 86 err = workertest.CheckKill(c, lf) 87 } 88 c.Check(errors.Cause(err), gc.Equals, expected) 89 s.stub.CheckCallNames(c, "Close") 90 } 91 92 type mockLogForwardConfig struct { 93 enabled bool 94 host string 95 changes chan struct{} 96 } 97 98 type mockWatcher struct { 99 watcher.NotifyWatcher 100 changes chan struct{} 101 } 102 103 func (m *mockWatcher) Changes() watcher.NotifyChannel { 104 return m.changes 105 } 106 107 func (*mockWatcher) Kill() { 108 } 109 110 func (*mockWatcher) Wait() error { 111 return nil 112 } 113 114 type mockCaller struct { 115 base.APICaller 116 } 117 118 func (*mockCaller) APICall(objType string, version int, id, request string, params, response interface{}) error { 119 return nil 120 } 121 122 func (*mockCaller) BestFacadeVersion(facade string) int { 123 return 0 124 } 125 126 func (c *mockLogForwardConfig) WatchForLogForwardConfigChanges() (watcher.NotifyWatcher, error) { 127 c.changes = make(chan struct{}, 1) 128 c.changes <- struct{}{} 129 return &mockWatcher{ 130 changes: c.changes, 131 }, nil 132 } 133 134 func (c *mockLogForwardConfig) LogForwardConfig() (*syslog.RawConfig, bool, error) { 135 return &syslog.RawConfig{ 136 Enabled: c.enabled, 137 Host: c.host, 138 CACert: coretesting.CACert, 139 ClientCert: coretesting.ServerCert, 140 ClientKey: coretesting.ServerKey, 141 }, true, nil 142 } 143 144 func (s *LogForwarderSuite) newLogForwarderArgs(c *gc.C, stream logforwarder.LogStream, sender *stubSender) logforwarder.OpenLogForwarderArgs { 145 api := &mockLogForwardConfig{ 146 enabled: stream != nil, 147 host: "10.0.0.1", 148 } 149 return s.newLogForwarderArgsWithAPI(c, api, stream, sender) 150 } 151 152 func (s *LogForwarderSuite) newLogForwarderArgsWithAPI(c *gc.C, configAPI logforwarder.LogForwardConfig, stream logforwarder.LogStream, sender *stubSender) logforwarder.OpenLogForwarderArgs { 153 return logforwarder.OpenLogForwarderArgs{ 154 Caller: &mockCaller{}, 155 LogForwardConfig: configAPI, 156 AllModels: true, 157 ControllerUUID: "feebdaed-2f18-4fd2-967d-db9663db7bea", 158 OpenSink: func(cfg *syslog.RawConfig) (*logforwarder.LogSink, error) { 159 sender.host = cfg.Host 160 sink := &logforwarder.LogSink{ 161 sender, 162 } 163 return sink, nil 164 }, 165 OpenLogStream: func(_ base.APICaller, _ params.LogStreamConfig, controllerUUID string) (logforwarder.LogStream, error) { 166 c.Assert(controllerUUID, gc.Equals, "feebdaed-2f18-4fd2-967d-db9663db7bea") 167 return stream, nil 168 }, 169 } 170 } 171 172 func (s *LogForwarderSuite) TestOne(c *gc.C) { 173 s.stream.setRecords(c, []logfwd.Record{ 174 s.rec, 175 }) 176 177 lf, err := logforwarder.NewLogForwarder(s.newLogForwarderArgs(c, s.stream, s.sender)) 178 c.Assert(err, jc.ErrorIsNil) 179 defer s.checkClose(c, lf, nil) 180 181 s.checkNext(c, s.rec) 182 } 183 184 func (s *LogForwarderSuite) TestConfigChange(c *gc.C) { 185 rec2 := s.rec 186 rec2.ID = 11 187 s.stream.setRecords(c, []logfwd.Record{ 188 s.rec, 189 rec2, 190 }) 191 192 api := &mockLogForwardConfig{ 193 enabled: true, 194 host: "10.0.0.1", 195 } 196 lf, err := logforwarder.NewLogForwarder(s.newLogForwarderArgsWithAPI(c, api, s.stream, s.sender)) 197 c.Assert(err, jc.ErrorIsNil) 198 defer s.checkClose(c, lf, nil) 199 200 s.checkNext(c, s.rec) 201 202 api.host = "10.0.0.2" 203 api.changes <- struct{}{} 204 s.sender.waitBeforeClose(c) 205 s.stream.waitBeforeNext(c) 206 s.stream.waitAfterNext(c) 207 s.sender.waitAfterSend(c) 208 // Check that the config change has been picked up and 209 // that the second record is sent. 210 rec2.Message = "send to 10.0.0.2" 211 s.stub.CheckCall(c, 2, "Send", []logfwd.Record{rec2}) 212 s.stub.ResetCalls() 213 } 214 215 func (s *LogForwarderSuite) TestNotEnabled(c *gc.C) { 216 lf, err := logforwarder.NewLogForwarder(s.newLogForwarderArgs(c, nil, s.sender)) 217 c.Assert(err, jc.ErrorIsNil) 218 workertest.CleanKill(c, lf) 219 } 220 221 func (s *LogForwarderSuite) TestStreamError(c *gc.C) { 222 failure := errors.New("<failure>") 223 s.stub.SetErrors(nil, nil, failure) 224 s.stream.setRecords(c, []logfwd.Record{ 225 s.rec, 226 }) 227 lf, err := logforwarder.NewLogForwarder(s.newLogForwarderArgs(c, s.stream, s.sender)) 228 c.Assert(err, jc.ErrorIsNil) 229 230 s.checkNext(c, s.rec) 231 s.stream.waitBeforeNext(c) 232 s.stream.waitAfterNext(c) 233 s.stub.CheckCallNames(c, "Next") 234 s.stub.ResetCalls() 235 s.checkClose(c, lf, failure) 236 } 237 238 func (s *LogForwarderSuite) TestSenderError(c *gc.C) { 239 failure := errors.New("<failure>") 240 s.stub.SetErrors(nil, nil, nil, failure) 241 rec2 := s.rec 242 rec2.ID = 11 243 s.stream.setRecords(c, []logfwd.Record{ 244 s.rec, 245 rec2, 246 }) 247 lf, err := logforwarder.NewLogForwarder(s.newLogForwarderArgs(c, s.stream, s.sender)) 248 c.Assert(err, jc.ErrorIsNil) 249 250 s.checkNext(c, s.rec) 251 s.checkNext(c, rec2) 252 s.checkClose(c, lf, failure) 253 } 254 255 type stubStream struct { 256 stub *testing.Stub 257 258 waitCh chan struct{} 259 ReturnNext <-chan logfwd.Record 260 } 261 262 func newStubStream(stub *testing.Stub) *stubStream { 263 return &stubStream{ 264 stub: stub, 265 waitCh: make(chan struct{}), 266 } 267 } 268 269 func (s *stubStream) setRecords(c *gc.C, recs []logfwd.Record) { 270 recCh := make(chan logfwd.Record) 271 go func() { 272 for _, rec := range recs { 273 select { 274 case recCh <- rec: 275 case <-time.After(coretesting.LongWait): 276 c.Error("timed out waiting for records on the channel") 277 } 278 279 } 280 }() 281 s.ReturnNext = recCh 282 } 283 284 func (s *stubStream) waitBeforeNext(c *gc.C) { 285 select { 286 case <-s.waitCh: 287 case <-time.After(coretesting.LongWait): 288 c.Error("timed out waiting") 289 } 290 } 291 292 func (s *stubStream) waitAfterNext(c *gc.C) { 293 select { 294 case <-s.waitCh: 295 case <-time.After(coretesting.LongWait): 296 c.Error("timed out waiting") 297 } 298 } 299 300 func (s *stubStream) Next() ([]logfwd.Record, error) { 301 s.waitCh <- struct{}{} 302 s.stub.AddCall("Next") 303 s.waitCh <- struct{}{} 304 if err := s.stub.NextErr(); err != nil { 305 return []logfwd.Record{}, errors.Trace(err) 306 } 307 308 rec := <-s.ReturnNext 309 return []logfwd.Record{rec}, nil 310 } 311 312 type stubSender struct { 313 stub *testing.Stub 314 315 host string 316 waitSendCh chan struct{} 317 waitCloseCh chan struct{} 318 } 319 320 func newStubSender(stub *testing.Stub) *stubSender { 321 return &stubSender{ 322 stub: stub, 323 waitSendCh: make(chan struct{}), 324 waitCloseCh: make(chan struct{}), 325 } 326 } 327 328 func (s *stubSender) waitAfterSend(c *gc.C) { 329 select { 330 case <-s.waitSendCh: 331 case <-time.After(coretesting.LongWait): 332 c.Error("timed out waiting") 333 } 334 } 335 336 func (s *stubSender) waitBeforeClose(c *gc.C) { 337 select { 338 case <-s.waitCloseCh: 339 case <-time.After(coretesting.LongWait): 340 c.Error("timed out waiting") 341 } 342 } 343 344 func (s *stubSender) Send(records []logfwd.Record) error { 345 for i, rec := range records { 346 rec.Message = "send to " + s.host 347 records[i] = rec 348 } 349 s.stub.AddCall("Send", records) 350 s.waitSendCh <- struct{}{} 351 if err := s.stub.NextErr(); err != nil { 352 return errors.Trace(err) 353 } 354 355 return nil 356 } 357 358 func (s *stubSender) Close() error { 359 s.waitCloseCh <- struct{}{} 360 s.stub.AddCall("Close") 361 if err := s.stub.NextErr(); err != nil { 362 return errors.Trace(err) 363 } 364 365 return nil 366 }