github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/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  	"github.com/juju/worker/v3/workertest"
    14  	gc "gopkg.in/check.v1"
    15  
    16  	"github.com/juju/juju/api/base"
    17  	"github.com/juju/juju/core/watcher"
    18  	"github.com/juju/juju/logfwd"
    19  	"github.com/juju/juju/logfwd/syslog"
    20  	"github.com/juju/juju/rpc/params"
    21  	coretesting "github.com/juju/juju/testing"
    22  	"github.com/juju/juju/version"
    23  	"github.com/juju/juju/worker/logforwarder"
    24  )
    25  
    26  type LogForwarderSuite struct {
    27  	testing.IsolationSuite
    28  
    29  	stream *stubStream
    30  	sender *stubSender
    31  	rec    logfwd.Record
    32  }
    33  
    34  var _ = gc.Suite(&LogForwarderSuite{})
    35  
    36  func (s *LogForwarderSuite) SetUpTest(c *gc.C) {
    37  	s.IsolationSuite.SetUpTest(c)
    38  
    39  	s.stream = newStubStream()
    40  	s.sender = newStubSender()
    41  	s.rec = logfwd.Record{
    42  		Origin: logfwd.Origin{
    43  			ControllerUUID: "feebdaed-2f18-4fd2-967d-db9663db7bea",
    44  			ModelUUID:      "deadbeef-2f18-4fd2-967d-db9663db7bea",
    45  			Hostname:       "machine-99.deadbeef-2f18-4fd2-967d-db9663db7bea",
    46  			Type:           logfwd.OriginTypeMachine,
    47  			Name:           "99",
    48  			Software: logfwd.Software{
    49  				PrivateEnterpriseNumber: 28978,
    50  				Name:                    "jujud-machine-agent",
    51  				Version:                 version.Current,
    52  			},
    53  		},
    54  		ID:        10,
    55  		Timestamp: time.Now(),
    56  		Level:     loggo.INFO,
    57  		Location: logfwd.SourceLocation{
    58  			Module:   "api.logstream.test",
    59  			Filename: "test.go",
    60  			Line:     42,
    61  		},
    62  		Message: "send to 10.0.0.1",
    63  	}
    64  }
    65  
    66  func (s *LogForwarderSuite) newLogForwarderArgs(
    67  	c *gc.C,
    68  	stream logforwarder.LogStream,
    69  	sender *stubSender,
    70  ) logforwarder.OpenLogForwarderArgs {
    71  	api := &mockLogForwardConfig{
    72  		enabled: stream != nil,
    73  		host:    "10.0.0.1",
    74  	}
    75  	return s.newLogForwarderArgsWithAPI(c, api, stream, sender)
    76  }
    77  
    78  func (s *LogForwarderSuite) newLogForwarderArgsWithAPI(
    79  	c *gc.C,
    80  	configAPI logforwarder.LogForwardConfig,
    81  	stream logforwarder.LogStream,
    82  	sender *stubSender,
    83  ) logforwarder.OpenLogForwarderArgs {
    84  	return logforwarder.OpenLogForwarderArgs{
    85  		Caller:           &mockCaller{},
    86  		LogForwardConfig: configAPI,
    87  		ControllerUUID:   "feebdaed-2f18-4fd2-967d-db9663db7bea",
    88  		OpenSink: func(cfg *syslog.RawConfig) (*logforwarder.LogSink, error) {
    89  			sender.host = cfg.Host
    90  			sink := &logforwarder.LogSink{
    91  				sender,
    92  			}
    93  			return sink, nil
    94  		},
    95  		OpenLogStream: func(_ base.APICaller, _ params.LogStreamConfig, controllerUUID string) (logforwarder.LogStream, error) {
    96  			c.Assert(controllerUUID, gc.Equals, "feebdaed-2f18-4fd2-967d-db9663db7bea")
    97  			return stream, nil
    98  		},
    99  		Logger: loggo.GetLogger("test"),
   100  	}
   101  }
   102  
   103  func (s *LogForwarderSuite) TestOne(c *gc.C) {
   104  	s.stream.addRecords(c, s.rec)
   105  	lf, err := logforwarder.NewLogForwarder(s.newLogForwarderArgs(c, s.stream, s.sender))
   106  	c.Assert(err, jc.ErrorIsNil)
   107  	defer workertest.DirtyKill(c, lf)
   108  
   109  	s.sender.waitForSend(c)
   110  	workertest.CleanKill(c, lf)
   111  	s.sender.stub.CheckCalls(c, []testing.StubCall{
   112  		{"Send", []interface{}{[]logfwd.Record{s.rec}}},
   113  		{"Close", nil},
   114  	})
   115  }
   116  
   117  func (s *LogForwarderSuite) TestConfigChange(c *gc.C) {
   118  	rec0 := s.rec
   119  	rec1 := s.rec
   120  	rec1.ID = 11
   121  
   122  	api := &mockLogForwardConfig{
   123  		enabled: true,
   124  		host:    "10.0.0.1",
   125  	}
   126  	lf, err := logforwarder.NewLogForwarder(s.newLogForwarderArgsWithAPI(c, api, s.stream, s.sender))
   127  	c.Assert(err, jc.ErrorIsNil)
   128  	defer workertest.DirtyKill(c, lf)
   129  
   130  	// Send the first record.
   131  	s.stream.addRecords(c, rec0)
   132  	s.sender.waitForSend(c)
   133  
   134  	// Config change.
   135  	api.host = "10.0.0.2"
   136  	api.changes <- struct{}{}
   137  	s.sender.waitForClose(c)
   138  
   139  	// Send the second record.
   140  	s.stream.addRecords(c, rec1)
   141  	s.sender.waitForSend(c)
   142  
   143  	workertest.CleanKill(c, lf)
   144  
   145  	// Check that both records were sent with the config change
   146  	// applied for the second send.
   147  	rec1.Message = "send to 10.0.0.2"
   148  	s.sender.stub.CheckCalls(c, []testing.StubCall{
   149  		{"Send", []interface{}{[]logfwd.Record{rec0}}},
   150  		{"Close", nil},
   151  		{"Send", []interface{}{[]logfwd.Record{rec1}}},
   152  		{"Close", nil},
   153  	})
   154  }
   155  
   156  func (s *LogForwarderSuite) TestNotEnabled(c *gc.C) {
   157  	lf, err := logforwarder.NewLogForwarder(s.newLogForwarderArgs(c, nil, s.sender))
   158  	c.Assert(err, jc.ErrorIsNil)
   159  
   160  	time.Sleep(coretesting.ShortWait)
   161  	workertest.CleanKill(c, lf)
   162  
   163  	// There should be no stream or sender activity when log
   164  	// forwarding is disabled.
   165  	s.stream.stub.CheckCallNames(c)
   166  	s.sender.stub.CheckCallNames(c)
   167  }
   168  
   169  func (s *LogForwarderSuite) TestStreamError(c *gc.C) {
   170  	failure := errors.New("<failure>")
   171  	s.stream.stub.SetErrors(nil, failure)
   172  	s.stream.addRecords(c, s.rec)
   173  
   174  	lf, err := logforwarder.NewLogForwarder(s.newLogForwarderArgs(c, s.stream, s.sender))
   175  	c.Assert(err, jc.ErrorIsNil)
   176  	defer workertest.DirtyKill(c, lf)
   177  
   178  	err = workertest.CheckKilled(c, lf)
   179  	c.Check(errors.Cause(err), gc.Equals, failure)
   180  
   181  	s.sender.stub.CheckCalls(c, []testing.StubCall{
   182  		{"Send", []interface{}{[]logfwd.Record{s.rec}}},
   183  		{"Close", nil},
   184  	})
   185  }
   186  
   187  func (s *LogForwarderSuite) TestSenderError(c *gc.C) {
   188  	failure := errors.New("<failure>")
   189  	s.sender.stub.SetErrors(nil, failure)
   190  
   191  	rec0 := s.rec
   192  	rec1 := s.rec
   193  	rec1.ID = 11
   194  	s.stream.addRecords(c, rec0, rec1)
   195  
   196  	lf, err := logforwarder.NewLogForwarder(s.newLogForwarderArgs(c, s.stream, s.sender))
   197  	c.Assert(err, jc.ErrorIsNil)
   198  	defer workertest.DirtyKill(c, lf)
   199  
   200  	err = workertest.CheckKilled(c, lf)
   201  	c.Check(errors.Cause(err), gc.Equals, failure)
   202  
   203  	s.sender.stub.CheckCalls(c, []testing.StubCall{
   204  		{"Send", []interface{}{[]logfwd.Record{rec0}}},
   205  		{"Send", []interface{}{[]logfwd.Record{rec1}}},
   206  		{"Close", nil},
   207  	})
   208  }
   209  
   210  type mockLogForwardConfig struct {
   211  	enabled bool
   212  	host    string
   213  	changes chan struct{}
   214  }
   215  
   216  type mockWatcher struct {
   217  	watcher.NotifyWatcher
   218  	changes chan struct{}
   219  }
   220  
   221  func (m *mockWatcher) Changes() watcher.NotifyChannel {
   222  	return m.changes
   223  }
   224  
   225  func (*mockWatcher) Kill() {
   226  }
   227  
   228  func (*mockWatcher) Wait() error {
   229  	return nil
   230  }
   231  
   232  type mockCaller struct {
   233  	base.APICaller
   234  }
   235  
   236  func (*mockCaller) APICall(objType string, version int, id, request string, params, response interface{}) error {
   237  	return nil
   238  }
   239  
   240  func (*mockCaller) BestFacadeVersion(facade string) int {
   241  	return 0
   242  }
   243  
   244  func (c *mockLogForwardConfig) WatchForLogForwardConfigChanges() (watcher.NotifyWatcher, error) {
   245  	c.changes = make(chan struct{}, 1)
   246  	c.changes <- struct{}{}
   247  	return &mockWatcher{
   248  		changes: c.changes,
   249  	}, nil
   250  }
   251  
   252  func (c *mockLogForwardConfig) LogForwardConfig() (*syslog.RawConfig, bool, error) {
   253  	return &syslog.RawConfig{
   254  		Enabled:    c.enabled,
   255  		Host:       c.host,
   256  		CACert:     coretesting.CACert,
   257  		ClientCert: coretesting.ServerCert,
   258  		ClientKey:  coretesting.ServerKey,
   259  	}, true, nil
   260  }
   261  
   262  type stubStream struct {
   263  	stub     *testing.Stub
   264  	nextRecs chan logfwd.Record
   265  }
   266  
   267  func newStubStream() *stubStream {
   268  	return &stubStream{
   269  		stub:     new(testing.Stub),
   270  		nextRecs: make(chan logfwd.Record, 16),
   271  	}
   272  }
   273  
   274  func (s *stubStream) addRecords(c *gc.C, recs ...logfwd.Record) {
   275  	for _, rec := range recs {
   276  		s.nextRecs <- rec
   277  	}
   278  }
   279  
   280  func (s *stubStream) Next() ([]logfwd.Record, error) {
   281  	s.stub.AddCall("Next")
   282  	if err := s.stub.NextErr(); err != nil {
   283  		return []logfwd.Record{}, errors.Trace(err)
   284  	}
   285  	return []logfwd.Record{<-s.nextRecs}, nil
   286  }
   287  
   288  type stubSender struct {
   289  	stub     *testing.Stub
   290  	activity chan string
   291  	host     string
   292  }
   293  
   294  func newStubSender() *stubSender {
   295  	return &stubSender{
   296  		stub:     new(testing.Stub),
   297  		activity: make(chan string, 16),
   298  	}
   299  }
   300  
   301  func (s *stubSender) Send(records []logfwd.Record) error {
   302  	for i, rec := range records {
   303  		rec.Message = "send to " + s.host
   304  		records[i] = rec
   305  	}
   306  	s.stub.AddCall("Send", records)
   307  	s.activity <- "Send"
   308  	return errors.Trace(s.stub.NextErr())
   309  }
   310  
   311  func (s *stubSender) Close() error {
   312  	s.stub.AddCall("Close")
   313  	s.activity <- "Close"
   314  	return errors.Trace(s.stub.NextErr())
   315  }
   316  
   317  func (s *stubSender) waitForSend(c *gc.C) {
   318  	s.waitForActivity(c, "Send")
   319  }
   320  
   321  func (s *stubSender) waitForClose(c *gc.C) {
   322  	s.waitForActivity(c, "Close")
   323  }
   324  
   325  func (s *stubSender) waitForActivity(c *gc.C, name string) {
   326  	select {
   327  	case a := <-s.activity:
   328  		if a != name {
   329  			c.Fatalf("expected %v, got %v", name, a)
   330  		}
   331  	case <-time.After(coretesting.LongWait):
   332  		c.Fatalf("timeout out waiting for %v", name)
   333  	}
   334  }