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  }