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  }