github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/state/minimumunits_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state_test 5 6 import ( 7 jc "github.com/juju/testing/checkers" 8 gc "gopkg.in/check.v1" 9 "gopkg.in/mgo.v2" 10 11 "github.com/juju/juju/state" 12 ) 13 14 type MinUnitsSuite struct { 15 ConnSuite 16 service *state.Application 17 } 18 19 var _ = gc.Suite(&MinUnitsSuite{}) 20 21 func (s *MinUnitsSuite) SetUpTest(c *gc.C) { 22 s.ConnSuite.SetUpTest(c) 23 s.service = s.AddTestingService(c, "dummy-application", s.AddTestingCharm(c, "dummy")) 24 } 25 26 func (s *MinUnitsSuite) assertRevno(c *gc.C, expectedRevno int, expectedErr error) { 27 revno, err := state.MinUnitsRevno(s.State, s.service.Name()) 28 c.Assert(err, gc.Equals, expectedErr) 29 c.Assert(revno, gc.Equals, expectedRevno) 30 } 31 32 func (s *MinUnitsSuite) addUnits(c *gc.C, count int) { 33 for i := 0; i < count; i++ { 34 _, err := s.service.AddUnit() 35 c.Assert(err, jc.ErrorIsNil) 36 } 37 } 38 39 func (s *MinUnitsSuite) TestSetMinUnits(c *gc.C) { 40 service := s.service 41 for i, t := range []struct { 42 about string 43 initial int 44 changes []int 45 revno int 46 err error 47 }{{ 48 // Revno is set to zero on creation. 49 about: "setting minimum units", 50 changes: []int{42}, 51 }, { 52 // Revno is increased by the update operation. 53 about: "updating minimum units", 54 initial: 1, 55 changes: []int{42}, 56 revno: 1, 57 }, { 58 // Revno does not change. 59 about: "updating minimum units with the same value", 60 initial: 42, 61 changes: []int{42}, 62 }, { 63 // Revno is increased by each update. 64 about: "increasing minimum units multiple times", 65 initial: 1, 66 changes: []int{2, 3, 4}, 67 revno: 3, 68 }, { 69 // Revno does not change. 70 about: "decreasing minimum units multiple times", 71 initial: 5, 72 changes: []int{3, 2, 1}, 73 }, { 74 // No-op. 75 about: "removing not existent minimum units", 76 changes: []int{0}, 77 err: mgo.ErrNotFound, 78 }, { 79 // The document is deleted. 80 about: "removing existing minimum units", 81 initial: 42, 82 changes: []int{0}, 83 err: mgo.ErrNotFound, 84 }} { 85 c.Logf("test %d. %s", i, t.about) 86 // Set up initial minimum units if required. 87 if t.initial > 0 { 88 err := service.SetMinUnits(t.initial) 89 c.Assert(err, jc.ErrorIsNil) 90 } 91 // Insert/update minimum units. 92 for _, input := range t.changes { 93 err := service.SetMinUnits(input) 94 c.Assert(err, jc.ErrorIsNil) 95 c.Assert(service.MinUnits(), gc.Equals, input) 96 c.Assert(service.Refresh(), gc.IsNil) 97 c.Assert(service.MinUnits(), gc.Equals, input) 98 } 99 // Check the document existence and revno. 100 s.assertRevno(c, t.revno, t.err) 101 // Clean up, if required, the minUnits document. 102 err := service.SetMinUnits(0) 103 c.Assert(err, jc.ErrorIsNil) 104 } 105 } 106 107 func (s *MinUnitsSuite) TestInvalidMinUnits(c *gc.C) { 108 err := s.service.SetMinUnits(-1) 109 c.Assert(err, gc.ErrorMatches, `cannot set minimum units for application "dummy-application": cannot set a negative minimum number of units`) 110 } 111 112 func (s *MinUnitsSuite) TestMinUnitsInsertRetry(c *gc.C) { 113 defer state.SetRetryHooks(c, s.State, func() { 114 err := s.service.SetMinUnits(41) 115 c.Assert(err, jc.ErrorIsNil) 116 s.assertRevno(c, 0, nil) 117 }, func() { 118 s.assertRevno(c, 1, nil) 119 }).Check() 120 err := s.service.SetMinUnits(42) 121 c.Assert(err, jc.ErrorIsNil) 122 c.Assert(s.service.MinUnits(), gc.Equals, 42) 123 } 124 125 func (s *MinUnitsSuite) TestMinUnitsUpdateRetry(c *gc.C) { 126 err := s.service.SetMinUnits(41) 127 c.Assert(err, jc.ErrorIsNil) 128 defer state.SetRetryHooks(c, s.State, func() { 129 err := s.service.SetMinUnits(0) 130 c.Assert(err, jc.ErrorIsNil) 131 s.assertRevno(c, 0, mgo.ErrNotFound) 132 }, func() { 133 s.assertRevno(c, 0, nil) 134 }).Check() 135 err = s.service.SetMinUnits(42) 136 c.Assert(err, jc.ErrorIsNil) 137 c.Assert(s.service.MinUnits(), gc.Equals, 42) 138 } 139 140 func (s *MinUnitsSuite) TestMinUnitsRemoveBefore(c *gc.C) { 141 err := s.service.SetMinUnits(41) 142 c.Assert(err, jc.ErrorIsNil) 143 defer state.SetBeforeHooks(c, s.State, func() { 144 err := s.service.SetMinUnits(0) 145 c.Assert(err, jc.ErrorIsNil) 146 s.assertRevno(c, 0, mgo.ErrNotFound) 147 }).Check() 148 err = s.service.SetMinUnits(0) 149 c.Assert(err, jc.ErrorIsNil) 150 c.Assert(s.service.MinUnits(), gc.Equals, 0) 151 } 152 153 func (s *MinUnitsSuite) testDestroyOrRemoveServiceBefore(c *gc.C, initial, input int, preventRemoval bool) { 154 err := s.service.SetMinUnits(initial) 155 c.Assert(err, jc.ErrorIsNil) 156 expectedErr := `cannot set minimum units for application "dummy-application": application "dummy-application" not found` 157 if preventRemoval { 158 expectedErr = `cannot set minimum units for application "dummy-application": application is no longer alive` 159 s.addUnits(c, 1) 160 } 161 defer state.SetBeforeHooks(c, s.State, func() { 162 err := s.service.Destroy() 163 c.Assert(err, jc.ErrorIsNil) 164 }).Check() 165 err = s.service.SetMinUnits(input) 166 c.Assert(err, gc.ErrorMatches, expectedErr) 167 s.assertRevno(c, 0, mgo.ErrNotFound) 168 } 169 170 func (s *MinUnitsSuite) TestMinUnitsInsertDestroyServiceBefore(c *gc.C) { 171 s.testDestroyOrRemoveServiceBefore(c, 0, 42, true) 172 } 173 174 func (s *MinUnitsSuite) TestMinUnitsUpdateDestroyServiceBefore(c *gc.C) { 175 s.testDestroyOrRemoveServiceBefore(c, 1, 42, true) 176 } 177 178 func (s *MinUnitsSuite) TestMinUnitsRemoveDestroyServiceBefore(c *gc.C) { 179 s.testDestroyOrRemoveServiceBefore(c, 1, 0, true) 180 } 181 182 func (s *MinUnitsSuite) TestMinUnitsInsertRemoveServiceBefore(c *gc.C) { 183 s.testDestroyOrRemoveServiceBefore(c, 0, 42, false) 184 } 185 186 func (s *MinUnitsSuite) TestMinUnitsUpdateRemoveServiceBefore(c *gc.C) { 187 s.testDestroyOrRemoveServiceBefore(c, 1, 42, false) 188 } 189 190 func (s *MinUnitsSuite) TestMinUnitsRemoveRemoveServiceBefore(c *gc.C) { 191 s.testDestroyOrRemoveServiceBefore(c, 1, 0, false) 192 } 193 194 func (s *MinUnitsSuite) TestMinUnitsSetDestroyEntities(c *gc.C) { 195 err := s.service.SetMinUnits(1) 196 c.Assert(err, jc.ErrorIsNil) 197 s.assertRevno(c, 0, nil) 198 199 // Add two units to the service for later use. 200 unit1, err := s.service.AddUnit() 201 c.Assert(err, jc.ErrorIsNil) 202 unit2, err := s.service.AddUnit() 203 c.Assert(err, jc.ErrorIsNil) 204 205 // Destroy a unit and ensure the revno has been increased. 206 preventUnitDestroyRemove(c, unit1) 207 err = unit1.Destroy() 208 c.Assert(err, jc.ErrorIsNil) 209 s.assertRevno(c, 1, nil) 210 211 // Remove a unit and ensure the revno has been increased.. 212 err = unit2.Destroy() 213 c.Assert(err, jc.ErrorIsNil) 214 s.assertRevno(c, 2, nil) 215 216 // Destroy the service and ensure the minUnits document has been removed. 217 err = s.service.Destroy() 218 c.Assert(err, jc.ErrorIsNil) 219 s.assertRevno(c, 0, mgo.ErrNotFound) 220 } 221 222 func (s *MinUnitsSuite) TestMinUnitsNotSetDestroyEntities(c *gc.C) { 223 // Add two units to the service for later use. 224 unit1, err := s.service.AddUnit() 225 c.Assert(err, jc.ErrorIsNil) 226 unit2, err := s.service.AddUnit() 227 c.Assert(err, jc.ErrorIsNil) 228 229 // Destroy a unit and ensure the minUnits document has not been created. 230 preventUnitDestroyRemove(c, unit1) 231 err = unit1.Destroy() 232 c.Assert(err, jc.ErrorIsNil) 233 s.assertRevno(c, 0, mgo.ErrNotFound) 234 235 // Remove a unit and ensure the minUnits document has not been created. 236 err = unit2.Destroy() 237 c.Assert(err, jc.ErrorIsNil) 238 s.assertRevno(c, 0, mgo.ErrNotFound) 239 240 // Destroy the service and ensure the minUnits document is still missing. 241 err = s.service.Destroy() 242 c.Assert(err, jc.ErrorIsNil) 243 s.assertRevno(c, 0, mgo.ErrNotFound) 244 } 245 246 func assertAllUnits(c *gc.C, service *state.Application, expected int) { 247 units, err := service.AllUnits() 248 c.Assert(err, jc.ErrorIsNil) 249 c.Assert(units, gc.HasLen, expected) 250 } 251 252 func (s *MinUnitsSuite) TestEnsureMinUnits(c *gc.C) { 253 service := s.service 254 for i, t := range []struct { 255 about string // Test description. 256 initial int // Initial number of units. 257 minimum int // Minimum number of units for the application. 258 destroy int // Number of units to be destroyed before calling EnsureMinUnits. 259 expected int // Expected number of units after calling EnsureMinUnits. 260 }{{ 261 about: "no minimum units set", 262 }, { 263 about: "initial units > minimum units", 264 initial: 2, 265 minimum: 1, 266 expected: 2, 267 }, { 268 about: "initial units == minimum units", 269 initial: 2, 270 minimum: 2, 271 expected: 2, 272 }, { 273 about: "initial units < minimum units", 274 initial: 1, 275 minimum: 2, 276 expected: 2, 277 }, { 278 about: "alive units < minimum units", 279 initial: 2, 280 minimum: 2, 281 destroy: 1, 282 expected: 3, 283 }, { 284 about: "add multiple units", 285 initial: 6, 286 minimum: 5, 287 destroy: 4, 288 expected: 9, 289 }} { 290 c.Logf("test %d. %s", i, t.about) 291 292 // Set up initial units if required. 293 s.addUnits(c, t.initial) 294 295 // Set up minimum units if required. 296 err := service.SetMinUnits(t.minimum) 297 c.Assert(err, jc.ErrorIsNil) 298 299 // Destroy units if required. 300 allUnits, err := service.AllUnits() 301 c.Assert(err, jc.ErrorIsNil) 302 for i := 0; i < t.destroy; i++ { 303 preventUnitDestroyRemove(c, allUnits[i]) 304 err = allUnits[i].Destroy() 305 c.Assert(err, jc.ErrorIsNil) 306 } 307 308 // Ensure the minimum number of units is correctly restored. 309 c.Assert(service.Refresh(), gc.IsNil) 310 err = service.EnsureMinUnits() 311 c.Assert(err, jc.ErrorIsNil) 312 assertAllUnits(c, service, t.expected) 313 314 // Clean up the minUnits document and the units. 315 err = service.SetMinUnits(0) 316 c.Assert(err, jc.ErrorIsNil) 317 removeAllUnits(c, service) 318 } 319 } 320 321 func (s *MinUnitsSuite) TestEnsureMinUnitsServiceNotAlive(c *gc.C) { 322 err := s.service.SetMinUnits(2) 323 c.Assert(err, jc.ErrorIsNil) 324 s.addUnits(c, 1) 325 err = s.service.Destroy() 326 c.Assert(err, jc.ErrorIsNil) 327 expectedErr := `cannot ensure minimum units for application "dummy-application": application is not alive` 328 329 // An error is returned if the application is not alive. 330 c.Assert(s.service.EnsureMinUnits(), gc.ErrorMatches, expectedErr) 331 332 // An error is returned if the service was removed. 333 err = s.State.Cleanup() 334 c.Assert(err, jc.ErrorIsNil) 335 c.Assert(s.service.EnsureMinUnits(), gc.ErrorMatches, expectedErr) 336 } 337 338 func (s *MinUnitsSuite) TestEnsureMinUnitsUpdateMinUnitsRetry(c *gc.C) { 339 s.addUnits(c, 1) 340 err := s.service.SetMinUnits(4) 341 c.Assert(err, jc.ErrorIsNil) 342 defer state.SetRetryHooks(c, s.State, func() { 343 err := s.service.SetMinUnits(2) 344 c.Assert(err, jc.ErrorIsNil) 345 }, func() { 346 assertAllUnits(c, s.service, 2) 347 }).Check() 348 err = s.service.EnsureMinUnits() 349 c.Assert(err, jc.ErrorIsNil) 350 351 } 352 353 func (s *MinUnitsSuite) TestEnsureMinUnitsAddUnitsRetry(c *gc.C) { 354 err := s.service.SetMinUnits(3) 355 c.Assert(err, jc.ErrorIsNil) 356 defer state.SetRetryHooks(c, s.State, func() { 357 s.addUnits(c, 2) 358 }, func() { 359 assertAllUnits(c, s.service, 3) 360 }).Check() 361 err = s.service.EnsureMinUnits() 362 c.Assert(err, jc.ErrorIsNil) 363 } 364 365 func (s *MinUnitsSuite) testEnsureMinUnitsBefore(c *gc.C, f func(), minUnits, expectedUnits int) { 366 service := s.service 367 err := service.SetMinUnits(minUnits) 368 c.Assert(err, jc.ErrorIsNil) 369 defer state.SetBeforeHooks(c, s.State, f).Check() 370 err = service.EnsureMinUnits() 371 c.Assert(err, jc.ErrorIsNil) 372 assertAllUnits(c, service, expectedUnits) 373 } 374 375 func (s *MinUnitsSuite) TestEnsureMinUnitsDecreaseMinUnitsBefore(c *gc.C) { 376 f := func() { 377 err := s.service.SetMinUnits(3) 378 c.Assert(err, jc.ErrorIsNil) 379 } 380 s.testEnsureMinUnitsBefore(c, f, 42, 3) 381 } 382 383 func (s *MinUnitsSuite) TestEnsureMinUnitsRemoveMinUnitsBefore(c *gc.C) { 384 f := func() { 385 err := s.service.SetMinUnits(0) 386 c.Assert(err, jc.ErrorIsNil) 387 } 388 s.testEnsureMinUnitsBefore(c, f, 2, 0) 389 } 390 391 func (s *MinUnitsSuite) TestEnsureMinUnitsAddUnitsBefore(c *gc.C) { 392 f := func() { 393 s.addUnits(c, 2) 394 } 395 s.testEnsureMinUnitsBefore(c, f, 2, 2) 396 } 397 398 func (s *MinUnitsSuite) TestEnsureMinUnitsDestroyServiceBefore(c *gc.C) { 399 s.addUnits(c, 1) 400 err := s.service.SetMinUnits(42) 401 c.Assert(err, jc.ErrorIsNil) 402 defer state.SetBeforeHooks(c, s.State, func() { 403 err := s.service.Destroy() 404 c.Assert(err, jc.ErrorIsNil) 405 }).Check() 406 c.Assert(s.service.EnsureMinUnits(), gc.ErrorMatches, 407 `cannot ensure minimum units for application "dummy-application": application is not alive`) 408 } 409 410 func (s *MinUnitsSuite) TestEnsureMinUnitsDecreaseMinUnitsAfter(c *gc.C) { 411 s.addUnits(c, 2) 412 service := s.service 413 err := service.SetMinUnits(5) 414 c.Assert(err, jc.ErrorIsNil) 415 defer state.SetAfterHooks(c, s.State, func() { 416 err := service.SetMinUnits(3) 417 c.Assert(err, jc.ErrorIsNil) 418 }).Check() 419 c.Assert(service.Refresh(), gc.IsNil) 420 err = service.EnsureMinUnits() 421 c.Assert(err, jc.ErrorIsNil) 422 assertAllUnits(c, service, 3) 423 }