github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/status_unit_test.go (about)

     1  // Copyright 2012-2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state_test
     5  
     6  import (
     7  	"time" // Only used for time types.
     8  
     9  	jc "github.com/juju/testing/checkers"
    10  	gc "gopkg.in/check.v1"
    11  
    12  	"github.com/juju/juju/core/status"
    13  	"github.com/juju/juju/state"
    14  	"github.com/juju/juju/testing"
    15  )
    16  
    17  type UnitStatusSuite struct {
    18  	ConnSuite
    19  	unit *state.Unit
    20  }
    21  
    22  var _ = gc.Suite(&UnitStatusSuite{})
    23  
    24  func (s *UnitStatusSuite) SetUpTest(c *gc.C) {
    25  	s.ConnSuite.SetUpTest(c)
    26  	s.unit = s.Factory.MakeUnit(c, nil)
    27  }
    28  
    29  func (s *UnitStatusSuite) TestInitialStatus(c *gc.C) {
    30  	s.checkInitialStatus(c)
    31  }
    32  
    33  func (s *UnitStatusSuite) checkInitialStatus(c *gc.C) {
    34  	statusInfo, err := s.unit.Status()
    35  	c.Check(err, jc.ErrorIsNil)
    36  	checkInitialWorkloadStatus(c, statusInfo)
    37  }
    38  
    39  func (s *UnitStatusSuite) TestSetUnknownStatus(c *gc.C) {
    40  	now := testing.ZeroTime()
    41  	sInfo := status.StatusInfo{
    42  		Status:  status.Status("vliegkat"),
    43  		Message: "orville",
    44  		Since:   &now,
    45  	}
    46  	err := s.unit.SetStatus(sInfo)
    47  	c.Check(err, gc.ErrorMatches, `cannot set invalid status "vliegkat"`)
    48  
    49  	s.checkInitialStatus(c)
    50  }
    51  
    52  func (s *UnitStatusSuite) TestSetOverwritesData(c *gc.C) {
    53  	now := testing.ZeroTime()
    54  	sInfo := status.StatusInfo{
    55  		Status:  status.Active,
    56  		Message: "healthy",
    57  		Data: map[string]interface{}{
    58  			"pew.pew": "zap",
    59  		},
    60  		Since: &now,
    61  	}
    62  	err := s.unit.SetStatus(sInfo)
    63  	c.Check(err, jc.ErrorIsNil)
    64  
    65  	s.checkGetSetStatus(c)
    66  }
    67  
    68  func (s *UnitStatusSuite) TestGetSetStatusAlive(c *gc.C) {
    69  	s.checkGetSetStatus(c)
    70  }
    71  
    72  func (s *UnitStatusSuite) checkGetSetStatus(c *gc.C) {
    73  	now := testing.ZeroTime()
    74  	sInfo := status.StatusInfo{
    75  		Status:  status.Active,
    76  		Message: "healthy",
    77  		Data: map[string]interface{}{
    78  			"$ping": map[string]interface{}{
    79  				"foo.bar": 123,
    80  			}},
    81  		Since: &now,
    82  	}
    83  	err := s.unit.SetStatus(sInfo)
    84  	c.Check(err, jc.ErrorIsNil)
    85  
    86  	unit, err := s.State.Unit(s.unit.Name())
    87  	c.Assert(err, jc.ErrorIsNil)
    88  
    89  	statusInfo, err := unit.Status()
    90  	c.Check(err, jc.ErrorIsNil)
    91  	c.Check(statusInfo.Status, gc.Equals, status.Active)
    92  	c.Check(statusInfo.Message, gc.Equals, "healthy")
    93  	c.Check(statusInfo.Data, jc.DeepEquals, map[string]interface{}{
    94  		"$ping": map[string]interface{}{
    95  			"foo.bar": 123,
    96  		},
    97  	})
    98  	c.Check(statusInfo.Since, gc.NotNil)
    99  }
   100  
   101  func (s *UnitStatusSuite) TestGetSetStatusDying(c *gc.C) {
   102  	preventUnitDestroyRemove(c, s.unit)
   103  	err := s.unit.Destroy()
   104  	c.Assert(err, jc.ErrorIsNil)
   105  
   106  	s.checkGetSetStatus(c)
   107  }
   108  
   109  func (s *UnitStatusSuite) TestGetSetStatusDead(c *gc.C) {
   110  	preventUnitDestroyRemove(c, s.unit)
   111  	err := s.unit.Destroy()
   112  	c.Assert(err, jc.ErrorIsNil)
   113  	err = s.unit.EnsureDead()
   114  	c.Assert(err, jc.ErrorIsNil)
   115  
   116  	// NOTE: it would be more technically correct to reject status updates
   117  	// while Dead, but it's easier and clearer, not to mention more efficient,
   118  	// to just depend on status doc existence.
   119  	s.checkGetSetStatus(c)
   120  }
   121  
   122  func (s *UnitStatusSuite) TestGetSetStatusGone(c *gc.C) {
   123  	err := s.unit.Destroy()
   124  	c.Assert(err, jc.ErrorIsNil)
   125  
   126  	now := testing.ZeroTime()
   127  	sInfo := status.StatusInfo{
   128  		Status:  status.Active,
   129  		Message: "not really",
   130  		Since:   &now,
   131  	}
   132  	err = s.unit.SetStatus(sInfo)
   133  	c.Check(err, gc.ErrorMatches, `cannot set status: unit not found`)
   134  
   135  	statusInfo, err := s.unit.Status()
   136  	c.Check(err, gc.ErrorMatches, `cannot get status: unit not found`)
   137  	c.Check(statusInfo, gc.DeepEquals, status.StatusInfo{})
   138  }
   139  
   140  func (s *UnitStatusSuite) TestSetUnitStatusSince(c *gc.C) {
   141  	now := testing.ZeroTime()
   142  	sInfo := status.StatusInfo{
   143  		Status:  status.Maintenance,
   144  		Message: "",
   145  		Since:   &now,
   146  	}
   147  	err := s.unit.SetStatus(sInfo)
   148  	c.Assert(err, jc.ErrorIsNil)
   149  	statusInfo, err := s.unit.Status()
   150  	c.Assert(err, jc.ErrorIsNil)
   151  	firstTime := statusInfo.Since
   152  	c.Assert(firstTime, gc.NotNil)
   153  	c.Assert(timeBeforeOrEqual(now, *firstTime), jc.IsTrue)
   154  
   155  	// Setting the same status a second time also updates the timestamp.
   156  	now = now.Add(1 * time.Second)
   157  	sInfo = status.StatusInfo{
   158  		Status:  status.Maintenance,
   159  		Message: "",
   160  		Since:   &now,
   161  	}
   162  	err = s.unit.SetStatus(sInfo)
   163  	c.Assert(err, jc.ErrorIsNil)
   164  	statusInfo, err = s.unit.Status()
   165  	c.Assert(err, jc.ErrorIsNil)
   166  	c.Assert(timeBeforeOrEqual(*firstTime, *statusInfo.Since), jc.IsTrue)
   167  }
   168  
   169  func (s *UnitStatusSuite) TestStatusSinceDoesNotChangeWhenReceivedStatusIsTheSameAsCurrent(c *gc.C) {
   170  	lastStatus, err := s.unit.Status()
   171  	c.Assert(err, jc.ErrorIsNil)
   172  	history, err := s.unit.StatusHistory(status.StatusHistoryFilter{Size: 10})
   173  	c.Check(err, jc.ErrorIsNil)
   174  	originalHistoryLength := len(history)
   175  
   176  	// Ensure new status change has a distinctly different update time.
   177  	now := lastStatus.Since.Add(1 * time.Hour)
   178  	changeTime := now
   179  	msg := "within the loop"
   180  	sInfo := status.StatusInfo{
   181  		Status:  status.Maintenance,
   182  		Message: msg,
   183  		Since:   &now,
   184  	}
   185  
   186  	// Setting the same status consecutively with different timestamps,
   187  	// should not update a status. It should, however, update
   188  	// the history record with the timestamp of the last call made.
   189  	for i := 0; i < 10; i++ {
   190  		err = s.unit.SetStatus(sInfo)
   191  		c.Assert(err, jc.ErrorIsNil)
   192  		// Next status sent will be an hour from now.
   193  		now = now.Add(1 * time.Hour)
   194  		sInfo.Since = &now
   195  	}
   196  
   197  	statusInfo, err := s.unit.Status()
   198  	c.Assert(err, jc.ErrorIsNil)
   199  	//Check that 'since' field reflects when change first happened.
   200  	c.Assert(changeTime.Equal((*statusInfo.Since)), jc.IsTrue)
   201  
   202  	historyAfter, err := s.unit.StatusHistory(status.StatusHistoryFilter{Size: 10})
   203  	c.Check(err, jc.ErrorIsNil)
   204  	// Only expecting one more status history addition.
   205  	c.Assert(len(historyAfter), gc.Equals, originalHistoryLength+1)
   206  
   207  	// The time of history record for this change should be updated
   208  	// to the last time setstatus was called.
   209  	expectedTimeInHistory := changeTime.Add(9 * time.Hour)
   210  	for _, record := range historyAfter {
   211  		if record.Message == msg {
   212  			c.Assert(expectedTimeInHistory.Equal((*record.Since)), jc.IsTrue)
   213  		}
   214  	}
   215  }
   216  
   217  func (s *UnitStatusSuite) TestStatusHistoryInitial(c *gc.C) {
   218  	history, err := s.unit.StatusHistory(status.StatusHistoryFilter{Size: 1})
   219  	c.Check(err, jc.ErrorIsNil)
   220  	c.Assert(history, gc.HasLen, 1)
   221  
   222  	checkInitialWorkloadStatus(c, history[0])
   223  }
   224  
   225  func (s *UnitStatusSuite) TestStatusHistoryShort(c *gc.C) {
   226  	primeUnitStatusHistory(c, s.Clock, s.unit, 5, 0)
   227  
   228  	history, err := s.unit.StatusHistory(status.StatusHistoryFilter{Size: 10})
   229  	c.Check(err, jc.ErrorIsNil)
   230  	c.Assert(history, gc.HasLen, 6)
   231  
   232  	checkInitialWorkloadStatus(c, history[5])
   233  	history = history[:5]
   234  	for i, statusInfo := range history {
   235  		checkPrimedUnitStatus(c, statusInfo, 4-i, 0)
   236  	}
   237  }
   238  
   239  func (s *UnitStatusSuite) TestStatusHistoryLong(c *gc.C) {
   240  	primeUnitStatusHistory(c, s.Clock, s.unit, 25, 0)
   241  
   242  	history, err := s.unit.StatusHistory(status.StatusHistoryFilter{Size: 15})
   243  	c.Check(err, jc.ErrorIsNil)
   244  	c.Check(history, gc.HasLen, 15)
   245  	for i, statusInfo := range history {
   246  		checkPrimedUnitStatus(c, statusInfo, 24-i, 0)
   247  	}
   248  }