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 }