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 }