github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/state/logs_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state_test
     5  
     6  import (
     7  	"math/rand"
     8  	"strconv"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/juju/loggo"
    13  	jc "github.com/juju/testing/checkers"
    14  	"github.com/juju/version"
    15  	gc "gopkg.in/check.v1"
    16  	"gopkg.in/juju/names.v2"
    17  	"gopkg.in/mgo.v2"
    18  	"gopkg.in/mgo.v2/bson"
    19  
    20  	"github.com/juju/juju/state"
    21  	coretesting "github.com/juju/juju/testing"
    22  	jujuversion "github.com/juju/juju/version"
    23  )
    24  
    25  type LogsSuite struct {
    26  	ConnSuite
    27  	logsColl *mgo.Collection
    28  }
    29  
    30  var _ = gc.Suite(&LogsSuite{})
    31  
    32  func (s *LogsSuite) SetUpTest(c *gc.C) {
    33  	s.ConnSuite.SetUpTest(c)
    34  
    35  	session := s.State.MongoSession()
    36  	s.logsColl = session.DB("logs").C("logs")
    37  }
    38  
    39  func (s *LogsSuite) TestLastSentLogTrackerSetGet(c *gc.C) {
    40  	tracker := state.NewLastSentLogTracker(s.State, s.State.ModelUUID(), "test-sink")
    41  	defer tracker.Close()
    42  
    43  	err := tracker.Set(10, 100)
    44  	c.Assert(err, jc.ErrorIsNil)
    45  	id1, ts1, err := tracker.Get()
    46  	c.Assert(err, jc.ErrorIsNil)
    47  	err = tracker.Set(20, 200)
    48  	c.Assert(err, jc.ErrorIsNil)
    49  	id2, ts2, err := tracker.Get()
    50  	c.Assert(err, jc.ErrorIsNil)
    51  
    52  	c.Check(id1, gc.Equals, int64(10))
    53  	c.Check(ts1, gc.Equals, int64(100))
    54  	c.Check(id2, gc.Equals, int64(20))
    55  	c.Check(ts2, gc.Equals, int64(200))
    56  }
    57  
    58  func (s *LogsSuite) TestLastSentLogTrackerGetNeverSet(c *gc.C) {
    59  	tracker := state.NewLastSentLogTracker(s.State, s.State.ModelUUID(), "test")
    60  	defer tracker.Close()
    61  
    62  	_, _, err := tracker.Get()
    63  
    64  	c.Check(err, gc.ErrorMatches, state.ErrNeverForwarded.Error())
    65  }
    66  
    67  func (s *LogsSuite) TestLastSentLogTrackerIndependentModels(c *gc.C) {
    68  	tracker0 := state.NewLastSentLogTracker(s.State, s.State.ModelUUID(), "test-sink")
    69  	defer tracker0.Close()
    70  	otherModel := s.NewStateForModelNamed(c, "test-model")
    71  	defer otherModel.Close()
    72  	tracker1 := state.NewLastSentLogTracker(otherModel, otherModel.ModelUUID(), "test-sink") // same sink
    73  	defer tracker1.Close()
    74  	err := tracker0.Set(10, 100)
    75  	c.Assert(err, jc.ErrorIsNil)
    76  	id0, ts0, err := tracker0.Get()
    77  	c.Assert(err, jc.ErrorIsNil)
    78  	c.Assert(id0, gc.Equals, int64(10))
    79  	c.Assert(ts0, gc.Equals, int64(100))
    80  
    81  	_, _, errBefore := tracker1.Get()
    82  	err = tracker1.Set(20, 200)
    83  	c.Assert(err, jc.ErrorIsNil)
    84  	id1, ts1, errAfter := tracker1.Get()
    85  	c.Assert(errAfter, jc.ErrorIsNil)
    86  	id0, ts0, err = tracker0.Get()
    87  	c.Assert(err, jc.ErrorIsNil)
    88  
    89  	c.Check(errBefore, gc.ErrorMatches, state.ErrNeverForwarded.Error())
    90  	c.Check(id1, gc.Equals, int64(20))
    91  	c.Check(ts1, gc.Equals, int64(200))
    92  	c.Check(id0, gc.Equals, int64(10))
    93  	c.Check(ts0, gc.Equals, int64(100))
    94  }
    95  
    96  func (s *LogsSuite) TestLastSentLogTrackerIndependentSinks(c *gc.C) {
    97  	tracker0 := state.NewLastSentLogTracker(s.State, s.State.ModelUUID(), "test-sink0")
    98  	defer tracker0.Close()
    99  	tracker1 := state.NewLastSentLogTracker(s.State, s.State.ModelUUID(), "test-sink1")
   100  	defer tracker1.Close()
   101  	err := tracker0.Set(10, 100)
   102  	c.Assert(err, jc.ErrorIsNil)
   103  	id0, ts0, err := tracker0.Get()
   104  	c.Assert(err, jc.ErrorIsNil)
   105  	c.Assert(id0, gc.Equals, int64(10))
   106  	c.Assert(ts0, gc.Equals, int64(100))
   107  
   108  	_, _, errBefore := tracker1.Get()
   109  	err = tracker1.Set(20, 200)
   110  	c.Assert(err, jc.ErrorIsNil)
   111  	id1, ts1, errAfter := tracker1.Get()
   112  	c.Assert(errAfter, jc.ErrorIsNil)
   113  	id0, ts0, err = tracker0.Get()
   114  	c.Assert(err, jc.ErrorIsNil)
   115  
   116  	c.Check(errBefore, gc.ErrorMatches, state.ErrNeverForwarded.Error())
   117  	c.Check(id1, gc.Equals, int64(20))
   118  	c.Check(ts1, gc.Equals, int64(200))
   119  	c.Check(id0, gc.Equals, int64(10))
   120  	c.Check(ts0, gc.Equals, int64(100))
   121  }
   122  
   123  func (s *LogsSuite) TestAllLastSentLogTrackerSetGet(c *gc.C) {
   124  	tracker, err := state.NewAllLastSentLogTracker(s.State, "test-sink")
   125  	c.Assert(err, jc.ErrorIsNil)
   126  	defer tracker.Close()
   127  
   128  	err = tracker.Set(10, 100)
   129  	c.Assert(err, jc.ErrorIsNil)
   130  	id1, ts1, err := tracker.Get()
   131  	c.Assert(err, jc.ErrorIsNil)
   132  	err = tracker.Set(20, 200)
   133  	c.Assert(err, jc.ErrorIsNil)
   134  	id2, ts2, err := tracker.Get()
   135  	c.Assert(err, jc.ErrorIsNil)
   136  
   137  	c.Check(id1, gc.Equals, int64(10))
   138  	c.Check(ts1, gc.Equals, int64(100))
   139  	c.Check(id2, gc.Equals, int64(20))
   140  	c.Check(ts2, gc.Equals, int64(200))
   141  }
   142  
   143  func (s *LogsSuite) TestAllLastSentLogTrackerNotController(c *gc.C) {
   144  	st := s.NewStateForModelNamed(c, "test-model")
   145  	defer st.Close()
   146  
   147  	_, err := state.NewAllLastSentLogTracker(st, "test")
   148  
   149  	c.Check(err, gc.ErrorMatches, `only the admin model can track all log records`)
   150  }
   151  
   152  func (s *LogsSuite) TestIndexesCreated(c *gc.C) {
   153  	// Indexes should be created on the logs collection when state is opened.
   154  	indexes, err := s.logsColl.Indexes()
   155  	c.Assert(err, jc.ErrorIsNil)
   156  	var keys []string
   157  	for _, index := range indexes {
   158  		keys = append(keys, strings.Join(index.Key, "-"))
   159  	}
   160  	c.Assert(keys, jc.SameContents, []string{
   161  		"_id", // default index
   162  		"e-t", // model-uuid and timestamp
   163  		"e-n", // model-uuid and entity
   164  	})
   165  }
   166  
   167  func (s *LogsSuite) TestDbLogger(c *gc.C) {
   168  	logger := state.NewDbLogger(s.State, names.NewMachineTag("22"), jujuversion.Current)
   169  	defer logger.Close()
   170  	t0 := coretesting.ZeroTime().Truncate(time.Millisecond) // MongoDB only stores timestamps with ms precision.
   171  	logger.Log(t0, "some.where", "foo.go:99", loggo.INFO, "all is well")
   172  	t1 := t0.Add(time.Second)
   173  	logger.Log(t1, "else.where", "bar.go:42", loggo.ERROR, "oh noes")
   174  
   175  	var docs []bson.M
   176  	err := s.logsColl.Find(nil).Sort("t").All(&docs)
   177  	c.Assert(err, jc.ErrorIsNil)
   178  	c.Assert(docs, gc.HasLen, 2)
   179  
   180  	c.Assert(docs[0]["t"], gc.Equals, t0.UnixNano())
   181  	c.Assert(docs[0]["e"], gc.Equals, s.State.ModelUUID())
   182  	c.Assert(docs[0]["n"], gc.Equals, "machine-22")
   183  	c.Assert(docs[0]["m"], gc.Equals, "some.where")
   184  	c.Assert(docs[0]["l"], gc.Equals, "foo.go:99")
   185  	c.Assert(docs[0]["v"], gc.Equals, int(loggo.INFO))
   186  	c.Assert(docs[0]["x"], gc.Equals, "all is well")
   187  
   188  	c.Assert(docs[1]["t"], gc.Equals, t1.UnixNano())
   189  	c.Assert(docs[1]["e"], gc.Equals, s.State.ModelUUID())
   190  	c.Assert(docs[1]["n"], gc.Equals, "machine-22")
   191  	c.Assert(docs[1]["m"], gc.Equals, "else.where")
   192  	c.Assert(docs[1]["l"], gc.Equals, "bar.go:42")
   193  	c.Assert(docs[1]["v"], gc.Equals, int(loggo.ERROR))
   194  	c.Assert(docs[1]["x"], gc.Equals, "oh noes")
   195  }
   196  
   197  func (s *LogsSuite) TestPruneLogsByTime(c *gc.C) {
   198  	dbLogger := state.NewDbLogger(s.State, names.NewMachineTag("22"), jujuversion.Current)
   199  	defer dbLogger.Close()
   200  	log := func(t time.Time, msg string) {
   201  		err := dbLogger.Log(t, "module", "loc", loggo.INFO, msg)
   202  		c.Assert(err, jc.ErrorIsNil)
   203  	}
   204  
   205  	now := coretesting.NonZeroTime()
   206  	maxLogTime := now.Add(-time.Minute)
   207  	log(now, "keep")
   208  	log(maxLogTime.Add(time.Second), "keep")
   209  	log(maxLogTime, "keep")
   210  	log(maxLogTime.Add(-time.Second), "prune")
   211  	log(maxLogTime.Add(-(2 * time.Second)), "prune")
   212  
   213  	noPruneMB := 100
   214  	err := state.PruneLogs(s.State, maxLogTime, noPruneMB)
   215  	c.Assert(err, jc.ErrorIsNil)
   216  
   217  	// After pruning there should just be 3 "keep" messages left.
   218  	var docs []bson.M
   219  	err = s.logsColl.Find(nil).All(&docs)
   220  	c.Assert(err, jc.ErrorIsNil)
   221  	c.Assert(docs, gc.HasLen, 3)
   222  	for _, doc := range docs {
   223  		c.Assert(doc["x"], gc.Equals, "keep")
   224  	}
   225  }
   226  
   227  func (s *LogsSuite) TestPruneLogsBySize(c *gc.C) {
   228  	// Set up 3 models and generate different amounts of logs
   229  	// for them.
   230  	now := coretesting.NonZeroTime().Truncate(time.Millisecond)
   231  
   232  	s0 := s.State
   233  	startingLogsS0 := 10
   234  	s.generateLogs(c, s0, now, startingLogsS0)
   235  
   236  	s1 := s.Factory.MakeModel(c, nil)
   237  	defer s1.Close()
   238  	startingLogsS1 := 10000
   239  	s.generateLogs(c, s1, now, startingLogsS1)
   240  
   241  	s2 := s.Factory.MakeModel(c, nil)
   242  	defer s2.Close()
   243  	startingLogsS2 := 12000
   244  	s.generateLogs(c, s2, now, startingLogsS2)
   245  
   246  	// Prune logs collection back to 1 MiB.
   247  	tsNoPrune := coretesting.NonZeroTime().Add(-3 * 24 * time.Hour)
   248  	err := state.PruneLogs(s.State, tsNoPrune, 1)
   249  	c.Assert(err, jc.ErrorIsNil)
   250  
   251  	// Logs for first env should not be touched.
   252  	c.Assert(s.countLogs(c, s0), gc.Equals, startingLogsS0)
   253  
   254  	// Logs for second env should be pruned.
   255  	c.Assert(s.countLogs(c, s1), jc.LessThan, startingLogsS1)
   256  
   257  	// Logs for third env should be pruned to a similar level as
   258  	// second env.
   259  	c.Assert(s.countLogs(c, s2), jc.LessThan, startingLogsS1)
   260  
   261  	// Ensure that the latest log records are still there.
   262  	assertLatestTs := func(st *state.State) {
   263  		var doc bson.M
   264  		err := s.logsColl.Find(bson.M{"e": st.ModelUUID()}).Sort("-t").One(&doc)
   265  		c.Assert(err, jc.ErrorIsNil)
   266  		c.Assert(doc["t"], gc.Equals, now.UnixNano())
   267  	}
   268  	assertLatestTs(s0)
   269  	assertLatestTs(s1)
   270  	assertLatestTs(s2)
   271  }
   272  
   273  func (s *LogsSuite) generateLogs(c *gc.C, st *state.State, endTime time.Time, count int) {
   274  	dbLogger := state.NewDbLogger(st, names.NewMachineTag("0"), jujuversion.Current)
   275  	defer dbLogger.Close()
   276  	for i := 0; i < count; i++ {
   277  		ts := endTime.Add(-time.Duration(i) * time.Second)
   278  		err := dbLogger.Log(ts, "module", "loc", loggo.INFO, "message")
   279  		c.Assert(err, jc.ErrorIsNil)
   280  	}
   281  }
   282  
   283  func (s *LogsSuite) countLogs(c *gc.C, st *state.State) int {
   284  	count, err := s.logsColl.Find(bson.M{"e": st.ModelUUID()}).Count()
   285  	c.Assert(err, jc.ErrorIsNil)
   286  	return count
   287  }
   288  
   289  type LogTailerSuite struct {
   290  	ConnWithWallClockSuite
   291  	logsColl   *mgo.Collection
   292  	oplogColl  *mgo.Collection
   293  	otherState *state.State
   294  }
   295  
   296  var _ = gc.Suite(&LogTailerSuite{})
   297  
   298  func (s *LogTailerSuite) SetUpTest(c *gc.C) {
   299  	s.ConnWithWallClockSuite.SetUpTest(c)
   300  
   301  	session := s.State.MongoSession()
   302  	s.logsColl = session.DB("logs").C("logs")
   303  
   304  	// Create a fake oplog collection.
   305  	s.oplogColl = session.DB("logs").C("oplog.fake")
   306  	err := s.oplogColl.Create(&mgo.CollectionInfo{
   307  		Capped:   true,
   308  		MaxBytes: 1024 * 1024,
   309  	})
   310  	c.Assert(err, jc.ErrorIsNil)
   311  	s.AddCleanup(func(*gc.C) { s.oplogColl.DropCollection() })
   312  
   313  	s.otherState = s.NewStateForModelNamed(c, "test-model")
   314  	c.Assert(s.otherState, gc.NotNil)
   315  	s.AddCleanup(func(c *gc.C) {
   316  		err := s.otherState.Close()
   317  		c.Assert(err, jc.ErrorIsNil)
   318  	})
   319  }
   320  
   321  func (s *LogTailerSuite) TestTimeFiltering(c *gc.C) {
   322  	// Add 10 logs that shouldn't be returned.
   323  	threshT := coretesting.NonZeroTime()
   324  	s.writeLogsT(c,
   325  		threshT.Add(-5*time.Second), threshT.Add(-time.Millisecond), 5,
   326  		logTemplate{Message: "dont want"},
   327  	)
   328  
   329  	// Add 5 logs that should be returned.
   330  	want := logTemplate{Message: "want"}
   331  	s.writeLogsT(c, threshT, threshT.Add(5*time.Second), 5, want)
   332  	tailer, err := state.NewLogTailer(s.otherState, &state.LogTailerParams{
   333  		StartTime: threshT,
   334  		Oplog:     s.oplogColl,
   335  	})
   336  	c.Assert(err, jc.ErrorIsNil)
   337  	defer tailer.Stop()
   338  	s.assertTailer(c, tailer, 5, want)
   339  
   340  	// Write more logs. These will be read from the the oplog.
   341  	want2 := logTemplate{Message: "want 2"}
   342  	s.writeLogsT(c, threshT.Add(6*time.Second), threshT.Add(10*time.Second), 5, want2)
   343  	s.assertTailer(c, tailer, 5, want2)
   344  
   345  }
   346  
   347  func (s *LogTailerSuite) TestOplogTransition(c *gc.C) {
   348  	// Ensure that logs aren't repeated as the log tailer moves from
   349  	// reading from the logs collection to tailing the oplog.
   350  	//
   351  	// All logs are written out with the same timestamp to create a
   352  	// challenging scenario for the tailer.
   353  
   354  	for i := 0; i < 5; i++ {
   355  		s.writeLogs(c, 1, logTemplate{Message: strconv.Itoa(i)})
   356  	}
   357  
   358  	tailer, err := state.NewLogTailer(s.otherState, &state.LogTailerParams{
   359  		Oplog: s.oplogColl,
   360  	})
   361  	c.Assert(err, jc.ErrorIsNil)
   362  	defer tailer.Stop()
   363  	for i := 0; i < 5; i++ {
   364  		s.assertTailer(c, tailer, 1, logTemplate{Message: strconv.Itoa(i)})
   365  	}
   366  
   367  	// Write more logs. These will be read from the the oplog.
   368  	for i := 5; i < 10; i++ {
   369  		lt := logTemplate{Message: strconv.Itoa(i)}
   370  		s.writeLogs(c, 2, lt)
   371  		s.assertTailer(c, tailer, 2, lt)
   372  	}
   373  }
   374  
   375  func (s *LogTailerSuite) TestModelFiltering(c *gc.C) {
   376  	good := logTemplate{Message: "good"}
   377  	writeLogs := func() {
   378  		s.writeLogs(c, 1, logTemplate{
   379  			ModelUUID: "someuuid0",
   380  			Message:   "bad",
   381  		})
   382  		s.writeLogs(c, 1, logTemplate{
   383  			ModelUUID: "someuuid1",
   384  			Message:   "bad",
   385  		})
   386  		s.writeLogs(c, 1, good)
   387  	}
   388  
   389  	assert := func(tailer state.LogTailer) {
   390  		// Only the entries the s.State's UUID should be reported.
   391  		s.assertTailer(c, tailer, 1, good)
   392  	}
   393  
   394  	s.checkLogTailerFiltering(c, s.otherState, &state.LogTailerParams{}, writeLogs, assert)
   395  }
   396  
   397  func (s *LogTailerSuite) TestTailingLogsForAllModels(c *gc.C) {
   398  	writeLogs := func() {
   399  		s.writeLogs(c, 1, logTemplate{
   400  			ModelUUID: "someuuid0",
   401  			Message:   "bad",
   402  		})
   403  		s.writeLogs(c, 1, logTemplate{
   404  			ModelUUID: "someuuid1",
   405  			Message:   "bad",
   406  		})
   407  		s.writeLogs(c, 1, logTemplate{Message: "good"})
   408  	}
   409  
   410  	assert := func(tailer state.LogTailer) {
   411  		messagesFrom := map[string]bool{
   412  			s.otherState.ModelUUID(): false,
   413  			"someuuid0":              false,
   414  			"someuuid1":              false,
   415  		}
   416  		defer func() {
   417  			c.Assert(messagesFrom, gc.HasLen, 3)
   418  			for _, v := range messagesFrom {
   419  				c.Assert(v, jc.IsTrue)
   420  			}
   421  		}()
   422  		count := 0
   423  		for {
   424  			select {
   425  			case log := <-tailer.Logs():
   426  				messagesFrom[log.ModelUUID] = true
   427  				count++
   428  				c.Logf("count %d", count)
   429  				if count >= 3 {
   430  					return
   431  				}
   432  			case <-time.After(coretesting.ShortWait):
   433  				c.Fatalf("timeout waiting for logs %d", count)
   434  			}
   435  		}
   436  	}
   437  	s.checkLogTailerFiltering(c, s.State, &state.LogTailerParams{AllModels: true}, writeLogs, assert)
   438  }
   439  
   440  func (s *LogTailerSuite) TestTailingLogsOnlyForControllerModel(c *gc.C) {
   441  	writeLogs := func() {
   442  		s.writeLogs(c, 1, logTemplate{
   443  			ModelUUID: s.otherState.ModelUUID(),
   444  			Message:   "bad"},
   445  		)
   446  		s.writeLogs(c, 1, logTemplate{
   447  			ModelUUID: s.State.ModelUUID(),
   448  			Message:   "good1",
   449  		})
   450  		s.writeLogs(c, 1, logTemplate{
   451  			ModelUUID: s.State.ModelUUID(),
   452  			Message:   "good2",
   453  		})
   454  	}
   455  
   456  	assert := func(tailer state.LogTailer) {
   457  		messages := map[string]bool{}
   458  		defer func() {
   459  			c.Assert(messages, gc.HasLen, 2)
   460  			for m, _ := range messages {
   461  				if m != "good1" && m != "good2" {
   462  					c.Fatalf("received message: %v", m)
   463  				}
   464  			}
   465  		}()
   466  		count := 0
   467  		for {
   468  			select {
   469  			case log := <-tailer.Logs():
   470  				c.Assert(log.ModelUUID, gc.Equals, s.State.ModelUUID())
   471  				messages[log.Message] = true
   472  				count++
   473  				c.Logf("count %d", count)
   474  				if count >= 2 {
   475  					return
   476  				}
   477  			case <-time.After(coretesting.ShortWait):
   478  				c.Fatalf("timeout waiting for logs %d", count)
   479  			}
   480  		}
   481  	}
   482  	s.checkLogTailerFiltering(c, s.State, &state.LogTailerParams{}, writeLogs, assert)
   483  }
   484  
   485  func (s *LogTailerSuite) TestTailingAllLogsFromNonController(c *gc.C) {
   486  	_, err := state.NewLogTailer(s.otherState, &state.LogTailerParams{AllModels: true})
   487  	c.Assert(err, gc.ErrorMatches, "not allowed to tail logs from all models: not a controller")
   488  }
   489  
   490  func (s *LogTailerSuite) TestLevelFiltering(c *gc.C) {
   491  	info := logTemplate{Level: loggo.INFO}
   492  	error := logTemplate{Level: loggo.ERROR}
   493  	writeLogs := func() {
   494  		s.writeLogs(c, 1, logTemplate{Level: loggo.DEBUG})
   495  		s.writeLogs(c, 1, info)
   496  		s.writeLogs(c, 1, error)
   497  	}
   498  	params := &state.LogTailerParams{
   499  		MinLevel: loggo.INFO,
   500  	}
   501  	assert := func(tailer state.LogTailer) {
   502  		s.assertTailer(c, tailer, 1, info)
   503  		s.assertTailer(c, tailer, 1, error)
   504  	}
   505  	s.checkLogTailerFiltering(c, s.otherState, params, writeLogs, assert)
   506  }
   507  
   508  func (s *LogTailerSuite) TestInitialLines(c *gc.C) {
   509  	expected := logTemplate{Message: "want"}
   510  	s.writeLogs(c, 3, logTemplate{Message: "dont want"})
   511  	s.writeLogs(c, 5, expected)
   512  
   513  	tailer, err := state.NewLogTailer(s.otherState, &state.LogTailerParams{
   514  		InitialLines: 5,
   515  	})
   516  	c.Assert(err, jc.ErrorIsNil)
   517  	defer tailer.Stop()
   518  
   519  	// Should see just the last 5 lines as requested.
   520  	s.assertTailer(c, tailer, 5, expected)
   521  }
   522  
   523  func (s *LogTailerSuite) TestInitialLinesWithNotEnoughLines(c *gc.C) {
   524  	expected := logTemplate{Message: "want"}
   525  	s.writeLogs(c, 2, expected)
   526  
   527  	tailer, err := state.NewLogTailer(s.otherState, &state.LogTailerParams{
   528  		InitialLines: 5,
   529  	})
   530  	c.Assert(err, jc.ErrorIsNil)
   531  	defer tailer.Stop()
   532  
   533  	// Should see just the 2 lines that existed, even though 5 were
   534  	// asked for.
   535  	s.assertTailer(c, tailer, 2, expected)
   536  }
   537  
   538  func (s *LogTailerSuite) TestNoTail(c *gc.C) {
   539  	expected := logTemplate{Message: "want"}
   540  	s.writeLogs(c, 2, expected)
   541  
   542  	// Write a log entry that's only in the oplog.
   543  	doc := s.logTemplateToDoc(logTemplate{Message: "dont want"}, coretesting.ZeroTime())
   544  	err := s.writeLogToOplog(doc)
   545  	c.Assert(err, jc.ErrorIsNil)
   546  
   547  	tailer, err := state.NewLogTailer(s.otherState, &state.LogTailerParams{
   548  		NoTail: true,
   549  	})
   550  	c.Assert(err, jc.ErrorIsNil)
   551  	// Not strictly necessary, just in case NoTail doesn't work in the test.
   552  	defer tailer.Stop()
   553  
   554  	// Logs only in the oplog shouldn't be reported and the tailer
   555  	// should stop itself once the log collection has been read.
   556  	s.assertTailer(c, tailer, 2, expected)
   557  	select {
   558  	case _, ok := <-tailer.Logs():
   559  		if ok {
   560  			c.Fatal("shouldn't be any further logs")
   561  		}
   562  	case <-time.After(coretesting.LongWait):
   563  		c.Fatal("timed out waiting for logs channel to close")
   564  	}
   565  
   566  	select {
   567  	case <-tailer.Dying():
   568  		// Success.
   569  	case <-time.After(coretesting.LongWait):
   570  		c.Fatal("tailer didn't stop itself")
   571  	}
   572  }
   573  
   574  func (s *LogTailerSuite) TestIncludeEntity(c *gc.C) {
   575  	machine0 := logTemplate{Entity: names.NewMachineTag("0")}
   576  	foo0 := logTemplate{Entity: names.NewUnitTag("foo/0")}
   577  	foo1 := logTemplate{Entity: names.NewUnitTag("foo/1")}
   578  	writeLogs := func() {
   579  		s.writeLogs(c, 3, machine0)
   580  		s.writeLogs(c, 2, foo0)
   581  		s.writeLogs(c, 1, foo1)
   582  		s.writeLogs(c, 3, machine0)
   583  	}
   584  	params := &state.LogTailerParams{
   585  		IncludeEntity: []string{
   586  			"unit-foo-0",
   587  			"unit-foo-1",
   588  		},
   589  	}
   590  	assert := func(tailer state.LogTailer) {
   591  		s.assertTailer(c, tailer, 2, foo0)
   592  		s.assertTailer(c, tailer, 1, foo1)
   593  	}
   594  	s.checkLogTailerFiltering(c, s.otherState, params, writeLogs, assert)
   595  }
   596  
   597  func (s *LogTailerSuite) TestIncludeEntityWildcard(c *gc.C) {
   598  	machine0 := logTemplate{Entity: names.NewMachineTag("0")}
   599  	foo0 := logTemplate{Entity: names.NewUnitTag("foo/0")}
   600  	foo1 := logTemplate{Entity: names.NewUnitTag("foo/1")}
   601  	writeLogs := func() {
   602  		s.writeLogs(c, 3, machine0)
   603  		s.writeLogs(c, 2, foo0)
   604  		s.writeLogs(c, 1, foo1)
   605  		s.writeLogs(c, 3, machine0)
   606  	}
   607  	params := &state.LogTailerParams{
   608  		IncludeEntity: []string{
   609  			"unit-foo*",
   610  		},
   611  	}
   612  	assert := func(tailer state.LogTailer) {
   613  		s.assertTailer(c, tailer, 2, foo0)
   614  		s.assertTailer(c, tailer, 1, foo1)
   615  	}
   616  	s.checkLogTailerFiltering(c, s.otherState, params, writeLogs, assert)
   617  }
   618  
   619  func (s *LogTailerSuite) TestExcludeEntity(c *gc.C) {
   620  	machine0 := logTemplate{Entity: names.NewMachineTag("0")}
   621  	foo0 := logTemplate{Entity: names.NewUnitTag("foo/0")}
   622  	foo1 := logTemplate{Entity: names.NewUnitTag("foo/1")}
   623  	writeLogs := func() {
   624  		s.writeLogs(c, 3, machine0)
   625  		s.writeLogs(c, 2, foo0)
   626  		s.writeLogs(c, 1, foo1)
   627  		s.writeLogs(c, 3, machine0)
   628  	}
   629  	params := &state.LogTailerParams{
   630  		ExcludeEntity: []string{
   631  			"machine-0",
   632  			"unit-foo-0",
   633  		},
   634  	}
   635  	assert := func(tailer state.LogTailer) {
   636  		s.assertTailer(c, tailer, 1, foo1)
   637  	}
   638  	s.checkLogTailerFiltering(c, s.otherState, params, writeLogs, assert)
   639  }
   640  
   641  func (s *LogTailerSuite) TestExcludeEntityWildcard(c *gc.C) {
   642  	machine0 := logTemplate{Entity: names.NewMachineTag("0")}
   643  	foo0 := logTemplate{Entity: names.NewUnitTag("foo/0")}
   644  	foo1 := logTemplate{Entity: names.NewUnitTag("foo/1")}
   645  	writeLogs := func() {
   646  		s.writeLogs(c, 3, machine0)
   647  		s.writeLogs(c, 2, foo0)
   648  		s.writeLogs(c, 1, foo1)
   649  		s.writeLogs(c, 3, machine0)
   650  	}
   651  	params := &state.LogTailerParams{
   652  		ExcludeEntity: []string{
   653  			"machine*",
   654  			"unit-*-0",
   655  		},
   656  	}
   657  	assert := func(tailer state.LogTailer) {
   658  		s.assertTailer(c, tailer, 1, foo1)
   659  	}
   660  	s.checkLogTailerFiltering(c, s.otherState, params, writeLogs, assert)
   661  }
   662  func (s *LogTailerSuite) TestIncludeModule(c *gc.C) {
   663  	mod0 := logTemplate{Module: "foo.bar"}
   664  	mod1 := logTemplate{Module: "juju.thing"}
   665  	subMod1 := logTemplate{Module: "juju.thing.hai"}
   666  	mod2 := logTemplate{Module: "elsewhere"}
   667  	writeLogs := func() {
   668  		s.writeLogs(c, 1, mod0)
   669  		s.writeLogs(c, 1, mod1)
   670  		s.writeLogs(c, 1, mod0)
   671  		s.writeLogs(c, 1, subMod1)
   672  		s.writeLogs(c, 1, mod0)
   673  		s.writeLogs(c, 1, mod2)
   674  	}
   675  	params := &state.LogTailerParams{
   676  		IncludeModule: []string{"juju.thing", "elsewhere"},
   677  	}
   678  	assert := func(tailer state.LogTailer) {
   679  		s.assertTailer(c, tailer, 1, mod1)
   680  		s.assertTailer(c, tailer, 1, subMod1)
   681  		s.assertTailer(c, tailer, 1, mod2)
   682  	}
   683  	s.checkLogTailerFiltering(c, s.otherState, params, writeLogs, assert)
   684  }
   685  
   686  func (s *LogTailerSuite) TestExcludeModule(c *gc.C) {
   687  	mod0 := logTemplate{Module: "foo.bar"}
   688  	mod1 := logTemplate{Module: "juju.thing"}
   689  	subMod1 := logTemplate{Module: "juju.thing.hai"}
   690  	mod2 := logTemplate{Module: "elsewhere"}
   691  	writeLogs := func() {
   692  		s.writeLogs(c, 1, mod0)
   693  		s.writeLogs(c, 1, mod1)
   694  		s.writeLogs(c, 1, mod0)
   695  		s.writeLogs(c, 1, subMod1)
   696  		s.writeLogs(c, 1, mod0)
   697  		s.writeLogs(c, 1, mod2)
   698  	}
   699  	params := &state.LogTailerParams{
   700  		ExcludeModule: []string{"juju.thing", "elsewhere"},
   701  	}
   702  	assert := func(tailer state.LogTailer) {
   703  		s.assertTailer(c, tailer, 2, mod0)
   704  	}
   705  	s.checkLogTailerFiltering(c, s.otherState, params, writeLogs, assert)
   706  }
   707  
   708  func (s *LogTailerSuite) TestIncludeExcludeModule(c *gc.C) {
   709  	foo := logTemplate{Module: "foo"}
   710  	bar := logTemplate{Module: "bar"}
   711  	barSub := logTemplate{Module: "bar.thing"}
   712  	baz := logTemplate{Module: "baz"}
   713  	qux := logTemplate{Module: "qux"}
   714  	writeLogs := func() {
   715  		s.writeLogs(c, 1, foo)
   716  		s.writeLogs(c, 1, bar)
   717  		s.writeLogs(c, 1, barSub)
   718  		s.writeLogs(c, 1, baz)
   719  		s.writeLogs(c, 1, qux)
   720  	}
   721  	params := &state.LogTailerParams{
   722  		IncludeModule: []string{"foo", "bar", "qux"},
   723  		ExcludeModule: []string{"foo", "bar"},
   724  	}
   725  	assert := func(tailer state.LogTailer) {
   726  		// Except just "qux" because "foo" and "bar" were included and
   727  		// then excluded.
   728  		s.assertTailer(c, tailer, 1, qux)
   729  	}
   730  	s.checkLogTailerFiltering(c, s.otherState, params, writeLogs, assert)
   731  }
   732  
   733  func (s *LogTailerSuite) checkLogTailerFiltering(
   734  	c *gc.C,
   735  	st *state.State,
   736  	params *state.LogTailerParams,
   737  	writeLogs func(),
   738  	assertTailer func(state.LogTailer),
   739  ) {
   740  	// Check the tailer does the right thing when reading from the
   741  	// logs collection.
   742  	writeLogs()
   743  	params.Oplog = s.oplogColl
   744  	tailer, err := state.NewLogTailer(st, params)
   745  	c.Assert(err, jc.ErrorIsNil)
   746  	defer tailer.Stop()
   747  	assertTailer(tailer)
   748  
   749  	// Now write out logs and check the tailer again. These will be
   750  	// read from the oplog.
   751  	writeLogs()
   752  	assertTailer(tailer)
   753  }
   754  
   755  type logTemplate struct {
   756  	ModelUUID string
   757  	Entity    names.Tag
   758  	Version   version.Number
   759  	Module    string
   760  	Location  string
   761  	Level     loggo.Level
   762  	Message   string
   763  }
   764  
   765  // writeLogs creates count log messages at the current time using
   766  // the supplied template. As well as writing to the logs collection,
   767  // entries are also made into the fake oplog collection.
   768  func (s *LogTailerSuite) writeLogs(c *gc.C, count int, lt logTemplate) {
   769  	t := coretesting.ZeroTime()
   770  	s.writeLogsT(c, t, t, count, lt)
   771  }
   772  
   773  // writeLogsT creates count log messages between startTime and
   774  // endTime using the supplied template. As well as writing to the logs
   775  // collection, entries are also made into the fake oplog collection.
   776  func (s *LogTailerSuite) writeLogsT(c *gc.C, startTime, endTime time.Time, count int, lt logTemplate) {
   777  	interval := endTime.Sub(startTime) / time.Duration(count)
   778  	t := startTime
   779  	for i := 0; i < count; i++ {
   780  		doc := s.logTemplateToDoc(lt, t)
   781  		err := s.writeLogToOplog(doc)
   782  		c.Assert(err, jc.ErrorIsNil)
   783  		err = s.logsColl.Insert(doc)
   784  		c.Assert(err, jc.ErrorIsNil)
   785  		t = t.Add(interval)
   786  	}
   787  }
   788  
   789  // writeLogToOplog writes out a log record to the a (probably fake)
   790  // oplog collection.
   791  func (s *LogTailerSuite) writeLogToOplog(doc interface{}) error {
   792  	return s.oplogColl.Insert(bson.D{
   793  		{"ts", bson.MongoTimestamp(coretesting.ZeroTime().Unix() << 32)}, // an approximation which will do
   794  		{"h", rand.Int63()},                                              // again, a suitable fake
   795  		{"op", "i"},                                                      // this will always be an insert
   796  		{"ns", "logs.logs"},
   797  		{"o", doc},
   798  	})
   799  }
   800  
   801  func (s *LogTailerSuite) normaliseLogTemplate(lt *logTemplate) {
   802  	if lt.ModelUUID == "" {
   803  		lt.ModelUUID = s.otherState.ModelUUID()
   804  	}
   805  	if lt.Entity == nil {
   806  		lt.Entity = names.NewMachineTag("0")
   807  	}
   808  	if lt.Version == version.Zero {
   809  		lt.Version = jujuversion.Current
   810  	}
   811  	if lt.Module == "" {
   812  		lt.Module = "module"
   813  	}
   814  	if lt.Location == "" {
   815  		lt.Location = "loc"
   816  	}
   817  	if lt.Level == loggo.UNSPECIFIED {
   818  		lt.Level = loggo.INFO
   819  	}
   820  	if lt.Message == "" {
   821  		lt.Message = "message"
   822  	}
   823  }
   824  
   825  func (s *LogTailerSuite) logTemplateToDoc(lt logTemplate, t time.Time) interface{} {
   826  	s.normaliseLogTemplate(&lt)
   827  	return state.MakeLogDoc(
   828  		lt.ModelUUID,
   829  		lt.Entity,
   830  		t,
   831  		lt.Module,
   832  		lt.Location,
   833  		lt.Level,
   834  		lt.Message,
   835  	)
   836  }
   837  
   838  func (s *LogTailerSuite) assertTailer(c *gc.C, tailer state.LogTailer, expectedCount int, lt logTemplate) {
   839  	s.normaliseLogTemplate(&lt)
   840  
   841  	timeout := time.After(coretesting.LongWait)
   842  	count := 0
   843  	for {
   844  		select {
   845  		case log, ok := <-tailer.Logs():
   846  			if !ok {
   847  				c.Fatalf("tailer died unexpectedly: %v", tailer.Err())
   848  			}
   849  			c.Assert(log.Version, gc.Equals, lt.Version)
   850  			c.Assert(log.Entity, gc.Equals, lt.Entity)
   851  			c.Assert(log.Module, gc.Equals, lt.Module)
   852  			c.Assert(log.Location, gc.Equals, lt.Location)
   853  			c.Assert(log.Level, gc.Equals, lt.Level)
   854  			c.Assert(log.Message, gc.Equals, lt.Message)
   855  			c.Assert(log.ModelUUID, gc.Equals, lt.ModelUUID)
   856  			count++
   857  			if count == expectedCount {
   858  				return
   859  			}
   860  		case <-timeout:
   861  			c.Fatalf("timed out waiting for logs (received %d)", count)
   862  		}
   863  	}
   864  }