github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/status_history_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  	"regexp"
     8  	"time"
     9  
    10  	"github.com/juju/collections/set"
    11  	jc "github.com/juju/testing/checkers"
    12  	gc "gopkg.in/check.v1"
    13  
    14  	"github.com/juju/juju/core/arch"
    15  	"github.com/juju/juju/core/status"
    16  	"github.com/juju/juju/state"
    17  	statetesting "github.com/juju/juju/state/testing"
    18  	"github.com/juju/juju/testing/factory"
    19  )
    20  
    21  type StatusHistorySuite struct {
    22  	statetesting.StateSuite
    23  }
    24  
    25  var _ = gc.Suite(&StatusHistorySuite{})
    26  
    27  func (s *StatusHistorySuite) SetUpTest(c *gc.C) {
    28  	s.StateSuite.SetUpTest(c)
    29  }
    30  
    31  func (s *StatusHistorySuite) TestPruneStatusHistoryBySize(c *gc.C) {
    32  	application := s.Factory.MakeApplication(c, nil)
    33  
    34  	initialHistory := 20000
    35  	filter := status.StatusHistoryFilter{Size: 25000}
    36  	expectMax := 11100
    37  	// On some of the architectures, the status history collection is much
    38  	// smaller than amd64, so we need more entries to get the right size.
    39  	switch arch.HostArch() {
    40  	case arch.S390X, arch.PPC64EL, arch.ARM64:
    41  		initialHistory = 40000
    42  		filter = status.StatusHistoryFilter{Size: 50000}
    43  		expectMax = 20000
    44  	}
    45  
    46  	unit := s.Factory.MakeUnit(c, &factory.UnitParams{Application: application})
    47  	state.PrimeUnitStatusHistory(c, s.Clock, unit, status.Active, initialHistory, 1000, nil)
    48  
    49  	history, err := unit.StatusHistory(filter)
    50  	c.Assert(err, jc.ErrorIsNil)
    51  	c.Logf("%d\n", len(history))
    52  	c.Assert(history, gc.HasLen, initialHistory+1)
    53  
    54  	// Prune down to 1MB.
    55  	var stop <-chan struct{}
    56  	err = state.PruneStatusHistory(stop, s.State, 0, 1)
    57  	c.Assert(err, jc.ErrorIsNil)
    58  
    59  	history, err = unit.StatusHistory(filter)
    60  	c.Assert(err, jc.ErrorIsNil)
    61  	historyLen := len(history)
    62  	// When writing this test, the size was 6670 for about 0,00015 MB per entry
    63  	// but that is a size that can most likely change so I wont risk a flaky test
    64  	// here, enough to say that if this size suddenly is no longer less than
    65  	// half its good reason for suspicion.
    66  	c.Assert(historyLen, jc.LessThan, expectMax)
    67  }
    68  
    69  func (s *StatusHistorySuite) TestPruneStatusBySizeOnlyForController(c *gc.C) {
    70  	st := s.Factory.MakeModel(c, &factory.ModelParams{})
    71  	defer st.Close()
    72  
    73  	localFactory := factory.NewFactory(st, s.StatePool)
    74  	application := localFactory.MakeApplication(c, nil)
    75  	unit := localFactory.MakeUnit(c, &factory.UnitParams{Application: application})
    76  	state.PrimeUnitStatusHistory(c, s.Clock, unit, status.Active, 20000, 1000, nil)
    77  
    78  	history, err := unit.StatusHistory(status.StatusHistoryFilter{Size: 25000})
    79  	c.Assert(err, jc.ErrorIsNil)
    80  	c.Logf("%d\n", len(history))
    81  	c.Assert(history, gc.HasLen, 20001)
    82  
    83  	var stop <-chan struct{}
    84  	err = state.PruneStatusHistory(stop, st, 0, 1)
    85  	c.Assert(err, jc.ErrorIsNil)
    86  
    87  	history, err = unit.StatusHistory(status.StatusHistoryFilter{Size: 25000})
    88  	c.Assert(err, jc.ErrorIsNil)
    89  	historyLen := len(history)
    90  
    91  	// Pruning by size should only be done for the controller state.
    92  	c.Assert(historyLen, gc.Equals, 20001)
    93  }
    94  
    95  func (s *StatusHistorySuite) TestPruneStatusHistoryByDate(c *gc.C) {
    96  	// NOTE: the behaviour is bad, and the test is ugly. I'm just verifying
    97  	// the existing logic here.
    98  	//
    99  	// If you get the opportunity to fix this, you'll want a better shape of
   100  	// test (that injects a usable clock dependency, apart from anything else,
   101  	// and checks that we do our best to maintain a usable span of history
   102  	// rather than an arbitrary limit per entity. And isn't O(N) on status
   103  	// count in the model).
   104  
   105  	const count = 3
   106  	units := make([]*state.Unit, count)
   107  	agents := make([]*state.UnitAgent, count)
   108  	application := s.Factory.MakeApplication(c, nil)
   109  	for i := 0; i < count; i++ {
   110  		units[i] = s.Factory.MakeUnit(c, &factory.UnitParams{Application: application})
   111  		agents[i] = units[i].Agent()
   112  	}
   113  
   114  	primeUnitStatusHistory(c, s.Clock, units[0], 10, 0)
   115  	primeUnitStatusHistory(c, s.Clock, units[0], 10, 24*time.Hour)
   116  	primeUnitStatusHistory(c, s.Clock, units[1], 50, 0)
   117  	primeUnitStatusHistory(c, s.Clock, units[1], 50, 24*time.Hour)
   118  	primeUnitStatusHistory(c, s.Clock, units[2], 100, 0)
   119  	primeUnitStatusHistory(c, s.Clock, units[2], 100, 24*time.Hour)
   120  	primeUnitAgentStatusHistory(c, s.Clock, agents[0], 100, 0, "")
   121  	primeUnitAgentStatusHistory(c, s.Clock, agents[0], 100, 24*time.Hour, "")
   122  	primeUnitAgentStatusHistory(c, s.Clock, agents[1], 50, 0, "")
   123  	primeUnitAgentStatusHistory(c, s.Clock, agents[1], 50, 24*time.Hour, "")
   124  	primeUnitAgentStatusHistory(c, s.Clock, agents[2], 10, 0, "")
   125  	primeUnitAgentStatusHistory(c, s.Clock, agents[2], 10, 24*time.Hour, "")
   126  
   127  	history, err := units[0].StatusHistory(status.StatusHistoryFilter{Size: 50})
   128  	c.Assert(err, jc.ErrorIsNil)
   129  	c.Assert(history, gc.HasLen, 21)
   130  	checkInitialWorkloadStatus(c, history[10])
   131  	for i, statusInfo := range history[:10] {
   132  		checkPrimedUnitStatus(c, statusInfo, 9-i, 0)
   133  	}
   134  	for i, statusInfo := range history[11:20] {
   135  		checkPrimedUnitStatus(c, statusInfo, 9-i, 24*time.Hour)
   136  	}
   137  
   138  	var stop <-chan struct{}
   139  	err = state.PruneStatusHistory(stop, s.State, 10*time.Hour, 1024)
   140  	c.Assert(err, jc.ErrorIsNil)
   141  
   142  	history, err = units[0].StatusHistory(status.StatusHistoryFilter{Size: 50})
   143  	c.Assert(err, jc.ErrorIsNil)
   144  	c.Assert(history, gc.HasLen, 11)
   145  	checkInitialWorkloadStatus(c, history[10])
   146  	for i, statusInfo := range history[:10] {
   147  		checkPrimedUnitStatus(c, statusInfo, 9-i, 0)
   148  	}
   149  
   150  	history, err = units[1].StatusHistory(status.StatusHistoryFilter{Size: 100})
   151  	c.Assert(err, jc.ErrorIsNil)
   152  	c.Assert(history, gc.HasLen, 51)
   153  	for i, statusInfo := range history[:50] {
   154  		checkPrimedUnitStatus(c, statusInfo, 49-i, 0)
   155  	}
   156  
   157  	history, err = units[2].StatusHistory(status.StatusHistoryFilter{Size: 200})
   158  	c.Assert(err, jc.ErrorIsNil)
   159  	c.Assert(history, gc.HasLen, 101)
   160  	for i, statusInfo := range history[:100] {
   161  		checkPrimedUnitStatus(c, statusInfo, 99-i, 0)
   162  	}
   163  
   164  	history, err = agents[0].StatusHistory(status.StatusHistoryFilter{Size: 200})
   165  	c.Assert(err, jc.ErrorIsNil)
   166  	c.Assert(history, gc.HasLen, 101)
   167  	for i, statusInfo := range history[:100] {
   168  		checkPrimedUnitAgentStatus(c, statusInfo, 99-i, 0)
   169  	}
   170  
   171  	history, err = agents[1].StatusHistory(status.StatusHistoryFilter{Size: 100})
   172  	c.Assert(err, jc.ErrorIsNil)
   173  	c.Assert(history, gc.HasLen, 51)
   174  	for i, statusInfo := range history[:50] {
   175  		checkPrimedUnitAgentStatus(c, statusInfo, 49-i, 0)
   176  	}
   177  
   178  	history, err = agents[2].StatusHistory(status.StatusHistoryFilter{Size: 50})
   179  	c.Assert(err, jc.ErrorIsNil)
   180  	c.Assert(history, gc.HasLen, 11)
   181  	checkInitialUnitAgentStatus(c, history[10])
   182  	for i, statusInfo := range history[:10] {
   183  		checkPrimedUnitAgentStatus(c, statusInfo, 9-i, 0)
   184  	}
   185  }
   186  
   187  func (s *StatusHistorySuite) TestStatusHistoryFilterRunningUpdateStatusHook(c *gc.C) {
   188  	application := s.Factory.MakeApplication(c, nil)
   189  	unit := s.Factory.MakeUnit(c, &factory.UnitParams{Application: application})
   190  	agent := unit.Agent()
   191  
   192  	primeUnitAgentStatusHistory(c, s.Clock, agent, 100, 0, "running update-status hook")
   193  	primeUnitAgentStatusHistory(c, s.Clock, agent, 100, 0, "doing something else")
   194  	history, err := agent.StatusHistory(status.StatusHistoryFilter{Size: 200})
   195  	c.Assert(err, jc.ErrorIsNil)
   196  	c.Assert(history, gc.HasLen, 200)
   197  	stateNumber := 0
   198  	message, err := regexp.Compile("doing something else|running update-status hook")
   199  	c.Assert(err, jc.ErrorIsNil)
   200  	for _, h := range history {
   201  		checkPrimedUnitAgentStatusWithRegexMessage(c, h, message)
   202  		stateNumber++
   203  	}
   204  }
   205  
   206  func (s *StatusHistorySuite) TestStatusHistoryFilterRunningUpdateStatusHookFiltered(c *gc.C) {
   207  	application := s.Factory.MakeApplication(c, nil)
   208  	unit := s.Factory.MakeUnit(c, &factory.UnitParams{Application: application})
   209  	agent := unit.Agent()
   210  
   211  	primeUnitAgentStatusHistory(c, s.Clock, agent, 100, 0, "running update-status hook")
   212  	primeUnitAgentStatusHistory(c, s.Clock, agent, 100, 0, "doing something else")
   213  	history, err := agent.StatusHistory(status.StatusHistoryFilter{Size: 200, Exclude: set.NewStrings("running update-status hook")})
   214  	c.Assert(err, jc.ErrorIsNil)
   215  	c.Assert(history, gc.HasLen, 101)
   216  	for i, statusInfo := range history[:100] {
   217  		checkPrimedUnitAgentStatusWithCustomMessage(c, statusInfo, 99-i, 0, "doing something else")
   218  	}
   219  }
   220  
   221  func (s *StatusHistorySuite) TestStatusHistoryFiltersByDateAndDelta(c *gc.C) {
   222  	// TODO(perrito666) setup should be extracted into a fixture and the
   223  	// 6 or 7 test cases each get their own method.
   224  	application := s.Factory.MakeApplication(c, nil)
   225  	unit := s.Factory.MakeUnit(c, &factory.UnitParams{Application: application})
   226  
   227  	twoDaysBack := time.Hour * 48
   228  	threeDaysBack := time.Hour * 72
   229  	now := s.Clock.Now()
   230  	twoDaysAgo := now.Add(-twoDaysBack)
   231  	threeDaysAgo := now.Add(-threeDaysBack)
   232  	sInfo := status.StatusInfo{
   233  		Status:  status.Active,
   234  		Message: "current status",
   235  		Since:   &now,
   236  	}
   237  	err := unit.SetStatus(sInfo)
   238  	c.Assert(err, jc.ErrorIsNil)
   239  	sInfo = status.StatusInfo{
   240  		Status:  status.Active,
   241  		Message: "2 days ago",
   242  		Since:   &twoDaysAgo,
   243  	}
   244  	unit.SetStatus(sInfo)
   245  	sInfo = status.StatusInfo{
   246  		Status:  status.Active,
   247  		Message: "3 days ago",
   248  		Since:   &threeDaysAgo,
   249  	}
   250  	unit.SetStatus(sInfo)
   251  	history, err := unit.StatusHistory(status.StatusHistoryFilter{Size: 50})
   252  	c.Assert(err, jc.ErrorIsNil)
   253  	c.Assert(history, gc.HasLen, 4)
   254  	c.Assert(history[0].Message, gc.Equals, "current status")
   255  	c.Assert(history[1].Message, gc.Equals, "waiting for machine")
   256  	c.Assert(history[2].Message, gc.Equals, "2 days ago")
   257  	c.Assert(history[3].Message, gc.Equals, "3 days ago")
   258  	now = now.Add(10 * time.Second) // lets add some padding to prevent races here.
   259  
   260  	// logs up to one day back, using delta.
   261  	oneDayBack := time.Hour * 24
   262  	history, err = unit.StatusHistory(status.StatusHistoryFilter{Delta: &oneDayBack})
   263  	c.Assert(err, jc.ErrorIsNil)
   264  	c.Assert(history, gc.HasLen, 2)
   265  	c.Assert(history[0].Message, gc.Equals, "current status")
   266  	c.Assert(history[1].Message, gc.Equals, "waiting for machine")
   267  
   268  	// logs up to one day back, using date.
   269  	yesterday := now.Add(-(time.Hour * 24))
   270  	history, err = unit.StatusHistory(status.StatusHistoryFilter{FromDate: &yesterday})
   271  	c.Assert(err, jc.ErrorIsNil)
   272  	c.Assert(history, gc.HasLen, 2)
   273  	c.Assert(history[0].Message, gc.Equals, "current status")
   274  	c.Assert(history[1].Message, gc.Equals, "waiting for machine")
   275  
   276  	// Logs up to two days ago, using delta.
   277  	history, err = unit.StatusHistory(status.StatusHistoryFilter{Delta: &twoDaysBack})
   278  	c.Assert(err, jc.ErrorIsNil)
   279  	c.Assert(history, gc.HasLen, 2)
   280  	c.Assert(history[0].Message, gc.Equals, "current status")
   281  	c.Assert(history[1].Message, gc.Equals, "waiting for machine")
   282  
   283  	// Logs up to two days ago, using date.
   284  
   285  	history, err = unit.StatusHistory(status.StatusHistoryFilter{FromDate: &twoDaysAgo})
   286  	c.Assert(err, jc.ErrorIsNil)
   287  	c.Assert(history, gc.HasLen, 2)
   288  	c.Assert(history[0].Message, gc.Equals, "current status")
   289  	c.Assert(history[1].Message, gc.Equals, "waiting for machine")
   290  
   291  	// Logs up to three days ago, using delta.
   292  	history, err = unit.StatusHistory(status.StatusHistoryFilter{Delta: &threeDaysBack})
   293  	c.Assert(err, jc.ErrorIsNil)
   294  	c.Assert(history, gc.HasLen, 3)
   295  	c.Assert(history[0].Message, gc.Equals, "current status")
   296  	c.Assert(history[1].Message, gc.Equals, "waiting for machine")
   297  	c.Assert(history[2].Message, gc.Equals, "2 days ago")
   298  
   299  	// Logs up to three days ago, using date.
   300  	history, err = unit.StatusHistory(status.StatusHistoryFilter{FromDate: &threeDaysAgo})
   301  	c.Assert(err, jc.ErrorIsNil)
   302  	c.Assert(history, gc.HasLen, 3)
   303  	c.Assert(history[0].Message, gc.Equals, "current status")
   304  	c.Assert(history[1].Message, gc.Equals, "waiting for machine")
   305  	c.Assert(history[2].Message, gc.Equals, "2 days ago")
   306  }
   307  
   308  func (s *StatusHistorySuite) TestSameValueNotRepeated(c *gc.C) {
   309  	application := s.Factory.MakeApplication(c, nil)
   310  	unit := s.Factory.MakeUnit(c, &factory.UnitParams{Application: application})
   311  
   312  	now := s.Clock.Now()
   313  	for i := 0; i < 10; i++ {
   314  		when := now.Add(time.Duration(i) * time.Second)
   315  		err := unit.SetStatus(status.StatusInfo{
   316  			Status:  status.Active,
   317  			Message: "current status",
   318  			Since:   &when,
   319  		})
   320  		c.Assert(err, jc.ErrorIsNil)
   321  	}
   322  
   323  	history, err := unit.StatusHistory(status.StatusHistoryFilter{Size: 50})
   324  	c.Assert(err, jc.ErrorIsNil)
   325  	c.Assert(history, gc.HasLen, 2)
   326  	c.Assert(history[0].Message, gc.Equals, "current status")
   327  	c.Assert(history[1].Message, gc.Equals, "waiting for machine")
   328  }