github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/worker/logsender/worker_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package logsender_test
     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/mgo.v2/bson"
    14  
    15  	"github.com/juju/juju/agent"
    16  	"github.com/juju/juju/api"
    17  	jujutesting "github.com/juju/juju/juju/testing"
    18  	"github.com/juju/juju/testing"
    19  	"github.com/juju/juju/testing/factory"
    20  	"github.com/juju/juju/worker/gate"
    21  	"github.com/juju/juju/worker/logsender"
    22  )
    23  
    24  type workerSuite struct {
    25  	jujutesting.JujuConnSuite
    26  	apiInfo *api.Info
    27  }
    28  
    29  var _ = gc.Suite(&workerSuite{})
    30  
    31  func (s *workerSuite) SetUpTest(c *gc.C) {
    32  	s.SetInitialFeatureFlags("db-log")
    33  	s.JujuConnSuite.SetUpTest(c)
    34  
    35  	// Create a machine for the client to log in as.
    36  	nonce := "some-nonce"
    37  	machine, password := s.Factory.MakeMachineReturningPassword(c,
    38  		&factory.MachineParams{Nonce: nonce})
    39  	s.apiInfo = s.APIInfo(c)
    40  	s.apiInfo.Tag = machine.Tag()
    41  	s.apiInfo.Password = password
    42  	s.apiInfo.Nonce = nonce
    43  }
    44  
    45  func (s *workerSuite) agent() agent.Agent {
    46  	return &mockAgent{apiInfo: s.apiInfo}
    47  }
    48  
    49  func (s *workerSuite) TestLockedGate(c *gc.C) {
    50  
    51  	// Set a bad password to induce an error if we connect.
    52  	s.apiInfo.Password = "lol-borken"
    53  
    54  	// Run a logsender worker.
    55  	logsCh := make(chan *logsender.LogRecord)
    56  	worker := logsender.New(logsCh, lockedGate{}, s.agent())
    57  
    58  	// At the end of the test, make sure we never tried to connect.
    59  	defer func() {
    60  		worker.Kill()
    61  		c.Check(worker.Wait(), jc.ErrorIsNil)
    62  	}()
    63  
    64  	// Give it a chance to ignore the gate and read the log channel.
    65  	select {
    66  	case <-time.After(testing.ShortWait):
    67  	case logsCh <- &logsender.LogRecord{}:
    68  		c.Fatalf("read log channel without waiting for gate")
    69  	}
    70  }
    71  
    72  func (s *workerSuite) TestLogSending(c *gc.C) {
    73  	const logCount = 5
    74  	logsCh := make(chan *logsender.LogRecord, logCount)
    75  
    76  	// Start the logsender worker.
    77  	worker := logsender.New(logsCh, gate.AlreadyUnlocked{}, s.agent())
    78  	defer func() {
    79  		worker.Kill()
    80  		c.Check(worker.Wait(), jc.ErrorIsNil)
    81  	}()
    82  
    83  	// Send some logs, also building up what should appear in the
    84  	// database.
    85  	var expectedDocs []bson.M
    86  	for i := 0; i < logCount; i++ {
    87  		ts := time.Now().Truncate(time.Millisecond)
    88  		location := fmt.Sprintf("loc%d", i)
    89  		message := fmt.Sprintf("%d", i)
    90  
    91  		logsCh <- &logsender.LogRecord{
    92  			Time:     ts,
    93  			Module:   "logsender-test",
    94  			Location: location,
    95  			Level:    loggo.INFO,
    96  			Message:  message,
    97  		}
    98  
    99  		expectedDocs = append(expectedDocs, bson.M{
   100  			"t": ts,
   101  			"e": s.State.EnvironUUID(),
   102  			"n": s.apiInfo.Tag.String(),
   103  			"m": "logsender-test",
   104  			"l": location,
   105  			"v": int(loggo.INFO),
   106  			"x": message,
   107  		})
   108  	}
   109  
   110  	// Wait for the logs to appear in the database.
   111  	var docs []bson.M
   112  	logsColl := s.State.MongoSession().DB("logs").C("logs")
   113  	for a := testing.LongAttempt.Start(); a.Next(); {
   114  		err := logsColl.Find(bson.M{"m": "logsender-test"}).All(&docs)
   115  		c.Assert(err, jc.ErrorIsNil)
   116  		if len(docs) == logCount {
   117  			break
   118  		}
   119  	}
   120  
   121  	// Check that the logs are correct.
   122  	c.Assert(docs, gc.HasLen, logCount)
   123  	for i := 0; i < logCount; i++ {
   124  		doc := docs[i]
   125  		delete(doc, "_id")
   126  		c.Assert(doc, gc.DeepEquals, expectedDocs[i])
   127  	}
   128  }
   129  
   130  func (s *workerSuite) TestDroppedLogs(c *gc.C) {
   131  	logsCh := make(logsender.LogRecordCh)
   132  
   133  	// Start the logsender worker.
   134  	worker := logsender.New(logsCh, gate.AlreadyUnlocked{}, s.agent())
   135  	defer func() {
   136  		worker.Kill()
   137  		c.Check(worker.Wait(), jc.ErrorIsNil)
   138  	}()
   139  
   140  	// Send a log record which indicates some messages after it were
   141  	// dropped.
   142  	ts := time.Now().Truncate(time.Millisecond)
   143  	logsCh <- &logsender.LogRecord{
   144  		Time:         ts,
   145  		Module:       "aaa",
   146  		Location:     "loc",
   147  		Level:        loggo.INFO,
   148  		Message:      "message0",
   149  		DroppedAfter: 42,
   150  	}
   151  
   152  	// Send another log record with no drops indicated.
   153  	logsCh <- &logsender.LogRecord{
   154  		Time:     time.Now(),
   155  		Module:   "zzz",
   156  		Location: "loc",
   157  		Level:    loggo.INFO,
   158  		Message:  "message1",
   159  	}
   160  
   161  	// Wait for the logs to appear in the database.
   162  	var docs []bson.M
   163  	logsColl := s.State.MongoSession().DB("logs").C("logs")
   164  	for a := testing.LongAttempt.Start(); a.Next(); {
   165  		if !a.HasNext() {
   166  			c.Fatal("timed out waiting for logs")
   167  		}
   168  		err := logsColl.Find(nil).Sort("m").All(&docs)
   169  		c.Assert(err, jc.ErrorIsNil)
   170  		// Expect the 2 messages sent along with a message about
   171  		// dropped messages.
   172  		if len(docs) == 3 {
   173  			break
   174  		}
   175  	}
   176  
   177  	// Check that the log records sent are present as well as an additional
   178  	// message in between indicating that some messages were dropped.
   179  	c.Assert(docs[0]["x"], gc.Equals, "message0")
   180  	delete(docs[1], "_id")
   181  	c.Assert(docs[1], gc.DeepEquals, bson.M{
   182  		"t": ts, // Should share timestamp with previous message.
   183  		"e": s.State.EnvironUUID(),
   184  		"n": s.apiInfo.Tag.String(),
   185  		"m": "juju.worker.logsender",
   186  		"l": "",
   187  		"v": int(loggo.WARNING),
   188  		"x": "42 log messages dropped due to lack of API connectivity",
   189  	})
   190  	c.Assert(docs[2]["x"], gc.Equals, "message1")
   191  }
   192  
   193  type mockAgent struct {
   194  	agent.Agent
   195  	agent.Config
   196  	apiInfo *api.Info
   197  }
   198  
   199  func (a *mockAgent) CurrentConfig() agent.Config {
   200  	return a
   201  }
   202  
   203  func (a *mockAgent) APIInfo() *api.Info {
   204  	return a.apiInfo
   205  }
   206  
   207  type lockedGate struct{}
   208  
   209  func (lockedGate) Unlocked() <-chan struct{} {
   210  	return nil
   211  }