github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/featuretests/dblog_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package featuretests 5 6 import ( 7 "time" 8 9 "github.com/juju/loggo" 10 jujutesting "github.com/juju/testing" 11 jc "github.com/juju/testing/checkers" 12 gc "gopkg.in/check.v1" 13 "gopkg.in/juju/names.v2" 14 "gopkg.in/mgo.v2/bson" 15 16 "github.com/juju/juju/agent" 17 "github.com/juju/juju/api/common" 18 agentcmd "github.com/juju/juju/cmd/jujud/agent" 19 "github.com/juju/juju/cmd/jujud/agent/agenttest" 20 "github.com/juju/juju/state" 21 coretesting "github.com/juju/juju/testing" 22 "github.com/juju/juju/testing/factory" 23 "github.com/juju/juju/version" 24 "github.com/juju/juju/worker/logsender" 25 "github.com/juju/juju/worker/peergrouper" 26 ) 27 28 // dblogSuite tests that logs flow correctly from the machine and unit 29 // agents over the API into MongoDB. These are very much integration 30 // tests with more detailed testing of the individual components 31 // being done in unit tests. 32 type dblogSuite struct { 33 agenttest.AgentSuite 34 } 35 36 func (s *dblogSuite) SetUpTest(c *gc.C) { 37 s.AgentSuite.SetUpTest(c) 38 } 39 40 func (s *dblogSuite) TestMachineAgentLogsGoToDB(c *gc.C) { 41 // Create a machine and an agent for it. 42 m, password := s.Factory.MakeMachineReturningPassword(c, &factory.MachineParams{ 43 Nonce: agent.BootstrapNonce, 44 }) 45 46 s.PrimeAgent(c, m.Tag(), password) 47 agentConf := agentcmd.NewAgentConf(s.DataDir()) 48 agentConf.ReadConfig(m.Tag().String()) 49 logger, err := logsender.InstallBufferedLogWriter(1000) 50 c.Assert(err, jc.ErrorIsNil) 51 machineAgentFactory := agentcmd.MachineAgentFactoryFn( 52 agentConf, 53 logger, 54 agentcmd.DefaultIntrospectionSocketName, 55 noPreUpgradeSteps, 56 c.MkDir(), 57 ) 58 a, err := machineAgentFactory(m.Id()) 59 c.Assert(err, jc.ErrorIsNil) 60 61 // Ensure there's no logs to begin with. 62 c.Assert(s.getLogCount(c, m.Tag()), gc.Equals, 0) 63 64 // Start the agent. 65 go func() { c.Check(a.Run(nil), jc.ErrorIsNil) }() 66 defer a.Stop() 67 68 foundLogs := s.waitForLogs(c, m.Tag()) 69 c.Assert(foundLogs, jc.IsTrue) 70 } 71 72 func (s *dblogSuite) TestUnitAgentLogsGoToDB(c *gc.C) { 73 // Create a unit and an agent for it. 74 u, password := s.Factory.MakeUnitReturningPassword(c, nil) 75 s.PrimeAgent(c, u.Tag(), password) 76 logger, err := logsender.InstallBufferedLogWriter(1000) 77 c.Assert(err, jc.ErrorIsNil) 78 a, err := agentcmd.NewUnitAgent(nil, logger) 79 c.Assert(err, jc.ErrorIsNil) 80 s.InitAgent(c, a, "--unit-name", u.Name(), "--log-to-stderr=true") 81 82 // Ensure there's no logs to begin with. 83 c.Assert(s.getLogCount(c, u.Tag()), gc.Equals, 0) 84 85 // Start the agent. 86 go func() { c.Assert(a.Run(nil), jc.ErrorIsNil) }() 87 defer a.Stop() 88 89 foundLogs := s.waitForLogs(c, u.Tag()) 90 c.Assert(foundLogs, jc.IsTrue) 91 } 92 93 func (s *dblogSuite) getLogCount(c *gc.C, entity names.Tag) int { 94 // TODO(mjs) - replace this with State's functionality for reading 95 // logs from the DB, once it gets this. This will happen before 96 // the DB logging feature branch is merged. 97 logs := s.Session.DB("logs").C("logs." + s.State.ModelUUID()) 98 count, err := logs.Find(bson.M{"n": entity.String()}).Count() 99 c.Assert(err, jc.ErrorIsNil) 100 return count 101 } 102 103 func (s *dblogSuite) waitForLogs(c *gc.C, entityTag names.Tag) bool { 104 for a := coretesting.LongAttempt.Start(); a.Next(); { 105 if s.getLogCount(c, entityTag) > 0 { 106 return true 107 } 108 } 109 return false 110 } 111 112 // debugLogDbSuite tests that the debuglog API works when logs are 113 // being read from the database. 114 // NOTE: the actual tests had to be split as the resetting causes 115 // mongo on bionic to have issues, see note below. 116 type debugLogDbSuite struct { 117 agenttest.AgentSuite 118 } 119 120 func (s *debugLogDbSuite) SetUpSuite(c *gc.C) { 121 // Restart mongod with the replicaset enabled. 122 mongod := jujutesting.MgoServer 123 mongod.Params = []string{"--replSet", "juju"} 124 mongod.Restart() 125 126 // Initiate the replicaset. 127 info := mongod.DialInfo() 128 args := peergrouper.InitiateMongoParams{ 129 DialInfo: info, 130 MemberHostPort: mongod.Addr(), 131 } 132 err := peergrouper.InitiateMongoServer(args) 133 c.Assert(err, jc.ErrorIsNil) 134 135 s.AgentSuite.SetUpSuite(c) 136 } 137 138 func (s *debugLogDbSuite) TearDownSuite(c *gc.C) { 139 // Restart mongod without the replicaset enabled so as not to 140 // affect other tests that rely on this mongod instance in this 141 // package. 142 mongod := jujutesting.MgoServer 143 mongod.Params = []string{} 144 mongod.Restart() 145 146 s.AgentSuite.TearDownSuite(c) 147 } 148 149 // NOTE: this is terrible, however due to a bug in mongod on bionic 150 // when resetting a mongo service with repl set on, we hit an inveriant bug 151 // which causes the second test to fail always. 152 153 // NOTE: do not merge with debugLogDbSuite2 154 type debugLogDbSuite1 struct { 155 debugLogDbSuite 156 } 157 158 func (s *debugLogDbSuite1) TestLogsAPI(c *gc.C) { 159 dbLogger := state.NewDbLogger(s.State) 160 defer dbLogger.Close() 161 162 t := time.Date(2015, 6, 23, 13, 8, 49, 0, time.UTC) 163 dbLogger.Log([]state.LogRecord{{ 164 Time: t, 165 Entity: names.NewMachineTag("99"), 166 Version: version.Current, 167 Module: "juju.foo", 168 Location: "code.go:42", 169 Level: loggo.INFO, 170 Message: "all is well", 171 }, { 172 Time: t.Add(time.Second), 173 Entity: names.NewMachineTag("99"), 174 Version: version.Current, 175 Module: "juju.bar", 176 Location: "go.go:99", 177 Level: loggo.ERROR, 178 Message: "no it isn't", 179 }}) 180 181 messages := make(chan common.LogMessage) 182 go func(numMessages int) { 183 client := s.APIState.Client() 184 logMessages, err := client.WatchDebugLog(common.DebugLogParams{}) 185 c.Assert(err, jc.ErrorIsNil) 186 187 for n := 0; n < numMessages; n++ { 188 messages <- <-logMessages 189 } 190 }(3) 191 192 assertMessage := func(expected common.LogMessage) { 193 select { 194 case actual := <-messages: 195 c.Assert(actual, jc.DeepEquals, expected) 196 case <-time.After(coretesting.LongWait): 197 c.Fatal("timed out waiting for log line") 198 } 199 } 200 201 // Read the 2 lines that are in the logs collection. 202 assertMessage(common.LogMessage{ 203 Entity: "machine-99", 204 Timestamp: t, 205 Severity: "INFO", 206 Module: "juju.foo", 207 Location: "code.go:42", 208 Message: "all is well", 209 }) 210 assertMessage(common.LogMessage{ 211 Entity: "machine-99", 212 Timestamp: t.Add(time.Second), 213 Severity: "ERROR", 214 Module: "juju.bar", 215 Location: "go.go:99", 216 Message: "no it isn't", 217 }) 218 219 // Now write and observe another log. This should be read from the oplog. 220 dbLogger.Log([]state.LogRecord{{ 221 Time: t.Add(2 * time.Second), 222 Entity: names.NewMachineTag("99"), 223 Version: version.Current, 224 Module: "ju.jitsu", 225 Location: "no.go:3", 226 Level: loggo.WARNING, 227 Message: "beep beep", 228 }}) 229 assertMessage(common.LogMessage{ 230 Entity: "machine-99", 231 Timestamp: t.Add(2 * time.Second), 232 Severity: "WARNING", 233 Module: "ju.jitsu", 234 Location: "no.go:3", 235 Message: "beep beep", 236 }) 237 } 238 239 // NOTE: do not merge with debugLogDbSuite1 240 type debugLogDbSuite2 struct { 241 debugLogDbSuite 242 } 243 244 func (s *debugLogDbSuite2) TestLogsUsesStartTime(c *gc.C) { 245 dbLogger := state.NewDbLogger(s.State) 246 defer dbLogger.Close() 247 248 entity := names.NewMachineTag("99") 249 version := version.Current 250 t1 := time.Date(2015, 6, 23, 13, 8, 49, 100, time.UTC) 251 // Check that start time has subsecond resolution. 252 t2 := time.Date(2015, 6, 23, 13, 8, 51, 50, time.UTC) 253 t3 := t1.Add(2 * time.Second) 254 t4 := t1.Add(4 * time.Second) 255 dbLogger.Log([]state.LogRecord{{ 256 Time: t1, 257 Entity: entity, 258 Version: version, 259 Module: "juju.foo", 260 Location: "code.go:42", 261 Level: loggo.INFO, 262 Message: "spinto band", 263 }, { 264 Time: t2, 265 Entity: entity, 266 Version: version, 267 Module: "juju.quux", 268 Location: "ok.go:101", 269 Level: loggo.INFO, 270 Message: "king gizzard and the lizard wizard", 271 }, { 272 Time: t3, 273 Entity: entity, 274 Version: version, 275 Module: "juju.bar", 276 Location: "go.go:99", 277 Level: loggo.ERROR, 278 Message: "born ruffians", 279 }, { 280 Time: t4, 281 Entity: entity, 282 Version: version, 283 Module: "juju.baz", 284 Location: "go.go.go:23", 285 Level: loggo.WARNING, 286 Message: "cold war kids", 287 }}) 288 289 client := s.APIState.Client() 290 logMessages, err := client.WatchDebugLog(common.DebugLogParams{ 291 StartTime: t3, 292 }) 293 c.Assert(err, jc.ErrorIsNil) 294 295 assertMessage := func(expected common.LogMessage) { 296 select { 297 case actual := <-logMessages: 298 c.Assert(actual, jc.DeepEquals, expected) 299 case <-time.After(coretesting.LongWait): 300 c.Fatal("timed out waiting for log line") 301 } 302 } 303 assertMessage(common.LogMessage{ 304 Entity: "machine-99", 305 Timestamp: t3, 306 Severity: "ERROR", 307 Module: "juju.bar", 308 Location: "go.go:99", 309 Message: "born ruffians", 310 }) 311 assertMessage(common.LogMessage{ 312 Entity: "machine-99", 313 Timestamp: t4, 314 Severity: "WARNING", 315 Module: "juju.baz", 316 Location: "go.go.go:23", 317 Message: "cold war kids", 318 }) 319 }