github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/status_unitagent_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  	"fmt"
     8  	"time" // Only used for time types.
     9  
    10  	jc "github.com/juju/testing/checkers"
    11  	gc "gopkg.in/check.v1"
    12  
    13  	"github.com/juju/juju/core/status"
    14  	"github.com/juju/juju/state"
    15  	"github.com/juju/juju/testing"
    16  	"github.com/juju/juju/testing/factory"
    17  )
    18  
    19  type StatusUnitAgentSuite struct {
    20  	ConnSuite
    21  	unit  *state.Unit
    22  	agent *state.UnitAgent
    23  }
    24  
    25  var _ = gc.Suite(&StatusUnitAgentSuite{})
    26  
    27  func (s *StatusUnitAgentSuite) SetUpTest(c *gc.C) {
    28  	s.ConnSuite.SetUpTest(c)
    29  	s.unit = s.Factory.MakeUnit(c, nil)
    30  	s.agent = s.unit.Agent()
    31  }
    32  
    33  func (s *StatusUnitAgentSuite) TestInitialStatus(c *gc.C) {
    34  	s.checkInitialStatus(c)
    35  }
    36  
    37  func (s *StatusUnitAgentSuite) checkInitialStatus(c *gc.C) {
    38  	statusInfo, err := s.agent.Status()
    39  	c.Check(err, jc.ErrorIsNil)
    40  	checkInitialUnitAgentStatus(c, statusInfo)
    41  }
    42  
    43  func (s *StatusUnitAgentSuite) TestSetUnknownStatus(c *gc.C) {
    44  	now := testing.ZeroTime()
    45  	sInfo := status.StatusInfo{
    46  		Status:  status.Status("vliegkat"),
    47  		Message: "orville",
    48  		Since:   &now,
    49  	}
    50  	err := s.agent.SetStatus(sInfo)
    51  	c.Check(err, gc.ErrorMatches, `cannot set invalid status "vliegkat"`)
    52  
    53  	s.checkInitialStatus(c)
    54  }
    55  
    56  func (s *StatusUnitAgentSuite) TestSetErrorStatusWithoutInfo(c *gc.C) {
    57  	now := testing.ZeroTime()
    58  	sInfo := status.StatusInfo{
    59  		Status:  status.Error,
    60  		Message: "",
    61  		Since:   &now,
    62  	}
    63  	err := s.agent.SetStatus(sInfo)
    64  	c.Check(err, gc.ErrorMatches, `cannot set status "error" without info`)
    65  
    66  	s.checkInitialStatus(c)
    67  }
    68  
    69  func (s *StatusUnitAgentSuite) TestSetAllocatingStatusAlreadyAssigned(c *gc.C) {
    70  	now := testing.ZeroTime()
    71  	sInfo := status.StatusInfo{
    72  		Status:  status.Allocating,
    73  		Message: "",
    74  		Since:   &now,
    75  	}
    76  	err := s.agent.SetStatus(sInfo)
    77  	c.Check(err, gc.ErrorMatches, `cannot set status "allocating" as unit is already assigned`)
    78  
    79  	s.checkInitialStatus(c)
    80  }
    81  
    82  func (s *StatusUnitAgentSuite) TestSetStatusUnassigned(c *gc.C) {
    83  	app := s.Factory.MakeApplication(c, &factory.ApplicationParams{Name: "foo"})
    84  	u, err := app.AddUnit(state.AddUnitParams{})
    85  	c.Assert(err, jc.ErrorIsNil)
    86  	agent := u.Agent()
    87  	for _, value := range []status.Status{status.Idle, status.Executing, status.Rebooting, status.Failed} {
    88  		now := testing.ZeroTime()
    89  		sInfo := status.StatusInfo{
    90  			Status:  value,
    91  			Message: "",
    92  			Since:   &now,
    93  		}
    94  		err := agent.SetStatus(sInfo)
    95  		c.Check(err, gc.ErrorMatches, fmt.Sprintf(`cannot set status %q until unit is assigned`, value))
    96  
    97  		s.checkInitialStatus(c)
    98  	}
    99  }
   100  
   101  func (s *StatusUnitAgentSuite) TestSetStatusRunningNonCAAS(c *gc.C) {
   102  	app := s.Factory.MakeApplication(c, &factory.ApplicationParams{Name: "foo"})
   103  	u, err := app.AddUnit(state.AddUnitParams{})
   104  	c.Assert(err, jc.ErrorIsNil)
   105  	agent := u.Agent()
   106  	now := testing.ZeroTime()
   107  	sInfo := status.StatusInfo{
   108  		Status:  status.Running,
   109  		Message: "",
   110  		Since:   &now,
   111  	}
   112  	err = agent.SetStatus(sInfo)
   113  	c.Check(err, gc.ErrorMatches, `cannot set invalid status "running"`)
   114  	s.checkInitialStatus(c)
   115  }
   116  
   117  func (s *StatusUnitAgentSuite) TestSetOverwritesData(c *gc.C) {
   118  	now := testing.ZeroTime()
   119  	sInfo := status.StatusInfo{
   120  		Status:  status.Idle,
   121  		Message: "something",
   122  		Data: map[string]interface{}{
   123  			"pew.pew": "zap",
   124  		},
   125  		Since: &now,
   126  	}
   127  	err := s.agent.SetStatus(sInfo)
   128  	c.Check(err, jc.ErrorIsNil)
   129  
   130  	s.checkGetSetStatus(c)
   131  }
   132  
   133  func (s *StatusUnitAgentSuite) TestGetSetStatusAlive(c *gc.C) {
   134  	s.checkGetSetStatus(c)
   135  }
   136  
   137  func (s *StatusUnitAgentSuite) checkGetSetStatus(c *gc.C) {
   138  	now := testing.ZeroTime()
   139  	sInfo := status.StatusInfo{
   140  		Status:  status.Idle,
   141  		Message: "something",
   142  		Data: map[string]interface{}{
   143  			"$foo":    "bar",
   144  			"baz.qux": "ping",
   145  			"pong": map[string]interface{}{
   146  				"$unset": "txn-revno",
   147  			},
   148  		},
   149  		Since: &now,
   150  	}
   151  	err := s.agent.SetStatus(sInfo)
   152  	c.Check(err, jc.ErrorIsNil)
   153  
   154  	unit, err := s.State.Unit(s.unit.Name())
   155  	c.Assert(err, jc.ErrorIsNil)
   156  	agent := unit.Agent()
   157  
   158  	statusInfo, err := agent.Status()
   159  	c.Check(err, jc.ErrorIsNil)
   160  	c.Check(statusInfo.Status, gc.Equals, status.Idle)
   161  	c.Check(statusInfo.Message, gc.Equals, "something")
   162  	c.Check(statusInfo.Data, jc.DeepEquals, map[string]interface{}{
   163  		"$foo":    "bar",
   164  		"baz.qux": "ping",
   165  		"pong": map[string]interface{}{
   166  			"$unset": "txn-revno",
   167  		},
   168  	})
   169  	c.Check(statusInfo.Since, gc.NotNil)
   170  }
   171  
   172  func (s *StatusUnitAgentSuite) TestGetSetStatusDying(c *gc.C) {
   173  	preventUnitDestroyRemove(c, s.unit)
   174  	err := s.unit.Destroy()
   175  	c.Assert(err, jc.ErrorIsNil)
   176  
   177  	s.checkGetSetStatus(c)
   178  }
   179  
   180  func (s *StatusUnitAgentSuite) TestGetSetStatusDead(c *gc.C) {
   181  	preventUnitDestroyRemove(c, s.unit)
   182  	err := s.unit.Destroy()
   183  	c.Assert(err, jc.ErrorIsNil)
   184  	err = s.unit.EnsureDead()
   185  	c.Assert(err, jc.ErrorIsNil)
   186  
   187  	// NOTE: it would be more technically correct to reject status updates
   188  	// while Dead, but it's easier and clearer, not to mention more efficient,
   189  	// to just depend on status doc existence.
   190  	s.checkGetSetStatus(c)
   191  }
   192  
   193  func (s *StatusUnitAgentSuite) TestGetSetStatusGone(c *gc.C) {
   194  	err := s.unit.Destroy()
   195  	c.Assert(err, jc.ErrorIsNil)
   196  
   197  	now := testing.ZeroTime()
   198  	sInfo := status.StatusInfo{
   199  		Status:  status.Idle,
   200  		Message: "not really",
   201  		Since:   &now,
   202  	}
   203  	err = s.agent.SetStatus(sInfo)
   204  	c.Check(err, gc.ErrorMatches, `cannot set status: agent not found`)
   205  
   206  	statusInfo, err := s.agent.Status()
   207  	c.Check(err, gc.ErrorMatches, `cannot get status: agent not found`)
   208  	c.Check(statusInfo, gc.DeepEquals, status.StatusInfo{})
   209  }
   210  
   211  func (s *StatusUnitAgentSuite) TestGetSetErrorStatus(c *gc.C) {
   212  	now := testing.ZeroTime()
   213  	sInfo := status.StatusInfo{
   214  		Status:  status.Error,
   215  		Message: "test-hook failed",
   216  		Data: map[string]interface{}{
   217  			"foo": "bar",
   218  		},
   219  		Since: &now,
   220  	}
   221  	err := s.agent.SetStatus(sInfo)
   222  	c.Assert(err, jc.ErrorIsNil)
   223  
   224  	// Agent error is reported as unit error.
   225  	statusInfo, err := s.unit.Status()
   226  	c.Check(err, jc.ErrorIsNil)
   227  	c.Check(statusInfo.Status, gc.Equals, status.Error)
   228  	c.Check(statusInfo.Message, gc.Equals, "test-hook failed")
   229  	c.Check(statusInfo.Data, gc.DeepEquals, map[string]interface{}{
   230  		"foo": "bar",
   231  	})
   232  
   233  	// For agents, error is reported as idle.
   234  	statusInfo, err = s.agent.Status()
   235  	c.Check(err, jc.ErrorIsNil)
   236  	c.Check(statusInfo.Status, gc.Equals, status.Idle)
   237  	c.Check(statusInfo.Message, gc.Equals, "")
   238  	c.Check(statusInfo.Data, gc.HasLen, 0)
   239  }
   240  
   241  func timeBeforeOrEqual(timeBefore, timeOther time.Time) bool {
   242  	return timeBefore.Before(timeOther) || timeBefore.Equal(timeOther)
   243  }
   244  
   245  func (s *StatusUnitAgentSuite) TestSetAgentStatusSince(c *gc.C) {
   246  	now := testing.ZeroTime()
   247  	sInfo := status.StatusInfo{
   248  		Status:  status.Idle,
   249  		Message: "",
   250  		Since:   &now,
   251  	}
   252  	err := s.agent.SetStatus(sInfo)
   253  	c.Assert(err, jc.ErrorIsNil)
   254  	statusInfo, err := s.agent.Status()
   255  	c.Assert(err, jc.ErrorIsNil)
   256  	firstTime := statusInfo.Since
   257  	c.Assert(firstTime, gc.NotNil)
   258  	c.Assert(timeBeforeOrEqual(now, *firstTime), jc.IsTrue)
   259  
   260  	// Setting the same status a second time also updates the timestamp.
   261  	now = now.Add(1 * time.Second)
   262  	sInfo = status.StatusInfo{
   263  		Status:  status.Idle,
   264  		Message: "",
   265  		Since:   &now,
   266  	}
   267  	err = s.agent.SetStatus(sInfo)
   268  	c.Assert(err, jc.ErrorIsNil)
   269  	statusInfo, err = s.agent.Status()
   270  	c.Assert(err, jc.ErrorIsNil)
   271  	c.Assert(timeBeforeOrEqual(*firstTime, *statusInfo.Since), jc.IsTrue)
   272  }
   273  
   274  func (s *StatusUnitAgentSuite) TestStatusHistoryInitial(c *gc.C) {
   275  	history, err := s.agent.StatusHistory(status.StatusHistoryFilter{Size: 1})
   276  	c.Check(err, jc.ErrorIsNil)
   277  	c.Assert(history, gc.HasLen, 1)
   278  
   279  	checkInitialUnitAgentStatus(c, history[0])
   280  }
   281  
   282  func (s *StatusUnitAgentSuite) TestStatusHistoryShort(c *gc.C) {
   283  	primeUnitAgentStatusHistory(c, s.Clock, s.agent, 5, 0, "")
   284  
   285  	history, err := s.agent.StatusHistory(status.StatusHistoryFilter{Size: 10})
   286  	c.Check(err, jc.ErrorIsNil)
   287  	c.Assert(history, gc.HasLen, 6)
   288  
   289  	checkInitialUnitAgentStatus(c, history[5])
   290  	history = history[:5]
   291  	for i, statusInfo := range history {
   292  		checkPrimedUnitAgentStatus(c, statusInfo, 4-i, 0)
   293  	}
   294  }
   295  
   296  func (s *StatusUnitAgentSuite) TestStatusHistoryLong(c *gc.C) {
   297  	primeUnitAgentStatusHistory(c, s.Clock, s.agent, 25, 0, "")
   298  
   299  	history, err := s.agent.StatusHistory(status.StatusHistoryFilter{Size: 15})
   300  	c.Check(err, jc.ErrorIsNil)
   301  	c.Check(history, gc.HasLen, 15)
   302  	for i, statusInfo := range history {
   303  		checkPrimedUnitAgentStatus(c, statusInfo, 24-i, 0)
   304  	}
   305  }