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 }