github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/dblogpruner/worker_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package dblogpruner_test
     5  
     6  import (
     7  	stdtesting "testing"
     8  	"time"
     9  
    10  	"github.com/juju/clock"
    11  	"github.com/juju/clock/testclock"
    12  	"github.com/juju/loggo"
    13  	jujutesting "github.com/juju/testing"
    14  	jc "github.com/juju/testing/checkers"
    15  	"github.com/juju/utils/arch"
    16  	gc "gopkg.in/check.v1"
    17  	"gopkg.in/juju/names.v2"
    18  	"gopkg.in/juju/worker.v1"
    19  	"gopkg.in/juju/worker.v1/workertest"
    20  	"gopkg.in/mgo.v2"
    21  	"gopkg.in/mgo.v2/bson"
    22  
    23  	"github.com/juju/juju/state"
    24  	statetesting "github.com/juju/juju/state/testing"
    25  	"github.com/juju/juju/testing"
    26  	"github.com/juju/juju/version"
    27  	"github.com/juju/juju/worker/dblogpruner"
    28  )
    29  
    30  func TestPackage(t *stdtesting.T) {
    31  	testing.MgoTestPackage(t)
    32  }
    33  
    34  var _ = gc.Suite(&suite{})
    35  
    36  type suite struct {
    37  	jujutesting.MgoSuite
    38  	testing.BaseSuite
    39  
    40  	state          *state.State
    41  	pruner         worker.Worker
    42  	logsColl       *mgo.Collection
    43  	controllerColl *mgo.Collection
    44  }
    45  
    46  func (s *suite) SetUpSuite(c *gc.C) {
    47  	s.MgoSuite.SetUpSuite(c)
    48  	s.BaseSuite.SetUpSuite(c)
    49  }
    50  
    51  func (s *suite) TearDownSuite(c *gc.C) {
    52  	s.BaseSuite.TearDownSuite(c)
    53  	s.MgoSuite.TearDownSuite(c)
    54  }
    55  
    56  func (s *suite) SetUpTest(c *gc.C) {
    57  	s.MgoSuite.SetUpTest(c)
    58  	s.BaseSuite.SetUpTest(c)
    59  }
    60  
    61  func (s *suite) TearDownTest(c *gc.C) {
    62  	s.BaseSuite.TearDownTest(c)
    63  	s.MgoSuite.TearDownTest(c)
    64  }
    65  
    66  func (s *suite) setupState(c *gc.C, maxLogAge, maxCollectionMB string) {
    67  	controllerConfig := map[string]interface{}{
    68  		"max-logs-age":  maxLogAge,
    69  		"max-logs-size": maxCollectionMB,
    70  	}
    71  
    72  	ctlr := statetesting.InitializeWithArgs(c, statetesting.InitializeArgs{
    73  		Owner:            names.NewLocalUserTag("test-admin"),
    74  		Clock:            testclock.NewClock(testing.NonZeroTime()),
    75  		ControllerConfig: controllerConfig,
    76  	})
    77  	s.AddCleanup(func(*gc.C) { ctlr.Close() })
    78  	s.state = ctlr.SystemState()
    79  	s.logsColl = s.state.MongoSession().DB("logs").C("logs." + s.state.ModelUUID())
    80  }
    81  
    82  func (s *suite) startWorker(c *gc.C) {
    83  	pruner, err := dblogpruner.NewWorker(dblogpruner.Config{
    84  		State:         s.state,
    85  		Clock:         clock.WallClock,
    86  		PruneInterval: time.Millisecond,
    87  	})
    88  	c.Assert(err, jc.ErrorIsNil)
    89  	s.pruner = pruner
    90  	s.AddCleanup(func(c *gc.C) { workertest.CleanKill(c, s.pruner) })
    91  }
    92  
    93  func (s *suite) TestWorkerHasReport(c *gc.C) {
    94  	s.setupState(c, "24h", "1024M")
    95  	s.startWorker(c)
    96  
    97  	type reporter interface {
    98  		Report() map[string]interface{}
    99  	}
   100  
   101  	r, ok := s.pruner.(reporter)
   102  	c.Assert(ok, jc.IsTrue)
   103  	report := r.Report()
   104  	// We can't generally check on particular values except that there are keys for
   105  	// prune-age and prune-size.
   106  	_, ok = report["prune-age"]
   107  	c.Assert(ok, jc.IsTrue)
   108  	_, ok = report["prune-size"]
   109  	c.Assert(ok, jc.IsTrue)
   110  }
   111  
   112  func (s *suite) TestPrunesOldLogs(c *gc.C) {
   113  	maxLogAge := 24 * time.Hour
   114  	s.setupState(c, "24h", "1000P")
   115  	s.startWorker(c)
   116  
   117  	now := time.Now()
   118  	addLogsToPrune := func(count int) {
   119  		// Add messages beyond the prune threshold.
   120  		tPrune := now.Add(-maxLogAge - 1)
   121  		s.addLogs(c, tPrune, "prune", count)
   122  	}
   123  	addLogsToKeep := func(count int) {
   124  		// Add messages within the prune threshold.
   125  		s.addLogs(c, now, "keep", count)
   126  	}
   127  	for i := 0; i < 10; i++ {
   128  		addLogsToKeep(5)
   129  		addLogsToPrune(5)
   130  	}
   131  
   132  	// Wait for all logs with the message "prune" to be removed.
   133  	for attempt := testing.LongAttempt.Start(); attempt.Next(); {
   134  		pruneRemaining, err := s.logsColl.Find(bson.M{"x": "prune"}).Count()
   135  		c.Assert(err, jc.ErrorIsNil)
   136  		if pruneRemaining == 0 {
   137  			// All the "keep" messages should still be there.
   138  			keepCount, err := s.logsColl.Find(bson.M{"x": "keep"}).Count()
   139  			c.Assert(err, jc.ErrorIsNil)
   140  			c.Assert(keepCount, gc.Equals, 50)
   141  			return
   142  		}
   143  	}
   144  	c.Fatal("pruning didn't happen as expected")
   145  }
   146  
   147  func (s *suite) TestPrunesLogsBySize(c *gc.C) {
   148  	s.setupState(c, "999h", "2M")
   149  	startingLogCount := 25000
   150  	// On some of the architectures, the logs collection is much
   151  	// smaller than amd64, so we need more logs to get the right size.
   152  	switch arch.HostArch() {
   153  	case arch.S390X, arch.PPC64EL, arch.ARM64:
   154  		startingLogCount *= 2
   155  	}
   156  	s.addLogs(c, time.Now(), "stuff", startingLogCount)
   157  
   158  	s.startWorker(c)
   159  	for attempt := testing.LongAttempt.Start(); attempt.Next(); {
   160  		count, err := s.logsColl.Count()
   161  		c.Assert(err, jc.ErrorIsNil)
   162  		// The space used by MongoDB by the collection isn't that
   163  		// predictable, so just treat any pruning due to size as
   164  		// success.
   165  		if count < startingLogCount {
   166  			return
   167  		}
   168  	}
   169  	c.Fatal("pruning didn't happen as expected")
   170  }
   171  
   172  func (s *suite) addLogs(c *gc.C, t0 time.Time, text string, count int) {
   173  	dbLogger := state.NewDbLogger(s.state)
   174  	defer dbLogger.Close()
   175  
   176  	for offset := 0; offset < count; offset++ {
   177  		t := t0.Add(-time.Duration(offset) * time.Second)
   178  		dbLogger.Log([]state.LogRecord{{
   179  			Time:     t,
   180  			Entity:   names.NewMachineTag("0"),
   181  			Version:  version.Current,
   182  			Module:   "some.module",
   183  			Location: "foo.go:42",
   184  			Level:    loggo.INFO,
   185  			Message:  text,
   186  		}})
   187  	}
   188  }