github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/unit_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 "fmt" 8 "strconv" 9 "strings" 10 "time" // Only used for time types. 11 12 "github.com/juju/charm/v12" 13 "github.com/juju/errors" 14 "github.com/juju/loggo" 15 "github.com/juju/names/v5" 16 jc "github.com/juju/testing/checkers" 17 jujutxn "github.com/juju/txn/v3" 18 gc "gopkg.in/check.v1" 19 "gopkg.in/juju/environschema.v1" 20 21 "github.com/juju/juju/core/config" 22 "github.com/juju/juju/core/constraints" 23 "github.com/juju/juju/core/instance" 24 "github.com/juju/juju/core/model" 25 "github.com/juju/juju/core/network" 26 "github.com/juju/juju/core/secrets" 27 "github.com/juju/juju/core/status" 28 "github.com/juju/juju/state" 29 stateerrors "github.com/juju/juju/state/errors" 30 "github.com/juju/juju/state/testing" 31 statetesting "github.com/juju/juju/state/testing" 32 coretesting "github.com/juju/juju/testing" 33 "github.com/juju/juju/testing/factory" 34 ) 35 36 const ( 37 contentionErr = ".*state changing too quickly; try again soon" 38 ) 39 40 type UnitSuite struct { 41 ConnSuite 42 charm *state.Charm 43 application *state.Application 44 unit *state.Unit 45 } 46 47 var _ = gc.Suite(&UnitSuite{}) 48 49 func (s *UnitSuite) SetUpTest(c *gc.C) { 50 s.ConnSuite.SetUpTest(c) 51 s.charm = s.AddTestingCharm(c, "wordpress") 52 s.application = s.AddTestingApplication(c, "wordpress", s.charm) 53 var err error 54 s.unit, err = s.application.AddUnit(state.AddUnitParams{}) 55 c.Assert(err, jc.ErrorIsNil) 56 c.Assert(s.unit.Base(), jc.DeepEquals, state.Base{OS: "ubuntu", Channel: "12.10/stable"}) 57 c.Assert(s.unit.ShouldBeAssigned(), jc.IsTrue) 58 } 59 60 func (s *UnitSuite) TestUnitNotFound(c *gc.C) { 61 _, err := s.State.Unit("subway/0") 62 c.Assert(err, gc.ErrorMatches, `unit "subway/0" not found`) 63 c.Assert(err, jc.Satisfies, errors.IsNotFound) 64 } 65 66 func (s *UnitSuite) TestApplication(c *gc.C) { 67 app, err := s.unit.Application() 68 c.Assert(err, jc.ErrorIsNil) 69 c.Assert(app.Name(), gc.Equals, s.unit.ApplicationName()) 70 } 71 72 func (s *UnitSuite) TestCharmStateQuotaLimitWithMissingStateDocument(c *gc.C) { 73 // Set initial state with a restrictive limit. Since the state document 74 // does not exist yet, this test checks that the insert new document 75 // codepath correctly enforces the quota limits. 76 newState := new(state.UnitState) 77 newState.SetCharmState(map[string]string{ 78 "answer": "42", 79 "data": "encrypted", 80 }) 81 82 err := s.unit.SetState(newState, state.UnitStateSizeLimits{ 83 MaxCharmStateSize: 16, 84 }) 85 c.Assert(err, jc.Satisfies, errors.IsQuotaLimitExceeded) 86 87 // Try again with a more generous quota limit 88 err = s.unit.SetState(newState, state.UnitStateSizeLimits{ 89 MaxCharmStateSize: 640, 90 }) 91 c.Assert(err, jc.ErrorIsNil) 92 } 93 94 func (s *UnitSuite) TestCharmStateQuotaLimit(c *gc.C) { 95 // Set initial state with a generous limit 96 newState := new(state.UnitState) 97 newState.SetCharmState(map[string]string{ 98 "answer": "42", 99 "data": "encrypted", 100 }) 101 102 err := s.unit.SetState(newState, state.UnitStateSizeLimits{ 103 MaxCharmStateSize: 640000, 104 }) 105 c.Assert(err, jc.ErrorIsNil) 106 107 // Try to set a new state with a tight limit 108 newState.SetCharmState(map[string]string{ 109 "answer": "unknown", 110 "data": "decrypted", 111 }) 112 err = s.unit.SetState(newState, state.UnitStateSizeLimits{ 113 MaxCharmStateSize: 10, 114 }) 115 c.Assert(err, jc.Satisfies, errors.IsQuotaLimitExceeded) 116 } 117 118 func (s *UnitSuite) TestCombinedUnitStateQuotaLimit(c *gc.C) { 119 // Set initial state with a generous limit 120 newState := new(state.UnitState) 121 newState.SetUniterState("my state is legendary") 122 newState.SetRelationState(map[int]string{42: "some serialized blob"}) 123 newState.SetStorageState("storage is cheap") 124 125 err := s.unit.SetState(newState, state.UnitStateSizeLimits{ 126 MaxAgentStateSize: 640000, 127 }) 128 c.Assert(err, jc.ErrorIsNil) 129 130 // Try to set a new uniter state where the combined data will trip the quota limit check 131 newState.SetUniterState("state") 132 newState.SetRelationState(map[int]string{42: "a fresh serialized blob"}) 133 newState.SetStorageState("storage") 134 err = s.unit.SetState(newState, state.UnitStateSizeLimits{ 135 MaxAgentStateSize: 42, 136 }) 137 c.Assert(errors.IsQuotaLimitExceeded(err), jc.IsTrue) 138 } 139 140 func (s *UnitSuite) TestUnitStateWithDualQuotaLimits(c *gc.C) { 141 // Set state with dual quota limits and check that both limits are 142 // correctly enforced and do not interfere with each other. 143 newState := new(state.UnitState) 144 newState.SetUniterState("my state is legendary") 145 newState.SetRelationState(map[int]string{42: "some serialized blob"}) 146 newState.SetStorageState("storage is cheap") 147 newState.SetCharmState(map[string]string{ 148 "charm-data": strings.Repeat("lol", 1024), 149 }) 150 151 err := s.unit.SetState(newState, state.UnitStateSizeLimits{ 152 MaxCharmStateSize: 4096, 153 MaxAgentStateSize: 128, 154 }) 155 c.Assert(err, jc.ErrorIsNil) 156 } 157 158 func (s *UnitSuite) TestUnitStateNotSet(c *gc.C) { 159 // Try fetching the state without a state doc present 160 uState, err := s.unit.State() 161 c.Assert(err, gc.IsNil) 162 st, found := uState.CharmState() 163 c.Assert(st, gc.IsNil, gc.Commentf("expected to receive a nil map when no state doc is present")) 164 c.Assert(found, jc.IsFalse) 165 ust, found := uState.UniterState() 166 c.Assert(ust, gc.Equals, "") 167 c.Assert(found, jc.IsFalse) 168 rst, found := uState.RelationState() 169 c.Assert(rst, gc.IsNil, gc.Commentf("expected to receive a nil map when no state doc is present")) 170 c.Assert(found, jc.IsFalse) 171 sst, found := uState.StorageState() 172 c.Assert(sst, gc.Equals, "") 173 c.Assert(found, jc.IsFalse) 174 mst, found := uState.MeterStatusState() 175 c.Assert(mst, gc.Equals, "") 176 c.Assert(found, jc.IsFalse) 177 } 178 179 func (s *UnitSuite) TestUnitStateExistingDocAddNewRelationData(c *gc.C) { 180 initialUniterState := "testing" 181 us := state.NewUnitState() 182 us.SetUniterState(initialUniterState) 183 err := s.unit.SetState(us, state.UnitStateSizeLimits{}) 184 c.Assert(err, gc.IsNil) 185 186 newRelationState := map[int]string{3: "three"} 187 newUS := state.NewUnitState() 188 newUS.SetRelationState(newRelationState) 189 err = s.unit.SetState(newUS, state.UnitStateSizeLimits{}) 190 c.Assert(err, gc.IsNil) 191 } 192 193 func (s *UnitSuite) TestUnitStateMutateState(c *gc.C) { 194 // Set initial state; this should create a new unitstate doc 195 initState := s.testUnitSuite(c) 196 197 // Mutate state again with an existing state doc 198 newCharmState := map[string]string{"foo": "42"} 199 newUS := state.NewUnitState() 200 newUS.SetCharmState(newCharmState) 201 err := s.unit.SetState(newUS, state.UnitStateSizeLimits{}) 202 c.Assert(err, gc.IsNil) 203 204 // Ensure state changed 205 uState, err := s.unit.State() 206 c.Assert(err, gc.IsNil) 207 assertUnitStateCharmState(c, uState, newCharmState) 208 209 // Ensure the other state did not. 210 assertUnitStateUniterState(c, uState, initState.uniterState) 211 assertUnitStateRelationState(c, uState, initState.relationState) 212 assertUnitStateStorageState(c, uState, initState.storageState) 213 assertUnitStateMeterStatusState(c, uState, initState.meterStatusState) 214 } 215 216 func (s *UnitSuite) TestUnitStateMutateUniterState(c *gc.C) { 217 // Set initial state; this should create a new unitstate doc 218 initState := s.testUnitSuite(c) 219 220 // Mutate uniter state again with an existing state doc 221 newUniterState := "new" 222 newUS := state.NewUnitState() 223 newUS.SetUniterState(newUniterState) 224 err := s.unit.SetState(newUS, state.UnitStateSizeLimits{}) 225 c.Assert(err, gc.IsNil) 226 227 // Ensure uniter state changed 228 uState, err := s.unit.State() 229 c.Assert(err, gc.IsNil) 230 assertUnitStateUniterState(c, uState, newUniterState) 231 232 // Ensure the other state did not. 233 assertUnitStateCharmState(c, uState, initState.charmState) 234 assertUnitStateRelationState(c, uState, initState.relationState) 235 assertUnitStateStorageState(c, uState, initState.storageState) 236 assertUnitStateMeterStatusState(c, uState, initState.meterStatusState) 237 } 238 239 func (s *UnitSuite) TestUnitStateMutateChangeRelationState(c *gc.C) { 240 // Set initial state; this should create a new unitstate doc 241 initState := s.testUnitSuite(c) 242 243 // Mutate relation state again with an existing state doc 244 // by changing a value. 245 expectedRelationState := initState.relationState 246 expectedRelationState[1] = "five" 247 newUS := state.NewUnitState() 248 newUS.SetRelationState(expectedRelationState) 249 err := s.unit.SetState(newUS, state.UnitStateSizeLimits{}) 250 c.Assert(err, gc.IsNil) 251 252 // Ensure relation state changed 253 uState, err := s.unit.State() 254 c.Assert(err, gc.IsNil) 255 assertUnitStateRelationState(c, uState, expectedRelationState) 256 257 // Ensure the other state did not. 258 assertUnitStateCharmState(c, uState, initState.charmState) 259 assertUnitStateUniterState(c, uState, initState.uniterState) 260 assertUnitStateStorageState(c, uState, initState.storageState) 261 } 262 263 func (s *UnitSuite) TestUnitStateMutateDeleteRelationState(c *gc.C) { 264 // Set initial state; this should create a new unitstate doc 265 initState := s.testUnitSuite(c) 266 267 // Mutate relation state again with an existing state doc 268 // by deleting value. 269 expectedRelationState := initState.relationState 270 delete(expectedRelationState, 2) 271 newUS := state.NewUnitState() 272 newUS.SetRelationState(expectedRelationState) 273 err := s.unit.SetState(newUS, state.UnitStateSizeLimits{}) 274 c.Assert(err, gc.IsNil) 275 276 // Ensure relation state changed 277 uState, err := s.unit.State() 278 c.Assert(err, gc.IsNil) 279 assertUnitStateRelationState(c, uState, expectedRelationState) 280 281 // Ensure the other state did not. 282 assertUnitStateCharmState(c, uState, initState.charmState) 283 assertUnitStateUniterState(c, uState, initState.uniterState) 284 assertUnitStateStorageState(c, uState, initState.storageState) 285 assertUnitStateMeterStatusState(c, uState, initState.meterStatusState) 286 } 287 288 func (s *UnitSuite) TestUnitStateMutateStorageState(c *gc.C) { 289 // Set initial state; this should create a new unitstate doc 290 initState := s.testUnitSuite(c) 291 292 // Mutate storage state again with an existing state doc 293 newStorageState := "state" 294 newUS := state.NewUnitState() 295 newUS.SetStorageState(newStorageState) 296 err := s.unit.SetState(newUS, state.UnitStateSizeLimits{}) 297 c.Assert(err, gc.IsNil) 298 299 // Ensure storage state changed 300 uState, err := s.unit.State() 301 c.Assert(err, gc.IsNil) 302 assertUnitStateStorageState(c, uState, newStorageState) 303 304 // Ensure the other state did not. 305 assertUnitStateCharmState(c, uState, initState.charmState) 306 assertUnitStateUniterState(c, uState, initState.uniterState) 307 assertUnitStateRelationState(c, uState, initState.relationState) 308 assertUnitStateMeterStatusState(c, uState, initState.meterStatusState) 309 } 310 311 func (s *UnitSuite) TestUnitStateMutateMeterStatusState(c *gc.C) { 312 // Set initial state; this should create a new unitstate doc 313 initState := s.testUnitSuite(c) 314 315 // Mutate meter status state again with an existing state doc 316 newMeterStatusState := "GREEN" 317 newUS := state.NewUnitState() 318 newUS.SetMeterStatusState(newMeterStatusState) 319 err := s.unit.SetState(newUS, state.UnitStateSizeLimits{}) 320 c.Assert(err, gc.IsNil) 321 322 // Ensure meter status state changed 323 uState, err := s.unit.State() 324 c.Assert(err, gc.IsNil) 325 assertUnitStateMeterStatusState(c, uState, newMeterStatusState) 326 327 // Ensure the other state did not. 328 assertUnitStateCharmState(c, uState, initState.charmState) 329 assertUnitStateUniterState(c, uState, initState.uniterState) 330 assertUnitStateRelationState(c, uState, initState.relationState) 331 assertUnitStateStorageState(c, uState, initState.storageState) 332 } 333 334 func (s *UnitSuite) TestUnitStateDeleteState(c *gc.C) { 335 // Set initial state; this should create a new unitstate doc 336 initState := s.testUnitSuite(c) 337 338 // Mutate state again with an existing state doc 339 newUS := state.NewUnitState() 340 newUS.SetCharmState(map[string]string{}) 341 err := s.unit.SetState(newUS, state.UnitStateSizeLimits{}) 342 c.Assert(err, gc.IsNil) 343 344 // Ensure state changed 345 uState, err := s.unit.State() 346 c.Assert(err, jc.ErrorIsNil) 347 st, _ := uState.CharmState() 348 c.Assert(st, gc.IsNil, gc.Commentf("expected to receive a nil map when no state doc is present")) 349 350 // Ensure the other state did not. 351 assertUnitStateUniterState(c, uState, initState.uniterState) 352 assertUnitStateRelationState(c, uState, initState.relationState) 353 assertUnitStateStorageState(c, uState, initState.storageState) 354 assertUnitStateMeterStatusState(c, uState, initState.meterStatusState) 355 } 356 357 func (s *UnitSuite) TestUnitStateDeleteRelationState(c *gc.C) { 358 // Set initial state; this should create a new unitstate doc 359 initState := s.testUnitSuite(c) 360 361 // Mutate state again with an existing state doc 362 newUS := state.NewUnitState() 363 newUS.SetRelationState(map[int]string{}) 364 err := s.unit.SetState(newUS, state.UnitStateSizeLimits{}) 365 c.Assert(err, gc.IsNil) 366 367 // Ensure state changed 368 uState, err := s.unit.State() 369 c.Assert(err, jc.ErrorIsNil) 370 st, _ := uState.RelationState() 371 c.Assert(st, gc.IsNil, gc.Commentf("expected to receive a nil map when no state doc is present")) 372 373 // Ensure the other state did not. 374 assertUnitStateCharmState(c, uState, initState.charmState) 375 assertUnitStateUniterState(c, uState, initState.uniterState) 376 assertUnitStateStorageState(c, uState, initState.storageState) 377 assertUnitStateMeterStatusState(c, uState, initState.meterStatusState) 378 } 379 380 type initialUnitState struct { 381 charmState map[string]string 382 uniterState string 383 relationState map[int]string 384 storageState string 385 meterStatusState string 386 } 387 388 func (s *UnitSuite) testUnitSuite(c *gc.C) initialUnitState { 389 // Set initial state; this should create a new unitstate doc 390 initialCharmState := map[string]string{ 391 "foo": "bar", 392 "key.with.dot": "must work", 393 "key.with.$": "must work to", 394 } 395 initialUniterState := "testing" 396 initialRelationState := map[int]string{ 397 1: "one", 398 2: "two", 399 } 400 initialStorageState := "gnitset" 401 initialMeterStatusState := "RED" 402 403 us := state.NewUnitState() 404 us.SetCharmState(initialCharmState) 405 us.SetUniterState(initialUniterState) 406 us.SetRelationState(initialRelationState) 407 us.SetStorageState(initialStorageState) 408 us.SetMeterStatusState(initialMeterStatusState) 409 err := s.unit.SetState(us, state.UnitStateSizeLimits{}) 410 c.Assert(err, gc.IsNil) 411 412 // Read back initial state 413 uState, err := s.unit.State() 414 c.Assert(err, gc.IsNil) 415 obtainedCharmState, found := uState.CharmState() 416 c.Assert(found, jc.IsTrue) 417 c.Assert(obtainedCharmState, gc.DeepEquals, initialCharmState) 418 obtainedUniterState, found := uState.UniterState() 419 c.Assert(found, jc.IsTrue) 420 c.Assert(obtainedUniterState, gc.Equals, initialUniterState) 421 obtainedRelationState, found := uState.RelationState() 422 c.Assert(found, jc.IsTrue) 423 c.Assert(obtainedRelationState, gc.DeepEquals, initialRelationState) 424 obtainedStorageState, found := uState.StorageState() 425 c.Assert(found, jc.IsTrue) 426 c.Assert(obtainedStorageState, gc.Equals, initialStorageState) 427 obtainedMeterStatusState, found := uState.MeterStatusState() 428 c.Assert(found, jc.IsTrue) 429 c.Assert(obtainedMeterStatusState, gc.Equals, initialMeterStatusState) 430 431 return initialUnitState{ 432 charmState: initialCharmState, 433 uniterState: initialUniterState, 434 relationState: initialRelationState, 435 storageState: initialStorageState, 436 meterStatusState: initialMeterStatusState, 437 } 438 } 439 440 func assertUnitStateCharmState(c *gc.C, uState *state.UnitState, expected map[string]string) { 441 obtained, found := uState.CharmState() 442 c.Assert(found, jc.IsTrue) 443 c.Assert(obtained, gc.DeepEquals, expected) 444 } 445 446 func assertUnitStateUniterState(c *gc.C, uState *state.UnitState, expected string) { 447 obtained, found := uState.UniterState() 448 c.Assert(found, jc.IsTrue) 449 c.Assert(obtained, gc.Equals, expected) 450 } 451 452 func assertUnitStateRelationState(c *gc.C, uState *state.UnitState, expected map[int]string) { 453 obtained, found := uState.RelationState() 454 c.Assert(found, jc.IsTrue) 455 c.Assert(obtained, gc.DeepEquals, expected) 456 } 457 458 func assertUnitStateStorageState(c *gc.C, uState *state.UnitState, expected string) { 459 obtained, found := uState.StorageState() 460 c.Assert(found, jc.IsTrue) 461 c.Assert(obtained, gc.Equals, expected) 462 } 463 464 func assertUnitStateMeterStatusState(c *gc.C, uState *state.UnitState, expected string) { 465 obtained, found := uState.MeterStatusState() 466 c.Assert(found, jc.IsTrue) 467 c.Assert(obtained, gc.Equals, expected) 468 } 469 470 func (s *UnitSuite) TestUnitStateNopMutation(c *gc.C) { 471 // Set initial state; this should create a new unitstate doc 472 initialCharmState := map[string]string{ 473 "foo": "bar", 474 "key.with.dot": "must work", 475 "key.with.$": "must work to", 476 } 477 initialUniterState := "unit state" 478 initialRelationState := map[int]string{ 479 1: "one", 480 2: "two", 481 3: "three", 482 } 483 initialStorageState := "storage state" 484 iUnitState := state.NewUnitState() 485 iUnitState.SetCharmState(initialCharmState) 486 iUnitState.SetUniterState(initialUniterState) 487 iUnitState.SetRelationState(initialRelationState) 488 iUnitState.SetStorageState(initialStorageState) 489 err := s.unit.SetState(iUnitState, state.UnitStateSizeLimits{}) 490 c.Assert(err, gc.IsNil) 491 492 // Read revno 493 txnDoc := struct { 494 TxnRevno int64 `bson:"txn-revno"` 495 }{} 496 coll := s.Session.DB("juju").C("unitstates") 497 err = coll.Find(nil).One(&txnDoc) 498 c.Assert(err, gc.IsNil) 499 curRevNo := txnDoc.TxnRevno 500 501 // Set state using the same KV pairs; this should be a no-op 502 err = s.unit.SetState(iUnitState, state.UnitStateSizeLimits{}) 503 c.Assert(err, gc.IsNil) 504 505 err = coll.Find(nil).One(&txnDoc) 506 c.Assert(err, gc.IsNil) 507 c.Assert(txnDoc.TxnRevno, gc.Equals, curRevNo, gc.Commentf("expected state doc revno to remain the same")) 508 509 // Set state using a different set of KV pairs 510 sUnitState := state.NewUnitState() 511 sUnitState.SetCharmState(map[string]string{"something": "else"}) 512 err = s.unit.SetState(sUnitState, state.UnitStateSizeLimits{}) 513 c.Assert(err, gc.IsNil) 514 515 err = coll.Find(nil).One(&txnDoc) 516 c.Assert(err, gc.IsNil) 517 c.Assert(txnDoc.TxnRevno, jc.GreaterThan, curRevNo, gc.Commentf("expected state doc revno to be bumped")) 518 } 519 520 func (s *UnitSuite) TestConfigSettingsNeedCharmURLSet(c *gc.C) { 521 _, err := s.unit.ConfigSettings() 522 c.Assert(err, gc.ErrorMatches, "unit's charm URL must be set before retrieving config") 523 } 524 525 func (s *UnitSuite) TestConfigSettingsIncludeDefaults(c *gc.C) { 526 err := s.unit.SetCharmURL(s.charm.URL()) 527 c.Assert(err, jc.ErrorIsNil) 528 settings, err := s.unit.ConfigSettings() 529 c.Assert(err, jc.ErrorIsNil) 530 c.Assert(settings, gc.DeepEquals, charm.Settings{"blog-title": "My Title"}) 531 } 532 533 func (s *UnitSuite) TestConfigSettingsReflectApplication(c *gc.C) { 534 err := s.application.UpdateCharmConfig(model.GenerationMaster, charm.Settings{"blog-title": "no title"}) 535 c.Assert(err, jc.ErrorIsNil) 536 err = s.unit.SetCharmURL(s.charm.URL()) 537 c.Assert(err, jc.ErrorIsNil) 538 settings, err := s.unit.ConfigSettings() 539 c.Assert(err, jc.ErrorIsNil) 540 c.Assert(settings, gc.DeepEquals, charm.Settings{"blog-title": "no title"}) 541 542 err = s.application.UpdateCharmConfig(model.GenerationMaster, charm.Settings{"blog-title": "ironic title"}) 543 c.Assert(err, jc.ErrorIsNil) 544 settings, err = s.unit.ConfigSettings() 545 c.Assert(err, jc.ErrorIsNil) 546 c.Assert(settings, gc.DeepEquals, charm.Settings{"blog-title": "ironic title"}) 547 } 548 549 func (s *UnitSuite) TestConfigSettingsReflectCharm(c *gc.C) { 550 err := s.unit.SetCharmURL(s.charm.URL()) 551 c.Assert(err, jc.ErrorIsNil) 552 newCharm := s.AddConfigCharm(c, "wordpress", "options: {}", 123) 553 cfg := state.SetCharmConfig{Charm: newCharm, CharmOrigin: defaultCharmOrigin(newCharm.URL())} 554 err = s.application.SetCharm(cfg) 555 c.Assert(err, jc.ErrorIsNil) 556 557 // Settings still reflect charm set on unit. 558 settings, err := s.unit.ConfigSettings() 559 c.Assert(err, jc.ErrorIsNil) 560 c.Assert(settings, gc.DeepEquals, charm.Settings{"blog-title": "My Title"}) 561 562 // When the unit has the new charm set, it'll see the new config. 563 err = s.unit.SetCharmURL(newCharm.URL()) 564 c.Assert(err, jc.ErrorIsNil) 565 settings, err = s.unit.ConfigSettings() 566 c.Assert(err, jc.ErrorIsNil) 567 c.Assert(settings, gc.DeepEquals, charm.Settings{}) 568 } 569 570 func (s *UnitSuite) TestWatchConfigSettingsNeedsCharmURL(c *gc.C) { 571 _, err := s.unit.WatchConfigSettings() 572 c.Assert(err, gc.ErrorMatches, "unit's charm URL must be set before watching config") 573 } 574 575 func (s *UnitSuite) TestWatchConfigSettings(c *gc.C) { 576 err := s.unit.SetCharmURL(s.charm.URL()) 577 c.Assert(err, jc.ErrorIsNil) 578 579 s.WaitForModelWatchersIdle(c, s.Model.UUID()) 580 w, err := s.unit.WatchConfigSettings() 581 c.Assert(err, jc.ErrorIsNil) 582 defer testing.AssertStop(c, w) 583 584 // Initial event. 585 wc := testing.NewNotifyWatcherC(c, w) 586 wc.AssertOneChange() 587 588 // Update config a couple of times, check a single event. 589 err = s.application.UpdateCharmConfig(model.GenerationMaster, charm.Settings{"blog-title": "superhero paparazzi"}) 590 c.Assert(err, jc.ErrorIsNil) 591 // TODO(quiescence): these two changes should be one event. 592 wc.AssertOneChange() 593 err = s.application.UpdateCharmConfig(model.GenerationMaster, charm.Settings{"blog-title": "sauceror central"}) 594 c.Assert(err, jc.ErrorIsNil) 595 wc.AssertOneChange() 596 597 // Non-change is not reported. 598 err = s.application.UpdateCharmConfig(model.GenerationMaster, charm.Settings{"blog-title": "sauceror central"}) 599 c.Assert(err, jc.ErrorIsNil) 600 wc.AssertNoChange() 601 602 // Change application's charm; nothing detected. 603 newCharm := s.AddConfigCharm(c, "wordpress", floatConfig, 123) 604 cfg := state.SetCharmConfig{Charm: newCharm, CharmOrigin: defaultCharmOrigin(newCharm.URL())} 605 err = s.application.SetCharm(cfg) 606 c.Assert(err, jc.ErrorIsNil) 607 wc.AssertNoChange() 608 609 // Change application config for new charm; nothing detected. 610 err = s.application.UpdateCharmConfig(model.GenerationMaster, charm.Settings{ 611 "key": 42.0, 612 }) 613 c.Assert(err, jc.ErrorIsNil) 614 wc.AssertNoChange() 615 616 // NOTE: if we were to change the unit to use the new charm, we'd see 617 // another event, because the originally-watched document will become 618 // unreferenced and be removed. But I'm not testing that behaviour 619 // because it's not very helpful and subject to change. 620 } 621 622 func (s *UnitSuite) TestWatchConfigSettingsHash(c *gc.C) { 623 newCharm := s.AddConfigCharm(c, "wordpress", sortableConfig, 123) 624 cfg := state.SetCharmConfig{Charm: newCharm, CharmOrigin: defaultCharmOrigin(newCharm.URL())} 625 err := s.application.SetCharm(cfg) 626 c.Assert(err, jc.ErrorIsNil) 627 err = s.unit.SetCharmURL(newCharm.URL()) 628 c.Assert(err, jc.ErrorIsNil) 629 630 err = s.application.UpdateCharmConfig(model.GenerationMaster, charm.Settings{"blog-title": "sauceror central"}) 631 c.Assert(err, jc.ErrorIsNil) 632 633 s.WaitForModelWatchersIdle(c, s.Model.UUID()) 634 w, err := s.unit.WatchConfigSettingsHash() 635 c.Assert(err, jc.ErrorIsNil) 636 defer testing.AssertStop(c, w) 637 638 // Initial event. 639 wc := testing.NewStringsWatcherC(c, w) 640 wc.AssertChange("606cdac123d3d8d3031fe93db3d5afd9c95f709dfbc17e5eade1332b081ec6f9") 641 642 // Non-change is not reported. 643 err = s.application.UpdateCharmConfig(model.GenerationMaster, charm.Settings{ 644 "blog-title": "sauceror central", 645 }) 646 c.Assert(err, jc.ErrorIsNil) 647 wc.AssertNoChange() 648 649 // Add an attribute that comes before. 650 err = s.application.UpdateCharmConfig(model.GenerationMaster, charm.Settings{ 651 "alphabetic": 1, 652 }) 653 c.Assert(err, jc.ErrorIsNil) 654 // Sanity check. 655 config, err := s.unit.ConfigSettings() 656 c.Assert(err, jc.ErrorIsNil) 657 c.Assert(config, gc.DeepEquals, charm.Settings{ 658 // Ints that get round-tripped through mongo come back as int64. 659 "alphabetic": int64(1), 660 "blog-title": "sauceror central", 661 "zygomatic": nil, 662 }) 663 wc.AssertChange("886b9586df944be35b189e5678a85ac0b2ed4da46fcb10cecfd8c06b6d1baf0f") 664 665 // And one that comes after. 666 err = s.application.UpdateCharmConfig(model.GenerationMaster, charm.Settings{"zygomatic": 23}) 667 c.Assert(err, jc.ErrorIsNil) 668 wc.AssertChange("9ca0a511647f09db8fab07be3c70f49cc93f38f300ca82d6e26ec7d4a8b1b4c2") 669 670 // Setting a value to int64 instead of int has no effect on the 671 // hash (the information always comes back as int64). 672 err = s.application.UpdateCharmConfig(model.GenerationMaster, charm.Settings{"alphabetic": int64(1)}) 673 c.Assert(err, jc.ErrorIsNil) 674 wc.AssertNoChange() 675 676 // Change application's charm; nothing detected. 677 newCharm = s.AddConfigCharm(c, "wordpress", floatConfig, 125) 678 cfg = state.SetCharmConfig{Charm: newCharm, CharmOrigin: defaultCharmOrigin(newCharm.URL())} 679 err = s.application.SetCharm(cfg) 680 c.Assert(err, jc.ErrorIsNil) 681 wc.AssertNoChange() 682 683 // Change application config for new charm; nothing detected. 684 err = s.application.UpdateCharmConfig(model.GenerationMaster, charm.Settings{"key": 42.0}) 685 c.Assert(err, jc.ErrorIsNil) 686 wc.AssertNoChange() 687 688 // NOTE: if we were to change the unit to use the new charm, we'd see 689 // another event, because the originally-watched document will become 690 // unreferenced and be removed. But I'm not testing that behaviour 691 // because it's not very helpful and subject to change. 692 693 err = s.unit.Destroy() 694 c.Assert(err, jc.ErrorIsNil) 695 wc.AssertChange("") 696 } 697 698 func (s *UnitSuite) TestConfigHashesDifferentForDifferentCharms(c *gc.C) { 699 // Config hashes should be different if the charm url changes, 700 // even if the config is otherwise unchanged. This ensures that 701 // config-changed will be run on a unit after its charm is 702 // upgraded. 703 c.Logf("charm url %s", s.charm.URL()) 704 err := s.application.UpdateCharmConfig(model.GenerationMaster, charm.Settings{"blog-title": "sauceror central"}) 705 c.Assert(err, jc.ErrorIsNil) 706 err = s.unit.SetCharmURL(s.charm.URL()) 707 c.Assert(err, jc.ErrorIsNil) 708 709 w1, err := s.unit.WatchConfigSettingsHash() 710 c.Assert(err, jc.ErrorIsNil) 711 defer testing.AssertStop(c, w1) 712 713 wc1 := testing.NewStringsWatcherC(c, w1) 714 wc1.AssertChange("2e1f49c3e8b53892b822558401af33589522094681276a98458595114e04c0c1") 715 716 newCharm := s.AddConfigCharm(c, "wordpress", wordpressConfig, 125) 717 cfg := state.SetCharmConfig{Charm: newCharm, CharmOrigin: defaultCharmOrigin(newCharm.URL())} 718 err = s.application.SetCharm(cfg) 719 c.Assert(err, jc.ErrorIsNil) 720 721 c.Logf("new charm url %s", newCharm.URL()) 722 err = s.unit.SetCharmURL(newCharm.URL()) 723 c.Assert(err, jc.ErrorIsNil) 724 725 w3, err := s.unit.WatchConfigSettingsHash() 726 c.Assert(err, jc.ErrorIsNil) 727 defer testing.AssertStop(c, w3) 728 729 wc3 := testing.NewStringsWatcherC(c, w3) 730 wc3.AssertChange("35412457529c9e0b64b7642ad0f76137ee13b104c94136d0c18b2fe54ddf5d36") 731 } 732 733 func (s *UnitSuite) TestWatchApplicationConfigSettingsHash(c *gc.C) { 734 w, err := s.unit.WatchApplicationConfigSettingsHash() 735 c.Assert(err, jc.ErrorIsNil) 736 defer testing.AssertStop(c, w) 737 738 wc := testing.NewStringsWatcherC(c, w) 739 wc.AssertChange("92652ce7679e295c6567a3891c562dcab727c71543f8c1c3a38c3626ce064019") 740 741 schema := environschema.Fields{ 742 "username": environschema.Attr{Type: environschema.Tstring}, 743 "alive": environschema.Attr{Type: environschema.Tbool}, 744 "skill-level": environschema.Attr{Type: environschema.Tint}, 745 "options": environschema.Attr{Type: environschema.Tattrs}, 746 } 747 748 err = s.application.UpdateApplicationConfig(config.ConfigAttributes{ 749 "username": "abbas", 750 "alive": true, 751 "skill-level": 23, 752 "options": map[string]string{ 753 "fortuna": "crescis", 754 "luna": "velut", 755 "status": "malus", 756 }, 757 }, nil, schema, nil) 758 c.Assert(err, jc.ErrorIsNil) 759 760 wc.AssertChange("fa3b92db7c37210ed80da08c522abbe8ffffc9982ad2136342f3df8f221b5768") 761 762 // For reasons that I don't understand, application config 763 // converts int64s to int while charm config converts ints to 764 // int64 - I'm not going to try to untangle that now. 765 err = s.application.UpdateApplicationConfig(config.ConfigAttributes{ 766 "username": "bob", 767 "skill-level": int64(23), 768 }, nil, schema, nil) 769 c.Assert(err, jc.ErrorIsNil) 770 771 wc.AssertChange("d437a3b548fecf7f8c7748bf8d2acab0960ceb53f316a22803a6ca7442a9b8b3") 772 } 773 774 func (s *UnitSuite) addSubordinateUnit(c *gc.C) *state.Unit { 775 subCharm := s.AddTestingCharm(c, "logging") 776 s.AddTestingApplication(c, "logging", subCharm) 777 eps, err := s.State.InferEndpoints("wordpress", "logging") 778 c.Assert(err, jc.ErrorIsNil) 779 rel, err := s.State.AddRelation(eps...) 780 c.Assert(err, jc.ErrorIsNil) 781 ru, err := rel.Unit(s.unit) 782 c.Assert(err, jc.ErrorIsNil) 783 err = ru.EnterScope(nil) 784 c.Assert(err, jc.ErrorIsNil) 785 subUnit, err := s.State.Unit("logging/0") 786 c.Assert(err, jc.ErrorIsNil) 787 return subUnit 788 } 789 790 func (s *UnitSuite) setAssignedMachineAddresses(c *gc.C, u *state.Unit) { 791 mid, err := u.AssignedMachineId() 792 if errors.IsNotAssigned(err) { 793 err = u.AssignToNewMachine() 794 c.Assert(err, jc.ErrorIsNil) 795 mid, err = u.AssignedMachineId() 796 } 797 c.Assert(err, jc.ErrorIsNil) 798 machine, err := s.State.Machine(mid) 799 c.Assert(err, jc.ErrorIsNil) 800 err = machine.SetProvisioned("i-exist", "", "fake_nonce", nil) 801 c.Assert(err, jc.ErrorIsNil) 802 err = machine.SetProviderAddresses( 803 network.NewSpaceAddress("private.address.example.com", network.WithScope(network.ScopeCloudLocal)), 804 network.NewSpaceAddress("public.address.example.com", network.WithScope(network.ScopePublic)), 805 ) 806 c.Assert(err, jc.ErrorIsNil) 807 } 808 809 func (s *UnitSuite) TestPublicAddressSubordinate(c *gc.C) { 810 // A subordinate unit will never be created without the principal 811 // being assigned to a machine. 812 s.setAssignedMachineAddresses(c, s.unit) 813 subUnit := s.addSubordinateUnit(c) 814 address, err := subUnit.PublicAddress() 815 c.Assert(err, jc.ErrorIsNil) 816 c.Assert(address.Value, gc.Equals, "public.address.example.com") 817 } 818 819 func (s *UnitSuite) TestAllAddresses(c *gc.C) { 820 // Only used for CAAS units. 821 all, err := s.unit.AllAddresses() 822 c.Assert(err, jc.ErrorIsNil) 823 c.Assert(all, gc.HasLen, 0) 824 } 825 826 func (s *UnitSuite) TestPublicAddress(c *gc.C) { 827 machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits) 828 c.Assert(err, jc.ErrorIsNil) 829 err = s.unit.AssignToMachine(machine) 830 c.Assert(err, jc.ErrorIsNil) 831 832 _, err = s.unit.PublicAddress() 833 c.Assert(err, jc.Satisfies, network.IsNoAddressError) 834 835 public := network.NewSpaceAddress("8.8.8.8", network.WithScope(network.ScopePublic)) 836 private := network.NewSpaceAddress("127.0.0.1", network.WithScope(network.ScopeCloudLocal)) 837 838 err = machine.SetProviderAddresses(public, private) 839 c.Assert(err, jc.ErrorIsNil) 840 841 address, err := s.unit.PublicAddress() 842 c.Assert(err, jc.ErrorIsNil) 843 c.Check(address.Value, gc.Equals, "8.8.8.8") 844 } 845 846 func (s *UnitSuite) TestStablePrivateAddress(c *gc.C) { 847 machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits) 848 c.Assert(err, jc.ErrorIsNil) 849 err = s.unit.AssignToMachine(machine) 850 c.Assert(err, jc.ErrorIsNil) 851 852 err = machine.SetMachineAddresses(network.NewSpaceAddress("10.0.0.2")) 853 c.Assert(err, jc.ErrorIsNil) 854 855 // Now add an address that would previously have sorted before the 856 // default. 857 err = machine.SetMachineAddresses(network.NewSpaceAddress("10.0.0.1"), network.NewSpaceAddress("10.0.0.2")) 858 c.Assert(err, jc.ErrorIsNil) 859 860 // Assert the address is unchanged. 861 addr, err := s.unit.PrivateAddress() 862 c.Assert(err, jc.ErrorIsNil) 863 c.Assert(addr.Value, gc.Equals, "10.0.0.2") 864 } 865 866 func (s *UnitSuite) TestStablePublicAddress(c *gc.C) { 867 machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits) 868 c.Assert(err, jc.ErrorIsNil) 869 err = s.unit.AssignToMachine(machine) 870 c.Assert(err, jc.ErrorIsNil) 871 872 err = machine.SetProviderAddresses(network.NewSpaceAddress("8.8.8.8")) 873 c.Assert(err, jc.ErrorIsNil) 874 875 // Now add an address that would previously have sorted before the 876 // default. 877 err = machine.SetProviderAddresses(network.NewSpaceAddress("8.8.4.4"), network.NewSpaceAddress("8.8.8.8")) 878 c.Assert(err, jc.ErrorIsNil) 879 880 // Assert the address is unchanged. 881 addr, err := s.unit.PublicAddress() 882 c.Assert(err, jc.ErrorIsNil) 883 c.Assert(addr.Value, gc.Equals, "8.8.8.8") 884 } 885 886 func (s *UnitSuite) TestPublicAddressMachineAddresses(c *gc.C) { 887 machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits) 888 c.Assert(err, jc.ErrorIsNil) 889 err = s.unit.AssignToMachine(machine) 890 c.Assert(err, jc.ErrorIsNil) 891 892 publicProvider := network.NewSpaceAddress("8.8.8.8", network.WithScope(network.ScopePublic)) 893 privateProvider := network.NewSpaceAddress("127.0.0.1", network.WithScope(network.ScopeCloudLocal)) 894 privateMachine := network.NewSpaceAddress("127.0.0.2") 895 896 err = machine.SetProviderAddresses(privateProvider) 897 c.Assert(err, jc.ErrorIsNil) 898 err = machine.SetMachineAddresses(privateMachine) 899 c.Assert(err, jc.ErrorIsNil) 900 address, err := s.unit.PublicAddress() 901 c.Assert(err, jc.ErrorIsNil) 902 c.Check(address.Value, gc.Equals, "127.0.0.1") 903 904 err = machine.SetProviderAddresses(publicProvider, privateProvider) 905 c.Assert(err, jc.ErrorIsNil) 906 address, err = s.unit.PublicAddress() 907 c.Assert(err, jc.ErrorIsNil) 908 c.Check(address.Value, gc.Equals, "8.8.8.8") 909 } 910 911 func (s *UnitSuite) TestPrivateAddressSubordinate(c *gc.C) { 912 // A subordinate unit will never be created without the principal 913 // being assigned to a machine. 914 s.setAssignedMachineAddresses(c, s.unit) 915 subUnit := s.addSubordinateUnit(c) 916 address, err := subUnit.PrivateAddress() 917 c.Assert(err, jc.ErrorIsNil) 918 c.Assert(address.Value, gc.Equals, "private.address.example.com") 919 } 920 921 func (s *UnitSuite) TestPrivateAddress(c *gc.C) { 922 machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits) 923 c.Assert(err, jc.ErrorIsNil) 924 err = s.unit.AssignToMachine(machine) 925 c.Assert(err, jc.ErrorIsNil) 926 927 _, err = s.unit.PrivateAddress() 928 c.Assert(err, jc.Satisfies, network.IsNoAddressError) 929 930 public := network.NewSpaceAddress("8.8.8.8", network.WithScope(network.ScopePublic)) 931 private := network.NewSpaceAddress("127.0.0.1", network.WithScope(network.ScopeCloudLocal)) 932 933 err = machine.SetProviderAddresses(public, private) 934 c.Assert(err, jc.ErrorIsNil) 935 936 address, err := s.unit.PrivateAddress() 937 c.Assert(err, jc.ErrorIsNil) 938 c.Check(address.Value, gc.Equals, "127.0.0.1") 939 } 940 941 type destroyMachineTestCase struct { 942 target *state.Unit 943 host *state.Machine 944 desc string 945 destroyed bool 946 } 947 948 func (s *UnitSuite) destroyMachineTestCases(c *gc.C) []destroyMachineTestCase { 949 var result []destroyMachineTestCase 950 var err error 951 952 { 953 tc := destroyMachineTestCase{desc: "standalone principal", destroyed: true} 954 tc.host, err = s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits) 955 c.Assert(err, jc.ErrorIsNil) 956 tc.target, err = s.application.AddUnit(state.AddUnitParams{}) 957 c.Assert(err, jc.ErrorIsNil) 958 c.Assert(tc.target.AssignToMachine(tc.host), gc.IsNil) 959 result = append(result, tc) 960 } 961 { 962 tc := destroyMachineTestCase{desc: "co-located principals", destroyed: false} 963 tc.host, err = s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits) 964 c.Assert(err, jc.ErrorIsNil) 965 tc.target, err = s.application.AddUnit(state.AddUnitParams{}) 966 c.Assert(err, jc.ErrorIsNil) 967 c.Assert(tc.target.AssignToMachine(tc.host), gc.IsNil) 968 colocated, err := s.application.AddUnit(state.AddUnitParams{}) 969 c.Assert(err, jc.ErrorIsNil) 970 c.Assert(colocated.AssignToMachine(tc.host), gc.IsNil) 971 972 result = append(result, tc) 973 } 974 { 975 tc := destroyMachineTestCase{desc: "host has container", destroyed: false} 976 tc.host, err = s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits) 977 c.Assert(err, jc.ErrorIsNil) 978 _, err := s.State.AddMachineInsideMachine(state.MachineTemplate{ 979 Base: state.UbuntuBase("12.10"), 980 Jobs: []state.MachineJob{state.JobHostUnits}, 981 }, tc.host.Id(), instance.LXD) 982 c.Assert(err, jc.ErrorIsNil) 983 tc.target, err = s.application.AddUnit(state.AddUnitParams{}) 984 c.Assert(err, jc.ErrorIsNil) 985 c.Assert(tc.target.AssignToMachine(tc.host), gc.IsNil) 986 987 result = append(result, tc) 988 } 989 { 990 tc := destroyMachineTestCase{desc: "host has vote", destroyed: false} 991 tc.host, err = s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits) 992 c.Assert(err, jc.ErrorIsNil) 993 _, err = s.State.EnableHA(1, constraints.Value{}, state.UbuntuBase("12.10"), []string{tc.host.Id()}) 994 c.Assert(err, jc.ErrorIsNil) 995 node, err := s.State.ControllerNode(tc.host.Id()) 996 c.Assert(err, jc.ErrorIsNil) 997 c.Assert(node.SetHasVote(true), gc.IsNil) 998 tc.target, err = s.application.AddUnit(state.AddUnitParams{}) 999 c.Assert(err, jc.ErrorIsNil) 1000 c.Assert(tc.target.AssignToMachine(tc.host), gc.IsNil) 1001 1002 result = append(result, tc) 1003 } 1004 { 1005 tc := destroyMachineTestCase{desc: "unassigned unit", destroyed: true} 1006 tc.host, err = s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits) 1007 c.Assert(err, jc.ErrorIsNil) 1008 tc.target, err = s.application.AddUnit(state.AddUnitParams{}) 1009 c.Assert(err, jc.ErrorIsNil) 1010 c.Assert(tc.target.AssignToMachine(tc.host), gc.IsNil) 1011 result = append(result, tc) 1012 } 1013 1014 return result 1015 } 1016 1017 func (s *UnitSuite) TestRemoveUnitMachineFastForwardDestroy(c *gc.C) { 1018 for _, tc := range s.destroyMachineTestCases(c) { 1019 c.Log(tc.desc) 1020 err := tc.host.SetProvisioned("inst-id", "", "fake_nonce", nil) 1021 c.Assert(err, jc.ErrorIsNil) 1022 c.Assert(tc.target.Destroy(), gc.IsNil) 1023 if tc.destroyed { 1024 assertLife(c, tc.host, state.Dying) 1025 c.Assert(tc.host.EnsureDead(), gc.IsNil) 1026 } else { 1027 assertLife(c, tc.host, state.Alive) 1028 c.Assert(tc.host.Destroy(), gc.NotNil) 1029 } 1030 } 1031 } 1032 1033 func (s *UnitSuite) TestRemoveUnitMachineNoFastForwardDestroy(c *gc.C) { 1034 for _, tc := range s.destroyMachineTestCases(c) { 1035 c.Log(tc.desc) 1036 err := tc.host.SetProvisioned("inst-id", "", "fake_nonce", nil) 1037 c.Assert(err, jc.ErrorIsNil) 1038 preventUnitDestroyRemove(c, tc.target) 1039 c.Assert(tc.target.Destroy(), gc.IsNil) 1040 c.Assert(tc.target.EnsureDead(), gc.IsNil) 1041 assertLife(c, tc.host, state.Alive) 1042 c.Assert(tc.target.Remove(), gc.IsNil) 1043 if tc.destroyed { 1044 assertLife(c, tc.host, state.Dying) 1045 } else { 1046 assertLife(c, tc.host, state.Alive) 1047 c.Assert(tc.host.Destroy(), gc.NotNil) 1048 } 1049 } 1050 } 1051 1052 func (s *UnitSuite) TestRemoveUnitMachineNoDestroy(c *gc.C) { 1053 charmWithOut := s.AddTestingCharm(c, "mysql") 1054 applicationWithOutProfile := s.AddTestingApplication(c, "mysql", charmWithOut) 1055 1056 host, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits) 1057 c.Assert(err, jc.ErrorIsNil) 1058 err = host.SetProvisioned("inst-id", "", "fake_nonce", nil) 1059 c.Assert(err, jc.ErrorIsNil) 1060 1061 target, err := s.application.AddUnit(state.AddUnitParams{}) 1062 c.Assert(err, jc.ErrorIsNil) 1063 c.Assert(target.AssignToMachine(host), gc.IsNil) 1064 1065 colocated, err := applicationWithOutProfile.AddUnit(state.AddUnitParams{}) 1066 c.Assert(err, jc.ErrorIsNil) 1067 c.Assert(colocated.AssignToMachine(host), gc.IsNil) 1068 c.Assert(colocated.Destroy(), gc.IsNil) 1069 assertLife(c, host, state.Alive) 1070 1071 c.Assert(host.Destroy(), gc.NotNil) 1072 } 1073 1074 func (s *UnitSuite) setControllerVote(c *gc.C, id string, hasVote bool) { 1075 node, err := s.State.ControllerNode(id) 1076 c.Assert(err, jc.ErrorIsNil) 1077 c.Assert(node.SetHasVote(hasVote), gc.IsNil) 1078 } 1079 1080 func (s *UnitSuite) demoteController(c *gc.C, m *state.Machine) { 1081 s.setControllerVote(c, m.Id(), false) 1082 c.Assert(state.SetWantsVote(s.State, m.Id(), false), jc.ErrorIsNil) 1083 node, err := s.State.ControllerNode(m.Id()) 1084 c.Assert(err, jc.ErrorIsNil) 1085 err = s.State.RemoveControllerReference(node) 1086 c.Assert(err, jc.ErrorIsNil) 1087 } 1088 1089 func (s *UnitSuite) TestRemoveUnitMachineThrashed(c *gc.C) { 1090 host, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits) 1091 c.Assert(err, jc.ErrorIsNil) 1092 _, err = s.State.EnableHA(3, constraints.Value{}, state.UbuntuBase("12.10"), []string{host.Id()}) 1093 c.Assert(err, jc.ErrorIsNil) 1094 err = host.SetProvisioned("inst-id", "", "fake_nonce", nil) 1095 c.Assert(err, jc.ErrorIsNil) 1096 s.setControllerVote(c, host.Id(), true) 1097 target, err := s.application.AddUnit(state.AddUnitParams{}) 1098 c.Assert(err, jc.ErrorIsNil) 1099 c.Assert(target.AssignToMachine(host), gc.IsNil) 1100 flip := jujutxn.TestHook{ 1101 Before: func() { 1102 s.demoteController(c, host) 1103 }, 1104 } 1105 flop := jujutxn.TestHook{ 1106 Before: func() { 1107 s.setControllerVote(c, host.Id(), true) 1108 }, 1109 } 1110 state.SetMaxTxnAttempts(c, s.State, 3) 1111 defer state.SetTestHooks(c, s.State, flip, flop, flip).Check() 1112 1113 c.Assert(target.Destroy(), gc.ErrorMatches, `cannot destroy unit "wordpress/1": state changing too quickly; try again soon`) 1114 } 1115 1116 func (s *UnitSuite) TestRemoveUnitMachineRetryVoter(c *gc.C) { 1117 host, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits) 1118 c.Assert(err, jc.ErrorIsNil) 1119 _, err = s.State.EnableHA(3, constraints.Value{}, state.UbuntuBase("12.10"), []string{host.Id()}) 1120 c.Assert(err, jc.ErrorIsNil) 1121 err = host.SetProvisioned("inst-id", "", "fake_nonce", nil) 1122 c.Assert(err, jc.ErrorIsNil) 1123 target, err := s.application.AddUnit(state.AddUnitParams{}) 1124 c.Assert(err, jc.ErrorIsNil) 1125 c.Assert(target.AssignToMachine(host), gc.IsNil) 1126 1127 defer state.SetBeforeHooks(c, s.State, func() { 1128 s.setControllerVote(c, host.Id(), true) 1129 }).Check() 1130 1131 c.Assert(target.Destroy(), gc.IsNil) 1132 assertLife(c, host, state.Alive) 1133 } 1134 1135 func (s *UnitSuite) TestRemoveUnitMachineRetryNoVoter(c *gc.C) { 1136 host, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits) 1137 c.Assert(err, jc.ErrorIsNil) 1138 _, err = s.State.EnableHA(3, constraints.Value{}, state.UbuntuBase("12.10"), []string{host.Id()}) 1139 c.Assert(err, jc.ErrorIsNil) 1140 err = host.SetProvisioned("inst-id", "", "fake_nonce", nil) 1141 c.Assert(err, jc.ErrorIsNil) 1142 target, err := s.application.AddUnit(state.AddUnitParams{}) 1143 c.Assert(err, jc.ErrorIsNil) 1144 c.Assert(target.AssignToMachine(host), gc.IsNil) 1145 s.setControllerVote(c, host.Id(), true) 1146 1147 defer state.SetBeforeHooks(c, s.State, func() { 1148 s.demoteController(c, host) 1149 }, nil).Check() 1150 1151 c.Assert(target.Destroy(), gc.IsNil) 1152 assertLife(c, host, state.Dying) 1153 } 1154 1155 func (s *UnitSuite) TestRemoveUnitMachineRetryContainer(c *gc.C) { 1156 host, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits) 1157 c.Assert(err, jc.ErrorIsNil) 1158 target, err := s.application.AddUnit(state.AddUnitParams{}) 1159 c.Assert(err, jc.ErrorIsNil) 1160 c.Assert(target.AssignToMachine(host), gc.IsNil) 1161 defer state.SetTestHooks(c, s.State, jujutxn.TestHook{ 1162 Before: func() { 1163 machine, err := s.State.AddMachineInsideMachine(state.MachineTemplate{ 1164 Base: state.UbuntuBase("12.10"), 1165 Jobs: []state.MachineJob{state.JobHostUnits}, 1166 }, host.Id(), instance.LXD) 1167 c.Assert(err, jc.ErrorIsNil) 1168 assertLife(c, machine, state.Alive) 1169 1170 // test-setup verification for the disqualifying machine. 1171 hostHandle, err := s.State.Machine(host.Id()) 1172 c.Assert(err, jc.ErrorIsNil) 1173 containers, err := hostHandle.Containers() 1174 c.Assert(err, jc.ErrorIsNil) 1175 c.Assert(containers, gc.HasLen, 1) 1176 }, 1177 }).Check() 1178 1179 c.Assert(target.Destroy(), gc.IsNil) 1180 assertLife(c, host, state.Alive) 1181 } 1182 1183 func (s *UnitSuite) TestRemoveUnitMachineRetryOrCond(c *gc.C) { 1184 host, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits) 1185 c.Assert(err, jc.ErrorIsNil) 1186 _, err = s.State.EnableHA(1, constraints.Value{}, state.UbuntuBase("12.10"), []string{host.Id()}) 1187 c.Assert(err, jc.ErrorIsNil) 1188 err = host.SetProvisioned("inst-id", "", "fake_nonce", nil) 1189 c.Assert(err, jc.ErrorIsNil) 1190 target, err := s.application.AddUnit(state.AddUnitParams{}) 1191 c.Assert(err, jc.ErrorIsNil) 1192 c.Assert(target.AssignToMachine(host), gc.IsNil) 1193 1194 // This unit will be colocated in the transaction hook to cause a retry. 1195 colocated, err := s.application.AddUnit(state.AddUnitParams{}) 1196 c.Assert(err, jc.ErrorIsNil) 1197 1198 s.setControllerVote(c, host.Id(), true) 1199 1200 defer state.SetTestHooks(c, s.State, jujutxn.TestHook{ 1201 Before: func() { 1202 // Original assertion preventing host removal is no longer valid 1203 s.setControllerVote(c, host.Id(), false) 1204 1205 // But now the host gets a colocated unit, a different condition preventing removal 1206 hostHandle, err := s.State.Machine(host.Id()) 1207 c.Assert(err, jc.ErrorIsNil) 1208 c.Assert(colocated.AssignToMachine(hostHandle), gc.IsNil) 1209 }, 1210 }).Check() 1211 1212 c.Assert(target.Destroy(), gc.IsNil) 1213 assertLife(c, host, state.Alive) 1214 } 1215 1216 func (s *UnitSuite) TestRemoveUnitWRelationLastUnit(c *gc.C) { 1217 // This will assign it to a machine, and make it look like the machine agent is active. 1218 preventUnitDestroyRemove(c, s.unit) 1219 mysqlCharm := s.AddTestingCharm(c, "mysql") 1220 mysqlApp := s.AddTestingApplication(c, "mysql", mysqlCharm) 1221 mysqlUnit, err := mysqlApp.AddUnit(state.AddUnitParams{}) 1222 c.Assert(err, jc.ErrorIsNil) 1223 c.Assert(mysqlUnit.AssignToNewMachine(), jc.ErrorIsNil) 1224 endpoints, err := s.State.InferEndpoints("wordpress", "mysql") 1225 c.Assert(err, jc.ErrorIsNil) 1226 rel, err := s.State.AddRelation(endpoints...) 1227 c.Assert(err, jc.ErrorIsNil) 1228 relationUnit, err := rel.Unit(s.unit) 1229 c.Assert(err, jc.ErrorIsNil) 1230 c.Assert(relationUnit.EnterScope(nil), jc.ErrorIsNil) 1231 c.Assert(s.application.Destroy(), jc.ErrorIsNil) 1232 assertLife(c, s.application, state.Dying) 1233 c.Assert(s.unit.Destroy(), jc.ErrorIsNil) 1234 assertLife(c, s.unit, state.Dying) 1235 assertLife(c, s.application, state.Dying) 1236 c.Assert(s.unit.EnsureDead(), jc.ErrorIsNil) 1237 assertLife(c, s.application, state.Dying) 1238 c.Assert(s.unit.Remove(), jc.ErrorIsNil) 1239 c.Assert(s.State.Cleanup(), jc.ErrorIsNil) 1240 // Now the application should be gone 1241 c.Assert(s.application.Refresh(), jc.Satisfies, errors.IsNotFound) 1242 } 1243 1244 func (s *UnitSuite) TestRefresh(c *gc.C) { 1245 unit1, err := s.State.Unit(s.unit.Name()) 1246 c.Assert(err, jc.ErrorIsNil) 1247 1248 err = s.unit.SetPassword("arble-farble-dying-yarble") 1249 c.Assert(err, jc.ErrorIsNil) 1250 valid := unit1.PasswordValid("arble-farble-dying-yarble") 1251 c.Assert(valid, jc.IsFalse) 1252 err = unit1.Refresh() 1253 c.Assert(err, jc.ErrorIsNil) 1254 valid = unit1.PasswordValid("arble-farble-dying-yarble") 1255 c.Assert(valid, jc.IsTrue) 1256 1257 err = unit1.EnsureDead() 1258 c.Assert(err, jc.ErrorIsNil) 1259 err = unit1.Remove() 1260 c.Assert(err, jc.ErrorIsNil) 1261 err = unit1.Refresh() 1262 c.Assert(err, jc.Satisfies, errors.IsNotFound) 1263 } 1264 1265 func (s *UnitSuite) TestSetCharmURLSuccess(c *gc.C) { 1266 preventUnitDestroyRemove(c, s.unit) 1267 curl := s.unit.CharmURL() 1268 c.Assert(curl, gc.IsNil) 1269 1270 err := s.unit.SetCharmURL(s.charm.URL()) 1271 c.Assert(err, jc.ErrorIsNil) 1272 1273 curl = s.unit.CharmURL() 1274 c.Assert(curl, gc.NotNil) 1275 c.Assert(*curl, gc.Equals, s.charm.URL()) 1276 } 1277 1278 func (s *UnitSuite) TestSetCharmURLFailures(c *gc.C) { 1279 preventUnitDestroyRemove(c, s.unit) 1280 curl := s.unit.CharmURL() 1281 c.Assert(curl, gc.IsNil) 1282 1283 err := s.unit.SetCharmURL("") 1284 c.Assert(err, gc.ErrorMatches, "cannot set empty charm url") 1285 1286 err = s.unit.SetCharmURL("ch:missing/one-1") 1287 c.Assert(err, gc.ErrorMatches, `unknown charm url "ch:missing/one-1"`) 1288 1289 err = s.unit.EnsureDead() 1290 c.Assert(err, jc.ErrorIsNil) 1291 err = s.unit.SetCharmURL(s.charm.URL()) 1292 c.Assert(err, gc.Equals, stateerrors.ErrDead) 1293 } 1294 1295 func (s *UnitSuite) TestSetCharmURLWithRemovedUnit(c *gc.C) { 1296 err := s.unit.Destroy() 1297 c.Assert(err, jc.ErrorIsNil) 1298 assertRemoved(c, s.unit) 1299 1300 err = s.unit.SetCharmURL(s.charm.URL()) 1301 c.Assert(err, gc.Equals, stateerrors.ErrDead) 1302 } 1303 1304 func (s *UnitSuite) TestSetCharmURLWithDyingUnit(c *gc.C) { 1305 preventUnitDestroyRemove(c, s.unit) 1306 err := s.unit.Destroy() 1307 c.Assert(err, jc.ErrorIsNil) 1308 assertLife(c, s.unit, state.Dying) 1309 1310 err = s.unit.SetCharmURL(s.charm.URL()) 1311 c.Assert(err, jc.ErrorIsNil) 1312 1313 curl := s.unit.CharmURL() 1314 c.Assert(curl, gc.NotNil) 1315 c.Assert(*curl, gc.Equals, s.charm.URL()) 1316 } 1317 1318 func (s *UnitSuite) TestSetCharmURLRetriesWithDeadUnit(c *gc.C) { 1319 preventUnitDestroyRemove(c, s.unit) 1320 1321 defer state.SetBeforeHooks(c, s.State, func() { 1322 err := s.unit.Destroy() 1323 c.Assert(err, jc.ErrorIsNil) 1324 err = s.unit.EnsureDead() 1325 c.Assert(err, jc.ErrorIsNil) 1326 assertLife(c, s.unit, state.Dead) 1327 }).Check() 1328 1329 err := s.unit.SetCharmURL(s.charm.URL()) 1330 c.Assert(err, gc.Equals, stateerrors.ErrDead) 1331 } 1332 1333 func (s *UnitSuite) TestSetCharmURLRetriesWithDifferentURL(c *gc.C) { 1334 sch := s.AddConfigCharm(c, "wordpress", emptyConfig, 2) 1335 1336 defer state.SetTestHooks(c, s.State, 1337 jujutxn.TestHook{ 1338 Before: func() { 1339 // Set a different charm to force a retry: first on 1340 // the application, so the settings are created, then on 1341 // the unit. 1342 cfg := state.SetCharmConfig{Charm: sch, CharmOrigin: defaultCharmOrigin(sch.URL())} 1343 err := s.application.SetCharm(cfg) 1344 c.Assert(err, jc.ErrorIsNil) 1345 err = s.unit.SetCharmURL(sch.URL()) 1346 c.Assert(err, jc.ErrorIsNil) 1347 }, 1348 After: func() { 1349 // Set back the same charm on the application, so the 1350 // settings refcount is correct.. 1351 cfg := state.SetCharmConfig{Charm: s.charm, CharmOrigin: defaultCharmOrigin(s.charm.URL())} 1352 err := s.application.SetCharm(cfg) 1353 c.Assert(err, jc.ErrorIsNil) 1354 }, 1355 }, 1356 jujutxn.TestHook{ 1357 Before: nil, // Ensure there will be a retry. 1358 After: func() { 1359 // Verify it worked after the second attempt. 1360 err := s.unit.Refresh() 1361 c.Assert(err, jc.ErrorIsNil) 1362 currentURL := s.unit.CharmURL() 1363 c.Assert(currentURL, gc.NotNil) 1364 c.Assert(*currentURL, gc.Equals, s.charm.URL()) 1365 }, 1366 }, 1367 ).Check() 1368 1369 err := s.unit.SetCharmURL(s.charm.URL()) 1370 c.Assert(err, jc.ErrorIsNil) 1371 } 1372 1373 func (s *UnitSuite) TestDestroySetStatusRetry(c *gc.C) { 1374 defer state.SetRetryHooks(c, s.State, func() { 1375 err := s.unit.AssignToNewMachine() 1376 c.Assert(err, jc.ErrorIsNil) 1377 now := coretesting.NonZeroTime() 1378 sInfo := status.StatusInfo{ 1379 Status: status.Idle, 1380 Message: "", 1381 Since: &now, 1382 } 1383 err = s.unit.SetAgentStatus(sInfo) 1384 c.Assert(err, jc.ErrorIsNil) 1385 }, func() { 1386 assertLife(c, s.unit, state.Dying) 1387 }).Check() 1388 1389 err := s.unit.Destroy() 1390 c.Assert(err, jc.ErrorIsNil) 1391 } 1392 1393 func (s *UnitSuite) TestDestroySetCharmRetry(c *gc.C) { 1394 defer state.SetRetryHooks(c, s.State, func() { 1395 err := s.unit.SetCharmURL(s.charm.URL()) 1396 c.Assert(err, jc.ErrorIsNil) 1397 }, func() { 1398 assertRemoved(c, s.unit) 1399 }).Check() 1400 1401 err := s.unit.Destroy() 1402 c.Assert(err, jc.ErrorIsNil) 1403 } 1404 1405 func (s *UnitSuite) TestDestroyChangeCharmRetry(c *gc.C) { 1406 err := s.unit.SetCharmURL(s.charm.URL()) 1407 c.Assert(err, jc.ErrorIsNil) 1408 newCharm := s.AddConfigCharm(c, "mysql", "options: {}", 99) 1409 cfg := state.SetCharmConfig{Charm: newCharm, CharmOrigin: defaultCharmOrigin(newCharm.URL())} 1410 err = s.application.SetCharm(cfg) 1411 c.Assert(err, jc.ErrorIsNil) 1412 1413 defer state.SetRetryHooks(c, s.State, func() { 1414 err := s.unit.SetCharmURL(newCharm.URL()) 1415 c.Assert(err, jc.ErrorIsNil) 1416 }, func() { 1417 assertRemoved(c, s.unit) 1418 }).Check() 1419 1420 err = s.unit.Destroy() 1421 c.Assert(err, jc.ErrorIsNil) 1422 } 1423 1424 func (s *UnitSuite) TestDestroyAssignRetry(c *gc.C) { 1425 machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits) 1426 c.Assert(err, jc.ErrorIsNil) 1427 err = machine.SetProvisioned("inst-id", "", "fake_nonce", nil) 1428 c.Assert(err, jc.ErrorIsNil) 1429 1430 defer state.SetRetryHooks(c, s.State, func() { 1431 err := s.unit.AssignToMachine(machine) 1432 c.Assert(err, jc.ErrorIsNil) 1433 }, func() { 1434 assertRemoved(c, s.unit) 1435 // Also check the unit ref was properly removed from the machine doc -- 1436 // if it weren't, we wouldn't be able to make the machine Dead. 1437 err := machine.EnsureDead() 1438 c.Assert(err, jc.ErrorIsNil) 1439 }).Check() 1440 1441 err = s.unit.Destroy() 1442 c.Assert(err, jc.ErrorIsNil) 1443 } 1444 1445 func (s *UnitSuite) TestDestroyUnassignRetry(c *gc.C) { 1446 machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits) 1447 c.Assert(err, jc.ErrorIsNil) 1448 err = s.unit.AssignToMachine(machine) 1449 c.Assert(err, jc.ErrorIsNil) 1450 1451 defer state.SetRetryHooks(c, s.State, func() { 1452 err := s.unit.UnassignFromMachine() 1453 c.Assert(err, jc.ErrorIsNil) 1454 }, func() { 1455 assertRemoved(c, s.unit) 1456 }).Check() 1457 1458 err = s.unit.Destroy() 1459 c.Assert(err, jc.ErrorIsNil) 1460 } 1461 1462 func (s *UnitSuite) TestDestroyAssignErrorRetry(c *gc.C) { 1463 now := coretesting.NonZeroTime() 1464 sInfo := status.StatusInfo{ 1465 Status: status.Error, 1466 Message: "failed to assign", 1467 Since: &now, 1468 } 1469 err := s.unit.SetAgentStatus(sInfo) 1470 c.Assert(err, jc.ErrorIsNil) 1471 _, err = s.unit.AssignedMachineId() 1472 c.Assert(err, jc.Satisfies, errors.IsNotAssigned) 1473 1474 defer state.SetRetryHooks(c, s.State, func() { 1475 err := s.unit.AssignToNewMachine() 1476 c.Assert(err, jc.ErrorIsNil) 1477 now := coretesting.NonZeroTime() 1478 sInfo := status.StatusInfo{ 1479 Status: status.Idle, 1480 Message: "", 1481 Since: &now, 1482 } 1483 err = s.unit.SetAgentStatus(sInfo) 1484 c.Assert(err, jc.ErrorIsNil) 1485 }, func() { 1486 assertLife(c, s.unit, state.Dying) 1487 }).Check() 1488 err = s.unit.Destroy() 1489 c.Assert(err, jc.ErrorIsNil) 1490 } 1491 1492 func (s *UnitSuite) TestShortCircuitDestroyUnit(c *gc.C) { 1493 // A unit that has not set any status is removed directly. 1494 err := s.unit.Destroy() 1495 c.Assert(err, jc.ErrorIsNil) 1496 c.Assert(s.unit.Life(), gc.Equals, state.Dying) 1497 assertRemoved(c, s.unit) 1498 } 1499 1500 func (s *UnitSuite) TestShortCircuitDestroyUnitNotAssigned(c *gc.C) { 1501 // A unit that has not been assigned is removed directly. 1502 now := coretesting.NonZeroTime() 1503 err := s.unit.SetAgentStatus(status.StatusInfo{ 1504 Status: status.Error, 1505 Message: "cannot assign", 1506 Since: &now, 1507 }) 1508 c.Assert(err, jc.ErrorIsNil) 1509 err = s.unit.Destroy() 1510 c.Assert(err, jc.ErrorIsNil) 1511 c.Assert(s.unit.Life(), gc.Equals, state.Dying) 1512 assertRemoved(c, s.unit) 1513 } 1514 1515 func (s *UnitSuite) TestCannotShortCircuitDestroyAssignedUnit(c *gc.C) { 1516 // This test is similar to TestShortCircuitDestroyUnitNotAssigned but 1517 // the unit is assigned to a machine. 1518 err := s.unit.AssignToNewMachine() 1519 c.Assert(err, jc.ErrorIsNil) 1520 now := coretesting.NonZeroTime() 1521 err = s.unit.SetAgentStatus(status.StatusInfo{ 1522 Status: status.Error, 1523 Message: "some error", 1524 Since: &now, 1525 }) 1526 c.Assert(err, jc.ErrorIsNil) 1527 err = s.unit.Destroy() 1528 c.Assert(err, jc.ErrorIsNil) 1529 c.Assert(s.unit.Life(), gc.Equals, state.Dying) 1530 assertLife(c, s.unit, state.Dying) 1531 } 1532 1533 func (s *UnitSuite) TestCannotShortCircuitDestroyWithSubordinates(c *gc.C) { 1534 // A unit with subordinates is just set to Dying. 1535 s.AddTestingApplication(c, "logging", s.AddTestingCharm(c, "logging")) 1536 eps, err := s.State.InferEndpoints("logging", "wordpress") 1537 c.Assert(err, jc.ErrorIsNil) 1538 err = s.unit.AssignToNewMachine() 1539 c.Assert(err, jc.ErrorIsNil) 1540 rel, err := s.State.AddRelation(eps...) 1541 c.Assert(err, jc.ErrorIsNil) 1542 ru, err := rel.Unit(s.unit) 1543 c.Assert(err, jc.ErrorIsNil) 1544 err = ru.EnterScope(nil) 1545 c.Assert(err, jc.ErrorIsNil) 1546 err = s.unit.Destroy() 1547 c.Assert(err, jc.ErrorIsNil) 1548 c.Assert(s.unit.Life(), gc.Equals, state.Dying) 1549 assertLife(c, s.unit, state.Dying) 1550 } 1551 1552 func (s *UnitSuite) TestCannotShortCircuitDestroyWithAgentStatus(c *gc.C) { 1553 for i, test := range []struct { 1554 status status.Status 1555 info string 1556 }{{ 1557 status.Executing, "blah", 1558 }, { 1559 status.Idle, "blah", 1560 }, { 1561 status.Failed, "blah", 1562 }, { 1563 status.Rebooting, "blah", 1564 }} { 1565 c.Logf("test %d: %s", i, test.status) 1566 unit, err := s.application.AddUnit(state.AddUnitParams{}) 1567 c.Assert(err, jc.ErrorIsNil) 1568 err = unit.AssignToNewMachine() 1569 c.Assert(err, jc.ErrorIsNil) 1570 now := coretesting.NonZeroTime() 1571 sInfo := status.StatusInfo{ 1572 Status: test.status, 1573 Message: test.info, 1574 Since: &now, 1575 } 1576 err = unit.SetAgentStatus(sInfo) 1577 c.Assert(err, jc.ErrorIsNil) 1578 err = unit.Destroy() 1579 c.Assert(err, jc.ErrorIsNil) 1580 c.Assert(unit.Life(), gc.Equals, state.Dying) 1581 assertLife(c, unit, state.Dying) 1582 } 1583 } 1584 1585 func (s *UnitSuite) TestShortCircuitDestroyWithProvisionedMachine(c *gc.C) { 1586 // A unit assigned to a provisioned machine is still removed directly so 1587 // long as it has not set status. 1588 err := s.unit.AssignToNewMachine() 1589 c.Assert(err, jc.ErrorIsNil) 1590 mid, err := s.unit.AssignedMachineId() 1591 c.Assert(err, jc.ErrorIsNil) 1592 machine, err := s.State.Machine(mid) 1593 c.Assert(err, jc.ErrorIsNil) 1594 err = machine.SetProvisioned("i-malive", "", "fake_nonce", nil) 1595 c.Assert(err, jc.ErrorIsNil) 1596 err = s.unit.Destroy() 1597 c.Assert(err, jc.ErrorIsNil) 1598 c.Assert(s.unit.Life(), gc.Equals, state.Dying) 1599 assertRemoved(c, s.unit) 1600 } 1601 1602 func (s *UnitSuite) TestDestroyRemovesStatusHistory(c *gc.C) { 1603 err := s.unit.AssignToNewMachine() 1604 c.Assert(err, jc.ErrorIsNil) 1605 now := coretesting.NonZeroTime() 1606 for i := 0; i < 10; i++ { 1607 info := status.StatusInfo{ 1608 Status: status.Executing, 1609 Message: fmt.Sprintf("status %d", i), 1610 Since: &now, 1611 } 1612 err := s.unit.SetAgentStatus(info) 1613 c.Assert(err, jc.ErrorIsNil) 1614 info.Status = status.Active 1615 err = s.unit.SetStatus(info) 1616 c.Assert(err, jc.ErrorIsNil) 1617 1618 err = s.unit.SetWorkloadVersion(fmt.Sprintf("v.%d", i)) 1619 c.Assert(err, jc.ErrorIsNil) 1620 } 1621 1622 filter := status.StatusHistoryFilter{Size: 100} 1623 agentInfo, err := s.unit.AgentHistory().StatusHistory(filter) 1624 c.Assert(err, jc.ErrorIsNil) 1625 c.Assert(len(agentInfo), jc.GreaterThan, 9) 1626 1627 workloadInfo, err := s.unit.StatusHistory(filter) 1628 c.Assert(err, jc.ErrorIsNil) 1629 c.Assert(len(workloadInfo), jc.GreaterThan, 9) 1630 1631 versionInfo, err := s.unit.WorkloadVersionHistory().StatusHistory(filter) 1632 c.Assert(err, jc.ErrorIsNil) 1633 c.Assert(len(versionInfo), jc.GreaterThan, 9) 1634 1635 err = s.unit.Destroy() 1636 c.Assert(err, jc.ErrorIsNil) 1637 1638 agentInfo, err = s.unit.AgentHistory().StatusHistory(filter) 1639 c.Assert(err, jc.ErrorIsNil) 1640 c.Assert(agentInfo, gc.HasLen, 0) 1641 1642 workloadInfo, err = s.unit.StatusHistory(filter) 1643 c.Assert(err, jc.ErrorIsNil) 1644 c.Assert(workloadInfo, gc.HasLen, 0) 1645 1646 versionInfo, err = s.unit.WorkloadVersionHistory().StatusHistory(filter) 1647 c.Assert(err, jc.ErrorIsNil) 1648 c.Assert(versionInfo, gc.HasLen, 0) 1649 } 1650 1651 func assertLife(c *gc.C, entity state.Living, life state.Life) { 1652 c.Assert(entity.Refresh(), gc.IsNil) 1653 c.Assert(entity.Life(), gc.Equals, life) 1654 } 1655 1656 func assertRemoved(c *gc.C, entity state.Living) { 1657 c.Assert(entity.Refresh(), jc.Satisfies, errors.IsNotFound) 1658 c.Assert(entity.Destroy(), jc.ErrorIsNil) 1659 if entity, ok := entity.(state.AgentLiving); ok { 1660 c.Assert(entity.EnsureDead(), jc.ErrorIsNil) 1661 if err := entity.Remove(); err != nil { 1662 c.Assert(err, gc.ErrorMatches, ".*already removed.*") 1663 } 1664 err := entity.Refresh() 1665 c.Assert(err, jc.Satisfies, errors.IsNotFound) 1666 } 1667 } 1668 1669 func (s *UnitSuite) TestUnitsInError(c *gc.C) { 1670 now := coretesting.NonZeroTime() 1671 err := s.unit.SetAgentStatus(status.StatusInfo{ 1672 Status: status.Error, 1673 Message: "some error", 1674 Since: &now, 1675 }) 1676 c.Assert(err, jc.ErrorIsNil) 1677 1678 // Set a non unit error to ensure it's ignored. 1679 machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits) 1680 c.Assert(err, jc.ErrorIsNil) 1681 err = machine.SetStatus(status.StatusInfo{ 1682 Status: status.Error, 1683 Message: "some machine error", 1684 Since: &now, 1685 }) 1686 c.Assert(err, jc.ErrorIsNil) 1687 1688 // Add a unit not in error to ensure it's ignored. 1689 another, err := s.application.AddUnit(state.AddUnitParams{}) 1690 c.Assert(err, jc.ErrorIsNil) 1691 err = another.SetAgentStatus(status.StatusInfo{ 1692 Status: status.Allocating, 1693 Since: &now, 1694 }) 1695 c.Assert(err, jc.ErrorIsNil) 1696 1697 units, err := s.State.UnitsInError() 1698 c.Assert(err, jc.ErrorIsNil) 1699 c.Assert(units, gc.HasLen, 1) 1700 c.Assert(units[0], jc.DeepEquals, s.unit) 1701 } 1702 1703 func (s *UnitSuite) TestTag(c *gc.C) { 1704 c.Assert(s.unit.Tag().String(), gc.Equals, "unit-wordpress-0") 1705 } 1706 1707 func (s *UnitSuite) TestSetPassword(c *gc.C) { 1708 preventUnitDestroyRemove(c, s.unit) 1709 testSetPassword(c, func() (state.Authenticator, error) { 1710 return s.State.Unit(s.unit.Name()) 1711 }) 1712 } 1713 1714 func (s *UnitSuite) TestResolve(c *gc.C) { 1715 err := s.unit.Resolve(true) 1716 c.Assert(err, gc.ErrorMatches, `unit "wordpress/0" is not in an error state`) 1717 err = s.unit.Resolve(false) 1718 c.Assert(err, gc.ErrorMatches, `unit "wordpress/0" is not in an error state`) 1719 1720 now := coretesting.NonZeroTime() 1721 sInfo := status.StatusInfo{ 1722 Status: status.Error, 1723 Message: "gaaah", 1724 Since: &now, 1725 } 1726 err = s.unit.SetAgentStatus(sInfo) 1727 c.Assert(err, jc.ErrorIsNil) 1728 err = s.unit.Resolve(true) 1729 c.Assert(err, jc.ErrorIsNil) 1730 err = s.unit.Resolve(false) 1731 c.Assert(err, gc.ErrorMatches, `cannot set resolved mode for unit "wordpress/0": already resolved`) 1732 c.Assert(s.unit.Resolved(), gc.Equals, state.ResolvedRetryHooks) 1733 1734 err = s.unit.ClearResolved() 1735 c.Assert(err, jc.ErrorIsNil) 1736 err = s.unit.Resolve(false) 1737 c.Assert(err, jc.ErrorIsNil) 1738 err = s.unit.Resolve(true) 1739 c.Assert(err, gc.ErrorMatches, `cannot set resolved mode for unit "wordpress/0": already resolved`) 1740 c.Assert(s.unit.Resolved(), gc.Equals, state.ResolvedNoHooks) 1741 } 1742 1743 func (s *UnitSuite) TestGetSetClearResolved(c *gc.C) { 1744 mode := s.unit.Resolved() 1745 c.Assert(mode, gc.Equals, state.ResolvedNone) 1746 1747 err := s.unit.SetResolved(state.ResolvedNoHooks) 1748 c.Assert(err, jc.ErrorIsNil) 1749 err = s.unit.SetResolved(state.ResolvedNoHooks) 1750 c.Assert(err, gc.ErrorMatches, `cannot set resolved mode for unit "wordpress/0": already resolved`) 1751 1752 mode = s.unit.Resolved() 1753 c.Assert(mode, gc.Equals, state.ResolvedNoHooks) 1754 err = s.unit.Refresh() 1755 c.Assert(err, jc.ErrorIsNil) 1756 mode = s.unit.Resolved() 1757 c.Assert(mode, gc.Equals, state.ResolvedNoHooks) 1758 1759 err = s.unit.ClearResolved() 1760 c.Assert(err, jc.ErrorIsNil) 1761 mode = s.unit.Resolved() 1762 c.Assert(mode, gc.Equals, state.ResolvedNone) 1763 err = s.unit.Refresh() 1764 c.Assert(err, jc.ErrorIsNil) 1765 mode = s.unit.Resolved() 1766 c.Assert(mode, gc.Equals, state.ResolvedNone) 1767 err = s.unit.ClearResolved() 1768 c.Assert(err, jc.ErrorIsNil) 1769 1770 err = s.unit.SetResolved(state.ResolvedNone) 1771 c.Assert(err, gc.ErrorMatches, `cannot set resolved mode for unit "wordpress/0": invalid error resolution mode: ""`) 1772 err = s.unit.SetResolved(state.ResolvedMode("foo")) 1773 c.Assert(err, gc.ErrorMatches, `cannot set resolved mode for unit "wordpress/0": invalid error resolution mode: "foo"`) 1774 } 1775 1776 func (s *UnitSuite) TesOpenedPorts(c *gc.C) { 1777 // Accessing the port ranges for the unit should fail if it's not assigned to a machine. 1778 _, err := s.unit.OpenedPortRanges() 1779 c.Assert(errors.Cause(err), jc.Satisfies, errors.IsNotAssigned) 1780 1781 machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits) 1782 c.Assert(err, jc.ErrorIsNil) 1783 err = s.unit.AssignToMachine(machine) 1784 c.Assert(err, jc.ErrorIsNil) 1785 1786 // Verify no open ports before activity. 1787 unitPortRanges, err := s.unit.OpenedPortRanges() 1788 c.Assert(err, jc.ErrorIsNil) 1789 c.Assert(unitPortRanges.UniquePortRanges(), gc.HasLen, 0) 1790 1791 // Now open and close ports and ranges and check that they are persisted correctly 1792 s.assertPortRangesAfterOpenClose(c, s.unit, 1793 []network.PortRange{ 1794 network.MustParsePortRange("80/tcp"), 1795 network.MustParsePortRange("100-200/udp"), 1796 }, 1797 nil, // close 1798 []network.PortRange{ 1799 network.MustParsePortRange("80/tcp"), 1800 network.MustParsePortRange("100-200/udp"), 1801 }, 1802 ) 1803 1804 // Open a new port (53/udp) 1805 s.assertPortRangesAfterOpenClose(c, s.unit, 1806 []network.PortRange{ 1807 network.MustParsePortRange("53/udp"), 1808 }, 1809 nil, // close 1810 []network.PortRange{ 1811 network.MustParsePortRange("53/udp"), 1812 network.MustParsePortRange("80/tcp"), 1813 network.MustParsePortRange("100-200/udp"), 1814 }, 1815 ) 1816 1817 // Open same port but different protocol (53/tcp) 1818 s.assertPortRangesAfterOpenClose(c, s.unit, 1819 []network.PortRange{ 1820 network.MustParsePortRange("53/tcp"), 1821 }, 1822 nil, // close 1823 []network.PortRange{ 1824 network.MustParsePortRange("53/tcp"), 1825 network.MustParsePortRange("53/udp"), 1826 network.MustParsePortRange("80/tcp"), 1827 network.MustParsePortRange("100-200/udp"), 1828 }, 1829 ) 1830 1831 // Close an existing port (80/tcp) 1832 s.assertPortRangesAfterOpenClose(c, s.unit, 1833 nil, // open 1834 []network.PortRange{ 1835 network.MustParsePortRange("80/tcp"), 1836 }, 1837 []network.PortRange{ 1838 network.MustParsePortRange("53/tcp"), 1839 network.MustParsePortRange("53/udp"), 1840 network.MustParsePortRange("100-200/udp"), 1841 }, 1842 ) 1843 1844 // Close another existing port (100-200/udp) 1845 s.assertPortRangesAfterOpenClose(c, s.unit, 1846 nil, // open 1847 []network.PortRange{ 1848 network.MustParsePortRange("100-200/udp"), 1849 }, 1850 []network.PortRange{ 1851 network.MustParsePortRange("53/tcp"), 1852 network.MustParsePortRange("53/udp"), 1853 }, 1854 ) 1855 } 1856 1857 func (s *UnitSuite) assertPortRangesAfterOpenClose(c *gc.C, u *state.Unit, openRanges, closeRanges, exp []network.PortRange) { 1858 unitPortRanges, err := u.OpenedPortRanges() 1859 c.Assert(err, jc.ErrorIsNil) 1860 for _, pr := range openRanges { 1861 unitPortRanges.Open(allEndpoints, pr) 1862 } 1863 for _, pr := range closeRanges { 1864 unitPortRanges.Close(allEndpoints, pr) 1865 } 1866 c.Assert(s.State.ApplyOperation(unitPortRanges.Changes()), jc.ErrorIsNil) 1867 1868 // Reload ranges 1869 unitPortRanges, err = u.OpenedPortRanges() 1870 c.Assert(err, jc.ErrorIsNil) 1871 c.Assert(unitPortRanges.UniquePortRanges(), gc.DeepEquals, exp) 1872 } 1873 1874 func (s *UnitSuite) TestOpenClosePortWhenDying(c *gc.C) { 1875 machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits) 1876 c.Assert(err, jc.ErrorIsNil) 1877 err = s.unit.AssignToMachine(machine) 1878 c.Assert(err, jc.ErrorIsNil) 1879 1880 preventUnitDestroyRemove(c, s.unit) 1881 1882 // Ensure that we use a monotonically increasing port counter to 1883 // avoid no-op open port operations since the same unit is used for 1884 // both dying and dead unit states. 1885 nextPort := 1337 1886 testWhenDying(c, s.unit, noErr, contentionErr, func() error { 1887 // Open a port range 1888 unitPortRanges, err := s.unit.OpenedPortRanges() 1889 if err != nil { 1890 return errors.Annotatef(err, "cannot open port ranges") 1891 } 1892 1893 toOpen := fmt.Sprintf("%d/tcp", nextPort) 1894 nextPort++ 1895 unitPortRanges.Open(allEndpoints, network.MustParsePortRange(toOpen)) 1896 if err = s.State.ApplyOperation(unitPortRanges.Changes()); err != nil { 1897 return errors.Annotatef(err, "cannot open port ranges") 1898 } 1899 1900 err = s.unit.Refresh() 1901 if err != nil { 1902 return err 1903 } 1904 1905 // Open another port range 1906 toOpen = fmt.Sprintf("%d/tcp", nextPort) 1907 nextPort++ 1908 unitPortRanges, err = s.unit.OpenedPortRanges() 1909 if err != nil { 1910 return errors.Annotatef(err, "cannot open port ranges") 1911 } 1912 unitPortRanges.Open(allEndpoints, network.MustParsePortRange(toOpen)) 1913 if err = s.State.ApplyOperation(unitPortRanges.Changes()); err != nil { 1914 return errors.Annotatef(err, "cannot open port ranges") 1915 } 1916 return nil 1917 }) 1918 } 1919 1920 func (s *UnitSuite) TestRemoveLastUnitOnMachineRemovesAllPorts(c *gc.C) { 1921 machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits) 1922 c.Assert(err, jc.ErrorIsNil) 1923 err = s.unit.AssignToMachine(machine) 1924 c.Assert(err, jc.ErrorIsNil) 1925 1926 machPortRanges, err := machine.OpenedPortRanges() 1927 c.Assert(err, jc.ErrorIsNil) 1928 c.Assert(machPortRanges.UniquePortRanges(), gc.HasLen, 0) 1929 1930 state.MustOpenUnitPortRange(c, s.State, machine, s.unit.Name(), allEndpoints, network.MustParsePortRange("100-200/tcp")) 1931 1932 machPortRanges, err = machine.OpenedPortRanges() 1933 c.Assert(err, jc.ErrorIsNil) 1934 c.Assert(machPortRanges.UniquePortRanges(), gc.HasLen, 1) 1935 1936 // Now remove the unit and check again. 1937 err = s.unit.EnsureDead() 1938 c.Assert(err, jc.ErrorIsNil) 1939 err = s.unit.Remove() 1940 c.Assert(err, jc.ErrorIsNil) 1941 err = s.unit.Refresh() 1942 c.Assert(err, jc.Satisfies, errors.IsNotFound) 1943 1944 // Because that was the only range open, the ports doc will be 1945 // removed as well. 1946 machPortRanges, err = machine.OpenedPortRanges() 1947 c.Assert(err, jc.ErrorIsNil) 1948 c.Assert(machPortRanges.UniquePortRanges(), gc.HasLen, 0) 1949 } 1950 1951 func (s *UnitSuite) TestRemoveUnitRemovesItsPortsOnly(c *gc.C) { 1952 machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits) 1953 c.Assert(err, jc.ErrorIsNil) 1954 err = machine.SetProvisioned("inst-id", "", "fake_nonce", nil) 1955 c.Assert(err, jc.ErrorIsNil) 1956 err = s.unit.AssignToMachine(machine) 1957 c.Assert(err, jc.ErrorIsNil) 1958 1959 otherUnit, err := s.application.AddUnit(state.AddUnitParams{}) 1960 c.Assert(err, jc.ErrorIsNil) 1961 err = otherUnit.AssignToMachine(machine) 1962 c.Assert(err, jc.ErrorIsNil) 1963 1964 state.MustOpenUnitPortRange(c, s.State, machine, s.unit.Name(), allEndpoints, network.MustParsePortRange("100-200/tcp")) 1965 state.MustOpenUnitPortRange(c, s.State, machine, otherUnit.Name(), allEndpoints, network.MustParsePortRange("300-400/udp")) 1966 1967 machPortRanges, err := machine.OpenedPortRanges() 1968 c.Assert(err, jc.ErrorIsNil) 1969 c.Assert(machPortRanges.UniquePortRanges(), gc.HasLen, 2) 1970 1971 c.Assert(machPortRanges.ForUnit(s.unit.Name()).UniquePortRanges(), jc.DeepEquals, []network.PortRange{ 1972 network.MustParsePortRange("100-200/tcp"), 1973 }) 1974 c.Assert(machPortRanges.ForUnit(otherUnit.Name()).UniquePortRanges(), jc.DeepEquals, []network.PortRange{ 1975 network.MustParsePortRange("300-400/udp"), 1976 }) 1977 1978 // Now remove the first unit and check again. 1979 err = s.unit.EnsureDead() 1980 c.Assert(err, jc.ErrorIsNil) 1981 err = s.unit.Remove() 1982 c.Assert(err, jc.ErrorIsNil) 1983 err = s.unit.Refresh() 1984 c.Assert(err, jc.Satisfies, errors.IsNotFound) 1985 1986 // Verify only otherUnit still has open ports. 1987 machPortRanges, err = machine.OpenedPortRanges() 1988 c.Assert(err, jc.ErrorIsNil) 1989 c.Assert(machPortRanges.UniquePortRanges(), gc.HasLen, 1) 1990 c.Assert(machPortRanges.ForUnit(s.unit.Name()).UniquePortRanges(), gc.HasLen, 0) 1991 c.Assert(machPortRanges.ForUnit(otherUnit.Name()).UniquePortRanges(), jc.DeepEquals, []network.PortRange{ 1992 network.MustParsePortRange("300-400/udp"), 1993 }) 1994 } 1995 1996 func (s *UnitSuite) TestRemoveUnitDeletesUnitState(c *gc.C) { 1997 // Create unit state document 1998 us := state.NewUnitState() 1999 us.SetCharmState(map[string]string{"speed": "ludicrous"}) 2000 err := s.unit.SetState(us, state.UnitStateSizeLimits{}) 2001 c.Assert(err, jc.ErrorIsNil) 2002 2003 coll := s.Session.DB("juju").C("unitstates") 2004 numDocs, err := coll.Count() 2005 c.Assert(err, jc.ErrorIsNil) 2006 c.Assert(numDocs, gc.Equals, 1, gc.Commentf("expected a new document for the unit state to be created")) 2007 2008 // Destroy unit; this should also purge the state doc for the unit 2009 err = s.unit.Destroy() 2010 c.Assert(err, jc.ErrorIsNil) 2011 err = s.unit.EnsureDead() 2012 c.Assert(err, jc.ErrorIsNil) 2013 2014 numDocs, err = coll.Count() 2015 c.Assert(err, jc.ErrorIsNil) 2016 c.Assert(numDocs, gc.Equals, 0, gc.Commentf("expected unit state document to be removed when the unit is destroyed")) 2017 2018 // Any attempts to read/write a unit's state when not Alive should fail 2019 _, err = s.unit.State() 2020 c.Assert(errors.IsNotFound(err), jc.IsTrue) 2021 2022 newUS := state.NewUnitState() 2023 newUS.SetCharmState(map[string]string{"foo": "bar"}) 2024 err = s.unit.SetState(newUS, state.UnitStateSizeLimits{}) 2025 c.Assert(errors.IsNotFound(err), jc.IsTrue) 2026 } 2027 2028 func (s *UnitSuite) TestDestroyAlsoDeletesSecretPermissions(c *gc.C) { 2029 store := state.NewSecrets(s.State) 2030 uri := secrets.NewURI() 2031 cp := state.CreateSecretParams{ 2032 Version: 1, 2033 Owner: s.application.Tag(), 2034 UpdateSecretParams: state.UpdateSecretParams{ 2035 LeaderToken: &fakeToken{}, 2036 Data: map[string]string{"foo": "bar"}, 2037 }, 2038 } 2039 _, err := store.CreateSecret(uri, cp) 2040 c.Assert(err, jc.ErrorIsNil) 2041 2042 // Make a relation for the access scope. 2043 endpoint1, err := s.application.Endpoint("juju-info") 2044 c.Assert(err, jc.ErrorIsNil) 2045 application2 := s.Factory.MakeApplication(c, &factory.ApplicationParams{ 2046 Charm: s.Factory.MakeCharm(c, &factory.CharmParams{ 2047 Name: "logging", 2048 }), 2049 }) 2050 endpoint2, err := application2.Endpoint("info") 2051 c.Assert(err, jc.ErrorIsNil) 2052 rel := s.Factory.MakeRelation(c, &factory.RelationParams{ 2053 Endpoints: []state.Endpoint{endpoint1, endpoint2}, 2054 }) 2055 2056 unit := s.Factory.MakeUnit(c, nil) 2057 err = s.State.GrantSecretAccess(uri, state.SecretAccessParams{ 2058 LeaderToken: &fakeToken{}, 2059 Scope: rel.Tag(), 2060 Subject: unit.Tag(), 2061 Role: secrets.RoleView, 2062 }) 2063 c.Assert(err, jc.ErrorIsNil) 2064 access, err := s.State.SecretAccess(uri, unit.Tag()) 2065 c.Assert(err, jc.ErrorIsNil) 2066 c.Assert(access, gc.Equals, secrets.RoleView) 2067 2068 err = unit.Destroy() 2069 c.Assert(err, jc.ErrorIsNil) 2070 access, err = s.State.SecretAccess(uri, unit.Tag()) 2071 c.Assert(err, jc.ErrorIsNil) 2072 c.Assert(access, gc.Equals, secrets.RoleNone) 2073 } 2074 2075 func (s *UnitSuite) TestDestroyAlsoDeletesOwnedSecrets(c *gc.C) { 2076 store := state.NewSecrets(s.State) 2077 uri := secrets.NewURI() 2078 cp := state.CreateSecretParams{ 2079 Version: 1, 2080 Owner: s.unit.Tag(), 2081 UpdateSecretParams: state.UpdateSecretParams{ 2082 LeaderToken: &fakeToken{}, 2083 Label: ptr("label"), 2084 Data: map[string]string{"foo": "bar"}, 2085 }, 2086 } 2087 _, err := store.CreateSecret(uri, cp) 2088 c.Assert(err, jc.ErrorIsNil) 2089 2090 err = s.unit.Destroy() 2091 c.Assert(err, jc.ErrorIsNil) 2092 _, err = store.GetSecret(uri) 2093 c.Assert(err, jc.Satisfies, errors.IsNotFound) 2094 2095 // Create again, no label clash. 2096 s.unit, err = s.application.AddUnit(state.AddUnitParams{}) 2097 c.Assert(err, jc.ErrorIsNil) 2098 cp.Owner = s.unit.Tag() 2099 _, err = store.CreateSecret(uri, cp) 2100 c.Assert(err, jc.ErrorIsNil) 2101 } 2102 2103 func (s *UnitSuite) TestDestroyAlsoDeletesConsumerInfo(c *gc.C) { 2104 mysql := s.AddTestingApplication(c, "mysql", s.AddTestingCharm(c, "mysql")) 2105 _, err := mysql.AddUnit(state.AddUnitParams{}) 2106 c.Assert(err, jc.ErrorIsNil) 2107 2108 store := state.NewSecrets(s.State) 2109 uri := secrets.NewURI() 2110 cp := state.CreateSecretParams{ 2111 Version: 1, 2112 Owner: names.NewUnitTag("mysql/0"), 2113 UpdateSecretParams: state.UpdateSecretParams{ 2114 LeaderToken: &fakeToken{}, 2115 Label: ptr("label"), 2116 Data: map[string]string{"foo": "bar"}, 2117 }, 2118 } 2119 _, err = store.CreateSecret(uri, cp) 2120 c.Assert(err, jc.ErrorIsNil) 2121 2122 err = s.State.SaveSecretConsumer(uri, s.unit.UnitTag(), &secrets.SecretConsumerMetadata{CurrentRevision: 666}) 2123 c.Assert(err, jc.ErrorIsNil) 2124 2125 err = s.unit.Destroy() 2126 c.Assert(err, jc.ErrorIsNil) 2127 _, err = s.State.GetSecretConsumer(uri, s.unit.Tag()) 2128 c.Assert(err, jc.Satisfies, errors.IsNotFound) 2129 } 2130 2131 func (s *UnitSuite) TestSetClearResolvedWhenNotAlive(c *gc.C) { 2132 preventUnitDestroyRemove(c, s.unit) 2133 err := s.unit.Destroy() 2134 c.Assert(err, jc.ErrorIsNil) 2135 err = s.unit.SetResolved(state.ResolvedNoHooks) 2136 c.Assert(err, jc.ErrorIsNil) 2137 err = s.unit.Refresh() 2138 c.Assert(err, jc.ErrorIsNil) 2139 c.Assert(s.unit.Resolved(), gc.Equals, state.ResolvedNoHooks) 2140 err = s.unit.ClearResolved() 2141 c.Assert(err, jc.ErrorIsNil) 2142 2143 err = s.unit.EnsureDead() 2144 c.Assert(err, jc.ErrorIsNil) 2145 err = s.unit.SetResolved(state.ResolvedRetryHooks) 2146 c.Assert(err, gc.ErrorMatches, deadErr) 2147 err = s.unit.ClearResolved() 2148 c.Assert(err, jc.ErrorIsNil) 2149 } 2150 2151 func (s *UnitSuite) TestSubordinateChangeInPrincipal(c *gc.C) { 2152 subCharm := s.AddTestingCharm(c, "logging") 2153 for i := 0; i < 2; i++ { 2154 // Note: subordinate units can only be created as a side effect of a 2155 // principal entering scope; and a given principal can only have a 2156 // single subordinate unit of each application. 2157 name := "logging" + strconv.Itoa(i) 2158 s.AddTestingApplication(c, name, subCharm) 2159 eps, err := s.State.InferEndpoints(name, "wordpress") 2160 c.Assert(err, jc.ErrorIsNil) 2161 rel, err := s.State.AddRelation(eps...) 2162 c.Assert(err, jc.ErrorIsNil) 2163 ru, err := rel.Unit(s.unit) 2164 c.Assert(err, jc.ErrorIsNil) 2165 err = ru.EnterScope(nil) 2166 c.Assert(err, jc.ErrorIsNil) 2167 } 2168 2169 err := s.unit.Refresh() 2170 c.Assert(err, jc.ErrorIsNil) 2171 subordinates := s.unit.SubordinateNames() 2172 c.Assert(subordinates, gc.DeepEquals, []string{"logging0/0", "logging1/0"}) 2173 2174 su1, err := s.State.Unit("logging1/0") 2175 c.Assert(err, jc.ErrorIsNil) 2176 err = su1.EnsureDead() 2177 c.Assert(err, jc.ErrorIsNil) 2178 err = su1.Remove() 2179 c.Assert(err, jc.ErrorIsNil) 2180 err = s.unit.Refresh() 2181 c.Assert(err, jc.ErrorIsNil) 2182 subordinates = s.unit.SubordinateNames() 2183 c.Assert(subordinates, gc.DeepEquals, []string{"logging0/0"}) 2184 } 2185 2186 func (s *UnitSuite) TestDeathWithSubordinates(c *gc.C) { 2187 // Check that units can become dead when they've never had subordinates. 2188 u, err := s.application.AddUnit(state.AddUnitParams{}) 2189 c.Assert(err, jc.ErrorIsNil) 2190 err = u.EnsureDead() 2191 c.Assert(err, jc.ErrorIsNil) 2192 2193 // Create a new unit and add a subordinate. 2194 u, err = s.application.AddUnit(state.AddUnitParams{}) 2195 c.Assert(err, jc.ErrorIsNil) 2196 s.AddTestingApplication(c, "logging", s.AddTestingCharm(c, "logging")) 2197 c.Assert(err, jc.ErrorIsNil) 2198 eps, err := s.State.InferEndpoints("logging", "wordpress") 2199 c.Assert(err, jc.ErrorIsNil) 2200 rel, err := s.State.AddRelation(eps...) 2201 c.Assert(err, jc.ErrorIsNil) 2202 ru, err := rel.Unit(u) 2203 c.Assert(err, jc.ErrorIsNil) 2204 err = ru.EnterScope(nil) 2205 c.Assert(err, jc.ErrorIsNil) 2206 2207 // Check the unit cannot become Dead, but can become Dying... 2208 err = u.EnsureDead() 2209 c.Assert(err, gc.Equals, stateerrors.ErrUnitHasSubordinates) 2210 err = u.Destroy() 2211 c.Assert(err, jc.ErrorIsNil) 2212 2213 // ...and that it still can't become Dead now it's Dying. 2214 err = u.EnsureDead() 2215 c.Assert(err, gc.Equals, stateerrors.ErrUnitHasSubordinates) 2216 2217 // Make the subordinate Dead and check the principal still cannot be removed. 2218 sub, err := s.State.Unit("logging/0") 2219 c.Assert(err, jc.ErrorIsNil) 2220 err = sub.EnsureDead() 2221 c.Assert(err, jc.ErrorIsNil) 2222 err = u.EnsureDead() 2223 c.Assert(err, gc.Equals, stateerrors.ErrUnitHasSubordinates) 2224 2225 // remove the subordinate and check the principal can finally become Dead. 2226 err = sub.Remove() 2227 c.Assert(err, jc.ErrorIsNil) 2228 err = u.EnsureDead() 2229 c.Assert(err, jc.ErrorIsNil) 2230 } 2231 2232 func (s *UnitSuite) TestPrincipalName(c *gc.C) { 2233 subCharm := s.AddTestingCharm(c, "logging") 2234 s.AddTestingApplication(c, "logging", subCharm) 2235 eps, err := s.State.InferEndpoints("logging", "wordpress") 2236 c.Assert(err, jc.ErrorIsNil) 2237 rel, err := s.State.AddRelation(eps...) 2238 c.Assert(err, jc.ErrorIsNil) 2239 ru, err := rel.Unit(s.unit) 2240 c.Assert(err, jc.ErrorIsNil) 2241 err = ru.EnterScope(nil) 2242 c.Assert(err, jc.ErrorIsNil) 2243 2244 err = s.unit.Refresh() 2245 c.Assert(err, jc.ErrorIsNil) 2246 subordinates := s.unit.SubordinateNames() 2247 c.Assert(subordinates, gc.DeepEquals, []string{"logging/0"}) 2248 2249 su, err := s.State.Unit("logging/0") 2250 c.Assert(err, jc.ErrorIsNil) 2251 principal, valid := su.PrincipalName() 2252 c.Assert(valid, jc.IsTrue) 2253 c.Assert(principal, gc.Equals, s.unit.Name()) 2254 2255 // Calling PrincipalName on a principal unit yields "", false. 2256 principal, valid = s.unit.PrincipalName() 2257 c.Assert(valid, jc.IsFalse) 2258 c.Assert(principal, gc.Equals, "") 2259 } 2260 2261 func (s *UnitSuite) TestConstraintsDefaultArchNotRelevant(c *gc.C) { 2262 app := s.Factory.MakeApplication(c, &factory.ApplicationParams{ 2263 Name: "app", 2264 CharmOrigin: &state.CharmOrigin{Platform: &state.Platform{ 2265 Architecture: "arm64", 2266 OS: "ubuntu", 2267 Channel: "22.04", 2268 }}, 2269 }) 2270 err := app.SetConstraints(constraints.MustParse("instance-type=big")) 2271 c.Assert(err, jc.ErrorIsNil) 2272 unit0, err := app.AddUnit(state.AddUnitParams{}) 2273 c.Assert(err, jc.ErrorIsNil) 2274 cons, err := unit0.Constraints() 2275 c.Assert(err, jc.ErrorIsNil) 2276 c.Assert(cons.String(), gc.Equals, "instance-type=big") 2277 2278 app = s.Factory.MakeApplication(c, &factory.ApplicationParams{ 2279 Name: "app2", 2280 CharmOrigin: &state.CharmOrigin{Platform: &state.Platform{ 2281 Architecture: "arm64", 2282 OS: "ubuntu", 2283 Channel: "22.04", 2284 }}, 2285 Constraints: constraints.MustParse("arch=s390x"), 2286 }) 2287 unit1, err := app.AddUnit(state.AddUnitParams{}) 2288 c.Assert(err, jc.ErrorIsNil) 2289 cons, err = unit1.Constraints() 2290 c.Assert(err, jc.ErrorIsNil) 2291 c.Assert(cons.String(), gc.Equals, "arch=s390x") 2292 } 2293 2294 func (s *UnitSuite) TestConstraintsDefaultArch(c *gc.C) { 2295 app := s.Factory.MakeApplication(c, &factory.ApplicationParams{ 2296 Name: "app", 2297 CharmOrigin: &state.CharmOrigin{Platform: &state.Platform{ 2298 Architecture: "arm64", 2299 OS: "ubuntu", 2300 Channel: "22.04", 2301 }}, 2302 }) 2303 err := app.SetConstraints(constraints.MustParse("mem=4G")) 2304 c.Assert(err, jc.ErrorIsNil) 2305 unit0, err := app.AddUnit(state.AddUnitParams{}) 2306 c.Assert(err, jc.ErrorIsNil) 2307 cons, err := unit0.Constraints() 2308 c.Assert(err, jc.ErrorIsNil) 2309 c.Assert(cons.String(), gc.Equals, "arch=arm64 mem=4096M") 2310 } 2311 2312 func (s *UnitSuite) TestRelations(c *gc.C) { 2313 wordpress0 := s.unit 2314 mysql := s.AddTestingApplication(c, "mysql", s.AddTestingCharm(c, "mysql")) 2315 mysql0, err := mysql.AddUnit(state.AddUnitParams{}) 2316 c.Assert(err, jc.ErrorIsNil) 2317 eps, err := s.State.InferEndpoints("wordpress", "mysql") 2318 c.Assert(err, jc.ErrorIsNil) 2319 rel, err := s.State.AddRelation(eps...) 2320 c.Assert(err, jc.ErrorIsNil) 2321 2322 assertEquals := func(actual, expect []*state.Relation) { 2323 c.Assert(actual, gc.HasLen, len(expect)) 2324 for i, a := range actual { 2325 c.Assert(a.Id(), gc.Equals, expect[i].Id()) 2326 } 2327 } 2328 assertRelationsJoined := func(unit *state.Unit, expect ...*state.Relation) { 2329 actual, err := unit.RelationsJoined() 2330 c.Assert(err, jc.ErrorIsNil) 2331 assertEquals(actual, expect) 2332 } 2333 assertRelationsInScope := func(unit *state.Unit, expect ...*state.Relation) { 2334 actual, err := unit.RelationsInScope() 2335 c.Assert(err, jc.ErrorIsNil) 2336 assertEquals(actual, expect) 2337 } 2338 assertRelations := func(unit *state.Unit, expect ...*state.Relation) { 2339 assertRelationsInScope(unit, expect...) 2340 assertRelationsJoined(unit, expect...) 2341 } 2342 assertRelations(wordpress0) 2343 assertRelations(mysql0) 2344 2345 mysql0ru, err := rel.Unit(mysql0) 2346 c.Assert(err, jc.ErrorIsNil) 2347 err = mysql0ru.EnterScope(nil) 2348 c.Assert(err, jc.ErrorIsNil) 2349 assertRelations(wordpress0) 2350 assertRelations(mysql0, rel) 2351 2352 wordpress0ru, err := rel.Unit(wordpress0) 2353 c.Assert(err, jc.ErrorIsNil) 2354 err = wordpress0ru.EnterScope(nil) 2355 c.Assert(err, jc.ErrorIsNil) 2356 assertRelations(wordpress0, rel) 2357 assertRelations(mysql0, rel) 2358 2359 err = mysql0ru.PrepareLeaveScope() 2360 c.Assert(err, jc.ErrorIsNil) 2361 assertRelations(wordpress0, rel) 2362 assertRelationsInScope(mysql0, rel) 2363 assertRelationsJoined(mysql0) 2364 } 2365 2366 func (s *UnitSuite) TestRemove(c *gc.C) { 2367 err := s.unit.Remove() 2368 c.Assert(err, gc.ErrorMatches, `cannot remove unit "wordpress/0": unit is not dead`) 2369 err = s.unit.EnsureDead() 2370 c.Assert(err, jc.ErrorIsNil) 2371 err = s.unit.Remove() 2372 c.Assert(err, jc.ErrorIsNil) 2373 err = s.unit.Refresh() 2374 c.Assert(err, jc.Satisfies, errors.IsNotFound) 2375 units, err := s.application.AllUnits() 2376 c.Assert(err, jc.ErrorIsNil) 2377 c.Assert(units, gc.HasLen, 0) 2378 err = s.unit.Remove() 2379 c.Assert(err, jc.ErrorIsNil) 2380 } 2381 2382 func (s *UnitSuite) TestRemoveUnassignsFromBranch(c *gc.C) { 2383 // Add unit to a branch 2384 c.Assert(s.Model.AddBranch("apple", "testuser"), jc.ErrorIsNil) 2385 branch, err := s.Model.Branch("apple") 2386 c.Assert(err, jc.ErrorIsNil) 2387 c.Assert(branch.AssignUnit(s.unit.Name()), jc.ErrorIsNil) 2388 c.Assert(branch.Refresh(), jc.ErrorIsNil) 2389 c.Assert(branch.AssignedUnits(), gc.DeepEquals, map[string][]string{ 2390 s.application.Name(): {s.unit.Name()}, 2391 }) 2392 2393 // remove the unit 2394 c.Assert(s.unit.EnsureDead(), jc.ErrorIsNil) 2395 c.Assert(s.unit.Remove(), jc.ErrorIsNil) 2396 2397 // verify branch no longer tracks unit 2398 c.Assert(branch.Refresh(), jc.ErrorIsNil) 2399 c.Assert(branch.AssignedUnits(), gc.DeepEquals, map[string][]string{ 2400 s.application.Name(): {}, 2401 }) 2402 } 2403 2404 func (s *UnitSuite) TestRemovePathological(c *gc.C) { 2405 // Add a relation between wordpress and mysql... 2406 wordpress := s.application 2407 wordpress0 := s.unit 2408 mysql := s.AddTestingApplication(c, "mysql", s.AddTestingCharm(c, "mysql")) 2409 eps, err := s.State.InferEndpoints("wordpress", "mysql") 2410 c.Assert(err, jc.ErrorIsNil) 2411 rel, err := s.State.AddRelation(eps...) 2412 c.Assert(err, jc.ErrorIsNil) 2413 2414 // The relation holds a reference to wordpress, but that can't keep 2415 // wordpress from being removed -- because the relation will be removed 2416 // if we destroy wordpress. 2417 // However, if a unit of the *other* application joins the relation, that 2418 // will add an additional reference and prevent the relation -- and 2419 // thus wordpress itself -- from being removed when its last unit is. 2420 mysql0, err := mysql.AddUnit(state.AddUnitParams{}) 2421 c.Assert(err, jc.ErrorIsNil) 2422 mysql0ru, err := rel.Unit(mysql0) 2423 c.Assert(err, jc.ErrorIsNil) 2424 c.Assert(mysql0ru.EnterScope(nil), jc.ErrorIsNil) 2425 2426 // Destroy wordpress, and remove its last unit. 2427 c.Assert(wordpress.Destroy(), jc.ErrorIsNil) 2428 c.Assert(wordpress0.EnsureDead(), jc.ErrorIsNil) 2429 c.Assert(wordpress0.Remove(), jc.ErrorIsNil) 2430 2431 // Check this didn't kill the application or relation yet... 2432 c.Assert(wordpress.Refresh(), jc.ErrorIsNil) 2433 c.Assert(rel.Refresh(), jc.ErrorIsNil) 2434 2435 // ...but when the unit on the other side departs the relation, the 2436 // relation and the other application are cleaned up. 2437 c.Assert(mysql0ru.LeaveScope(), jc.ErrorIsNil) 2438 c.Assert(s.State.Cleanup(), jc.ErrorIsNil) 2439 c.Assert(wordpress.Refresh(), jc.Satisfies, errors.IsNotFound) 2440 c.Assert(rel.Refresh(), jc.Satisfies, errors.IsNotFound) 2441 } 2442 2443 func (s *UnitSuite) TestRemovePathologicalWithBuggyUniter(c *gc.C) { 2444 // Add a relation between wordpress and mysql... 2445 wordpress := s.application 2446 wordpress0 := s.unit 2447 mysql := s.AddTestingApplication(c, "mysql", s.AddTestingCharm(c, "mysql")) 2448 eps, err := s.State.InferEndpoints("wordpress", "mysql") 2449 c.Assert(err, jc.ErrorIsNil) 2450 rel, err := s.State.AddRelation(eps...) 2451 c.Assert(err, jc.ErrorIsNil) 2452 2453 // The relation holds a reference to wordpress, but that can't keep 2454 // wordpress from being removed -- because the relation will be removed 2455 // if we destroy wordpress. 2456 // However, if a unit of the *other* application joins the relation, that 2457 // will add an additional reference and prevent the relation -- and 2458 // thus wordpress itself -- from being removed when its last unit is. 2459 mysql0, err := mysql.AddUnit(state.AddUnitParams{}) 2460 c.Assert(err, jc.ErrorIsNil) 2461 mysql0ru, err := rel.Unit(mysql0) 2462 c.Assert(err, jc.ErrorIsNil) 2463 err = mysql0ru.EnterScope(nil) 2464 c.Assert(err, jc.ErrorIsNil) 2465 2466 // Destroy wordpress, and remove its last unit. 2467 c.Assert(wordpress.Destroy(), jc.ErrorIsNil) 2468 c.Assert(wordpress0.EnsureDead(), jc.ErrorIsNil) 2469 c.Assert(wordpress0.Remove(), jc.ErrorIsNil) 2470 2471 // Check this didn't kill the application or relation yet... 2472 c.Assert(wordpress.Refresh(), jc.ErrorIsNil) 2473 c.Assert(rel.Refresh(), jc.ErrorIsNil) 2474 2475 // ...and that when the malfunctioning unit agent on the other side 2476 // sets itself to dead *without* departing the relation, the unit's 2477 // removal causes the relation and the other application to be cleaned up. 2478 c.Assert(mysql0.EnsureDead(), jc.ErrorIsNil) 2479 c.Assert(mysql0.Remove(), jc.ErrorIsNil) 2480 c.Assert(s.State.Cleanup(), jc.ErrorIsNil) 2481 c.Assert(wordpress.Refresh(), jc.Satisfies, errors.IsNotFound) 2482 c.Assert(rel.Refresh(), jc.Satisfies, errors.IsNotFound) 2483 } 2484 2485 func (s *UnitSuite) TestWatchSubordinates(c *gc.C) { 2486 // TODO(mjs) - ModelUUID - test with multiple models with 2487 // identically named units and ensure there's no leakage. 2488 s.WaitForModelWatchersIdle(c, s.Model.UUID()) 2489 w := s.unit.WatchSubordinateUnits() 2490 defer testing.AssertStop(c, w) 2491 wc := testing.NewStringsWatcherC(c, w) 2492 wc.AssertChange() 2493 wc.AssertNoChange() 2494 2495 // Add a couple of subordinates, check change. 2496 subCharm := s.AddTestingCharm(c, "logging") 2497 var subUnits []*state.Unit 2498 for i := 0; i < 2; i++ { 2499 // Note: subordinate units can only be created as a side effect of a 2500 // principal entering scope; and a given principal can only have a 2501 // single subordinate unit of each application. 2502 name := "logging" + strconv.Itoa(i) 2503 subApp := s.AddTestingApplication(c, name, subCharm) 2504 eps, err := s.State.InferEndpoints(name, "wordpress") 2505 c.Assert(err, jc.ErrorIsNil) 2506 rel, err := s.State.AddRelation(eps...) 2507 c.Assert(err, jc.ErrorIsNil) 2508 ru, err := rel.Unit(s.unit) 2509 c.Assert(err, jc.ErrorIsNil) 2510 err = ru.EnterScope(nil) 2511 c.Assert(err, jc.ErrorIsNil) 2512 units, err := subApp.AllUnits() 2513 c.Assert(err, jc.ErrorIsNil) 2514 c.Assert(units, gc.HasLen, 1) 2515 subUnits = append(subUnits, units[0]) 2516 } 2517 // In order to ensure that both the subordinate creation events 2518 // have all been processed, wait for the model to be idle. 2519 s.WaitForModelWatchersIdle(c, s.Model.UUID()) 2520 wc.AssertChange(subUnits[0].Name(), subUnits[1].Name()) 2521 wc.AssertNoChange() 2522 2523 // Set one to Dying, check change. 2524 err := subUnits[0].Destroy() 2525 c.Assert(err, jc.ErrorIsNil) 2526 wc.AssertChange(subUnits[0].Name()) 2527 wc.AssertNoChange() 2528 2529 // Set both to Dead, and remove one; check change. 2530 err = subUnits[0].EnsureDead() 2531 c.Assert(err, jc.ErrorIsNil) 2532 err = subUnits[1].EnsureDead() 2533 c.Assert(err, jc.ErrorIsNil) 2534 err = subUnits[1].Remove() 2535 c.Assert(err, jc.ErrorIsNil) 2536 // In order to ensure that both the dead and remove operations 2537 // have been processed, we need to wait until the model is idle. 2538 s.WaitForModelWatchersIdle(c, s.Model.UUID()) 2539 wc.AssertChange(subUnits[0].Name(), subUnits[1].Name()) 2540 wc.AssertNoChange() 2541 2542 // Stop watcher, check closed. 2543 testing.AssertStop(c, w) 2544 wc.AssertClosed() 2545 2546 // Start a new watch, check Dead unit is reported. 2547 w = s.unit.WatchSubordinateUnits() 2548 defer testing.AssertStop(c, w) 2549 wc = testing.NewStringsWatcherC(c, w) 2550 wc.AssertChange(subUnits[0].Name()) 2551 wc.AssertNoChange() 2552 2553 // Remove the leftover, check no change. 2554 err = subUnits[0].Remove() 2555 c.Assert(err, jc.ErrorIsNil) 2556 wc.AssertNoChange() 2557 } 2558 2559 func (s *UnitSuite) TestWatchUnits(c *gc.C) { 2560 loggo.GetLogger("juju.state.pool.txnwatcher").SetLogLevel(loggo.TRACE) 2561 loggo.GetLogger("juju.state.watcher").SetLogLevel(loggo.TRACE) 2562 2563 s.WaitForModelWatchersIdle(c, s.Model.UUID()) 2564 w := s.State.WatchUnits() 2565 defer testing.AssertStop(c, w) 2566 2567 // Initial event for unit created in test setup. 2568 wc := testing.NewStringsWatcherC(c, w) 2569 wc.AssertChange("wordpress/0") 2570 2571 u, err := s.application.AddUnit(state.AddUnitParams{}) 2572 c.Assert(err, jc.ErrorIsNil) 2573 wc.AssertChange(u.Name()) 2574 err = u.AssignToNewMachine() 2575 c.Assert(err, jc.ErrorIsNil) 2576 wc.AssertChange(u.Name()) 2577 2578 err = u.EnsureDead() 2579 c.Assert(err, jc.ErrorIsNil) 2580 wc.AssertChange(u.Name()) 2581 2582 // Stop, check closed. 2583 testing.AssertStop(c, w) 2584 wc.AssertClosed() 2585 } 2586 2587 func (s *UnitSuite) TestWatchUnit(c *gc.C) { 2588 loggo.GetLogger("juju.state.pool.txnwatcher").SetLogLevel(loggo.TRACE) 2589 loggo.GetLogger("juju.state.watcher").SetLogLevel(loggo.TRACE) 2590 2591 s.WaitForModelWatchersIdle(c, s.Model.UUID()) 2592 w := s.unit.Watch() 2593 defer testing.AssertStop(c, w) 2594 2595 // Initial event. 2596 wc := testing.NewNotifyWatcherC(c, w) 2597 wc.AssertOneChange() 2598 2599 // Make one change (to a separate instance), check one event. 2600 unit, err := s.State.Unit(s.unit.Name()) 2601 c.Assert(err, jc.ErrorIsNil) 2602 s.setAssignedMachineAddresses(c, unit) 2603 wc.AssertOneChange() 2604 2605 // Make two changes, check one event. 2606 err = unit.SetPassword("arble-farble-dying-yarble") 2607 c.Assert(err, jc.ErrorIsNil) 2608 // TODO(quiescence): these two changes should be one event. 2609 wc.AssertOneChange() 2610 preventUnitDestroyRemove(c, unit) 2611 err = unit.Destroy() 2612 c.Assert(err, jc.ErrorIsNil) 2613 wc.AssertOneChange() 2614 2615 // Stop, check closed. 2616 testing.AssertStop(c, w) 2617 wc.AssertClosed() 2618 2619 // Remove unit, start new watch, check single event. 2620 err = unit.EnsureDead() 2621 c.Assert(err, jc.ErrorIsNil) 2622 err = unit.Remove() 2623 c.Assert(err, jc.ErrorIsNil) 2624 s.WaitForModelWatchersIdle(c, s.Model.UUID()) 2625 w = s.unit.Watch() 2626 defer testing.AssertStop(c, w) 2627 testing.NewNotifyWatcherC(c, w).AssertOneChange() 2628 } 2629 2630 func (s *UnitSuite) TestUnitAgentTools(c *gc.C) { 2631 preventUnitDestroyRemove(c, s.unit) 2632 testAgentTools(c, s.unit, `unit "wordpress/0"`) 2633 } 2634 2635 func (s *UnitSuite) TestValidActionsAndSpecs(c *gc.C) { 2636 basicActions := ` 2637 snapshot: 2638 params: 2639 outfile: 2640 type: string 2641 default: "abcd" 2642 `[1:] 2643 2644 wordpress := s.AddTestingApplication(c, "wordpress-actions", s.AddActionsCharm(c, "wordpress", basicActions, 1)) 2645 unit1, err := wordpress.AddUnit(state.AddUnitParams{}) 2646 c.Assert(err, jc.ErrorIsNil) 2647 specs, err := unit1.ActionSpecs() 2648 c.Assert(err, jc.ErrorIsNil) 2649 c.Check(specs, jc.DeepEquals, state.ActionSpecsByName{ 2650 "snapshot": charm.ActionSpec{ 2651 Description: "No description", 2652 Params: map[string]interface{}{ 2653 "type": "object", 2654 "title": "snapshot", 2655 "description": "No description", 2656 "properties": map[string]interface{}{ 2657 "outfile": map[string]interface{}{ 2658 "type": "string", 2659 "default": "abcd", 2660 }, 2661 }, 2662 }, 2663 }, 2664 }) 2665 2666 var tests = []struct { 2667 actionName string 2668 errString string 2669 givenPayload map[string]interface{} 2670 expectedPayload map[string]interface{} 2671 }{ 2672 { 2673 actionName: "snapshot", 2674 expectedPayload: map[string]interface{}{"outfile": "abcd"}, 2675 }, 2676 { 2677 actionName: "juju-exec", 2678 errString: `validation failed: \(root\) : "command" property is missing and required, given \{\}; \(root\) : "timeout" property is missing and required, given \{\}`, 2679 }, 2680 { 2681 actionName: "juju-exec", 2682 givenPayload: map[string]interface{}{"command": "allyourbasearebelongtous"}, 2683 errString: `validation failed: \(root\) : "timeout" property is missing and required, given \{"command":"allyourbasearebelongtous"\}`, 2684 }, 2685 { 2686 actionName: "juju-exec", 2687 givenPayload: map[string]interface{}{"timeout": 5 * time.Second}, 2688 // Note: in Go 1.8 the representation of large numbers in JSON changed 2689 // to use integer rather than exponential notation, hence the pattern. 2690 errString: `validation failed: \(root\) : "command" property is missing and required, given \{"timeout":5.*\}`, 2691 }, 2692 { 2693 actionName: "juju-exec", 2694 givenPayload: map[string]interface{}{"command": "allyourbasearebelongtous", "timeout": 5.0}, 2695 expectedPayload: map[string]interface{}{"command": "allyourbasearebelongtous", "timeout": 5.0}, 2696 }, 2697 { 2698 actionName: "baiku", 2699 errString: `action "baiku" not defined on unit "wordpress-actions/0"`, 2700 }, 2701 } 2702 2703 for i, t := range tests { 2704 c.Logf("running test %d", i) 2705 operationID, err := s.Model.EnqueueOperation("a test", 1) 2706 c.Assert(err, jc.ErrorIsNil) 2707 action, err := s.Model.AddAction(unit1, operationID, t.actionName, t.givenPayload, nil, nil) 2708 if t.errString != "" { 2709 c.Assert(err, gc.ErrorMatches, t.errString) 2710 } else { 2711 c.Assert(err, jc.ErrorIsNil) 2712 c.Assert(action.Parameters(), jc.DeepEquals, t.expectedPayload) 2713 c.Assert(state.ActionOperationId(action), gc.Equals, operationID) 2714 } 2715 } 2716 } 2717 2718 func (s *UnitSuite) TestAddActionWithError(c *gc.C) { 2719 operationID, err := s.Model.EnqueueOperation("a test", 1) 2720 c.Assert(err, jc.ErrorIsNil) 2721 _, err = s.Model.AddAction(s.unit, operationID, "benchmark", nil, nil, nil) 2722 c.Assert(err, gc.ErrorMatches, `action "benchmark" not defined on unit "wordpress/0"`) 2723 op, err := s.Model.Operation(operationID) 2724 c.Assert(err, jc.ErrorIsNil) 2725 c.Assert(op.Status(), gc.Equals, state.ActionError) 2726 } 2727 2728 func (s *UnitSuite) TestUnitActionsFindsRightActions(c *gc.C) { 2729 // An actions.yaml which permits actions by the following names 2730 basicActions := ` 2731 action-a-a: 2732 action-a-b: 2733 action-a-c: 2734 action-b-a: 2735 action-b-b: 2736 `[1:] 2737 2738 // Add simple application and two units 2739 dummy := s.AddTestingApplication(c, "dummy", s.AddActionsCharm(c, "dummy", basicActions, 1)) 2740 2741 unit1, err := dummy.AddUnit(state.AddUnitParams{}) 2742 c.Assert(err, jc.ErrorIsNil) 2743 2744 unit2, err := dummy.AddUnit(state.AddUnitParams{}) 2745 c.Assert(err, jc.ErrorIsNil) 2746 2747 // Add 3 actions to first unit, and 2 to the second unit 2748 operationID, err := s.Model.EnqueueOperation("a test", 5) 2749 c.Assert(err, jc.ErrorIsNil) 2750 _, err = s.Model.AddAction(unit1, operationID, "action-a-a", nil, nil, nil) 2751 c.Assert(err, jc.ErrorIsNil) 2752 _, err = s.Model.AddAction(unit1, operationID, "action-a-b", nil, nil, nil) 2753 c.Assert(err, jc.ErrorIsNil) 2754 _, err = s.Model.AddAction(unit1, operationID, "action-a-c", nil, nil, nil) 2755 c.Assert(err, jc.ErrorIsNil) 2756 2757 _, err = s.Model.AddAction(unit2, operationID, "action-b-a", nil, nil, nil) 2758 c.Assert(err, jc.ErrorIsNil) 2759 _, err = s.Model.AddAction(unit2, operationID, "action-b-b", nil, nil, nil) 2760 c.Assert(err, jc.ErrorIsNil) 2761 2762 // Verify that calling Actions on unit1 returns only 2763 // the three actions added to unit1 2764 actions1, err := unit1.Actions() 2765 c.Assert(err, jc.ErrorIsNil) 2766 c.Assert(len(actions1), gc.Equals, 3) 2767 for _, action := range actions1 { 2768 c.Assert(action.Name(), gc.Matches, "^action-a-.") 2769 } 2770 2771 // Verify that calling Actions on unit2 returns only 2772 // the two actions added to unit2 2773 actions2, err := unit2.Actions() 2774 c.Assert(err, jc.ErrorIsNil) 2775 c.Assert(len(actions2), gc.Equals, 2) 2776 for _, action := range actions2 { 2777 c.Assert(action.Name(), gc.Matches, "^action-b-.") 2778 } 2779 } 2780 2781 func (s *UnitSuite) TestWorkloadVersion(c *gc.C) { 2782 ch := state.AddTestingCharm(c, s.State, "dummy") 2783 app := state.AddTestingApplication(c, s.State, "alexandrite", ch) 2784 unit, err := app.AddUnit(state.AddUnitParams{}) 2785 c.Assert(err, jc.ErrorIsNil) 2786 2787 version, err := unit.WorkloadVersion() 2788 c.Assert(err, jc.ErrorIsNil) 2789 c.Check(version, gc.Equals, "") 2790 2791 err = unit.SetWorkloadVersion("3.combined") 2792 c.Assert(err, jc.ErrorIsNil) 2793 version, err = unit.WorkloadVersion() 2794 c.Assert(err, jc.ErrorIsNil) 2795 c.Check(version, gc.Equals, "3.combined") 2796 2797 regotUnit, err := s.State.Unit("alexandrite/0") 2798 c.Assert(err, jc.ErrorIsNil) 2799 version, err = regotUnit.WorkloadVersion() 2800 c.Assert(err, jc.ErrorIsNil) 2801 c.Check(version, gc.Equals, "3.combined") 2802 } 2803 2804 func (s *UnitSuite) TestDestroyWithForceWorksOnDyingUnit(c *gc.C) { 2805 // Ensure that a cleanup is scheduled if we force destroy a unit 2806 // that's already dying. 2807 ch := state.AddTestingCharm(c, s.State, "dummy") 2808 app := state.AddTestingApplication(c, s.State, "alexandrite", ch) 2809 unit, err := app.AddUnit(state.AddUnitParams{}) 2810 c.Assert(err, jc.ErrorIsNil) 2811 // Assign the unit to a machine and set the agent status so 2812 // removal can't be short-circuited. 2813 err = s.State.AssignUnit(unit, state.AssignCleanEmpty) 2814 c.Assert(err, jc.ErrorIsNil) 2815 err = unit.SetAgentStatus(status.StatusInfo{ 2816 Status: status.Idle, 2817 }) 2818 c.Assert(err, jc.ErrorIsNil) 2819 2820 err = unit.Destroy() 2821 c.Assert(err, jc.ErrorIsNil) 2822 2823 c.Assert(unit.Life(), gc.Equals, state.Dying) 2824 2825 needsCleanup, err := s.State.NeedsCleanup() 2826 c.Assert(err, jc.ErrorIsNil) 2827 c.Assert(needsCleanup, gc.Equals, true) 2828 2829 err = s.State.Cleanup() 2830 c.Assert(err, jc.ErrorIsNil) 2831 needsCleanup, err = s.State.NeedsCleanup() 2832 c.Assert(err, jc.ErrorIsNil) 2833 c.Assert(needsCleanup, gc.Equals, false) 2834 2835 // Force-destroying the unit should schedule a cleanup so we get a 2836 // chance for the fallback force-cleanup to run. 2837 opErrs, err := unit.DestroyWithForce(true, dontWait) 2838 c.Assert(err, jc.ErrorIsNil) 2839 c.Assert(opErrs, gc.IsNil) 2840 2841 // We scheduled the dying unit cleanup again even though the unit was dying already. 2842 needsCleanup, err = s.State.NeedsCleanup() 2843 c.Assert(err, jc.ErrorIsNil) 2844 c.Assert(needsCleanup, gc.Equals, true) 2845 } 2846 2847 func (s *UnitSuite) TestWatchMachineAndEndpointAddressesHash(c *gc.C) { 2848 // Create 2 spaces 2849 sn1, err := s.State.AddSubnet(network.SubnetInfo{CIDR: "10.0.0.0/24"}) 2850 c.Assert(err, gc.IsNil) 2851 sn2, err := s.State.AddSubnet(network.SubnetInfo{CIDR: "10.0.254.0/24"}) 2852 c.Assert(err, gc.IsNil) 2853 2854 _, err = s.State.AddSpace("public", "", []string{sn1.ID()}, false) 2855 c.Assert(err, gc.IsNil) 2856 _, err = s.State.AddSpace("private", "", []string{sn2.ID()}, false) 2857 c.Assert(err, gc.IsNil) 2858 2859 // Create machine with 2 interfaces on the above spaces 2860 m1, err := s.State.AddOneMachine(state.MachineTemplate{ 2861 Base: state.UbuntuBase("12.10"), 2862 Jobs: []state.MachineJob{state.JobHostUnits}, 2863 Constraints: constraints.MustParse("spaces=public,private"), 2864 }) 2865 c.Assert(err, gc.IsNil) 2866 err = m1.SetLinkLayerDevices( 2867 state.LinkLayerDeviceArgs{Name: "enp5s0", Type: network.EthernetDevice}, 2868 state.LinkLayerDeviceArgs{Name: "enp5s1", Type: network.EthernetDevice}, 2869 ) 2870 c.Assert(err, gc.IsNil) 2871 err = m1.SetDevicesAddresses( 2872 state.LinkLayerDeviceAddress{DeviceName: "enp5s0", CIDRAddress: "10.0.0.1/24", ConfigMethod: network.ConfigStatic}, 2873 state.LinkLayerDeviceAddress{DeviceName: "enp5s1", CIDRAddress: "10.0.254.42/24", ConfigMethod: network.ConfigStatic}, 2874 ) 2875 c.Assert(err, gc.IsNil) 2876 2877 // Deploy unit to machine 2878 ch := s.AddMetaCharm(c, "mysql", metaExtraEndpoints, 1) 2879 app := state.AddTestingApplicationWithBindings(c, s.State, "mysql", ch, map[string]string{ 2880 "server": "public", 2881 "foo": "private", 2882 }) 2883 unit, err := app.AddUnit(state.AddUnitParams{}) 2884 c.Assert(err, gc.IsNil) 2885 err = unit.AssignToMachine(m1) 2886 c.Assert(err, gc.IsNil) 2887 2888 // Create watcher 2889 w, err := unit.WatchMachineAndEndpointAddressesHash() 2890 c.Assert(err, gc.IsNil) 2891 defer func() { _ = w.Stop() }() 2892 2893 // The watcher will emit the original hash. 2894 wc := testing.NewStringsWatcherC(c, w) 2895 wc.AssertChange("b1b30f7f8b818a0ef59e858ab0e409a33ebe9eefead686f7a0f1d1ef7a11cf0e") 2896 2897 // Adding a new machine address should trigger a change 2898 err = m1.SetDevicesAddresses(state.LinkLayerDeviceAddress{ 2899 DeviceName: "enp5s0", 2900 CIDRAddress: "10.0.0.100/24", 2901 ConfigMethod: network.ConfigStatic, 2902 }) 2903 c.Assert(err, gc.IsNil) 2904 err = m1.SetProviderAddresses(network.NewSpaceAddress("10.0.0.100")) 2905 c.Assert(err, gc.IsNil) 2906 wc.AssertChange("46ed851765a963e100161210a7b4fbb28d59b24edb580a60f86dbbaebea14d37") 2907 2908 // Changing the application bindings after an upgrade should trigger a change 2909 sch := s.AddMetaCharm(c, "mysql", metaExtraEndpoints, 2) 2910 cfg := state.SetCharmConfig{ 2911 Charm: sch, 2912 CharmOrigin: defaultCharmOrigin(sch.URL()), 2913 ForceUnits: true, 2914 EndpointBindings: map[string]string{ 2915 "server": "private", 2916 }, 2917 } 2918 err = app.SetCharm(cfg) 2919 c.Assert(err, jc.ErrorIsNil) 2920 wc.AssertChange("c895e8b57123efd2194d48b74db431e4db4c3ae4fa75f55aa6f32c7f39f29abd") 2921 } 2922 2923 func unitMachine(c *gc.C, st *state.State, u *state.Unit) *state.Machine { 2924 machineId, err := u.AssignedMachineId() 2925 c.Assert(err, jc.ErrorIsNil) 2926 machine, err := st.Machine(machineId) 2927 c.Assert(err, jc.ErrorIsNil) 2928 return machine 2929 } 2930 2931 type CAASUnitSuite struct { 2932 ConnSuite 2933 application *state.Application 2934 operatorApp *state.Application 2935 } 2936 2937 var _ = gc.Suite(&CAASUnitSuite{}) 2938 2939 func (s *CAASUnitSuite) SetUpTest(c *gc.C) { 2940 s.ConnSuite.SetUpTest(c) 2941 st := s.Factory.MakeCAASModel(c, nil) 2942 s.AddCleanup(func(_ *gc.C) { st.Close() }) 2943 2944 var err error 2945 s.Model, err = st.Model() 2946 c.Assert(err, jc.ErrorIsNil) 2947 2948 f := factory.NewFactory(st, s.StatePool) 2949 ch := f.MakeCharm(c, &factory.CharmParams{Name: "gitlab", Series: "kubernetes"}) 2950 s.application = f.MakeApplication(c, &factory.ApplicationParams{ 2951 Name: "gitlab", Charm: ch, 2952 CharmOrigin: &state.CharmOrigin{Platform: &state.Platform{OS: "ubuntu", Channel: "20.04/stable"}}, 2953 }) 2954 2955 basicActions := ` 2956 snapshot: 2957 params: 2958 outfile: 2959 type: string 2960 default: "abcd" 2961 `[1:] 2962 ch = state.AddCustomCharm(c, st, "elastic-operator", "actions.yaml", basicActions, "kubernetes", 1) 2963 s.operatorApp = f.MakeApplication(c, &factory.ApplicationParams{Charm: ch}) 2964 } 2965 2966 func (s *CAASUnitSuite) TestShortCircuitDestroyUnit(c *gc.C) { 2967 // A unit that has not been allocated is removed directly. 2968 unit, err := s.application.AddUnit(state.AddUnitParams{}) 2969 c.Assert(err, jc.ErrorIsNil) 2970 c.Assert(unit.Base(), jc.DeepEquals, state.Base{OS: "ubuntu", Channel: "20.04/stable"}) 2971 c.Assert(unit.ShouldBeAssigned(), jc.IsFalse) 2972 2973 // A unit that has not set any status is removed directly. 2974 err = unit.Destroy() 2975 c.Assert(err, jc.ErrorIsNil) 2976 c.Assert(unit.Life(), gc.Equals, state.Dying) 2977 assertRemoved(c, unit) 2978 } 2979 2980 func (s *CAASUnitSuite) TestCannotShortCircuitDestroyAllocatedUnit(c *gc.C) { 2981 // This test is similar to TestShortCircuitDestroyUnit but 2982 // the unit has been allocated and a pod created. 2983 unit, err := s.application.AddUnit(state.AddUnitParams{}) 2984 c.Assert(err, jc.ErrorIsNil) 2985 unitState := state.NewUnitState() 2986 unitState.SetUniterState("error") 2987 err = unit.SetState(unitState, state.UnitStateSizeLimits{}) 2988 c.Assert(err, jc.ErrorIsNil) 2989 err = unit.Destroy() 2990 c.Assert(err, jc.ErrorIsNil) 2991 c.Assert(unit.Life(), gc.Equals, state.Dying) 2992 assertLife(c, unit, state.Dying) 2993 } 2994 2995 func (s *CAASUnitSuite) TestUpdateCAASUnitProviderId(c *gc.C) { 2996 existingUnit, err := s.application.AddUnit(state.AddUnitParams{ 2997 ProviderId: strPtr("unit-uuid"), 2998 Address: strPtr("192.168.1.1"), 2999 Ports: &[]string{"80"}, 3000 }) 3001 c.Assert(err, jc.ErrorIsNil) 3002 var updateUnits state.UpdateUnitsOperation 3003 updateUnits.Updates = []*state.UpdateUnitOperation{ 3004 existingUnit.UpdateOperation(state.UnitUpdateProperties{ 3005 ProviderId: strPtr("another-uuid"), 3006 })} 3007 err = s.application.UpdateUnits(&updateUnits) 3008 c.Assert(err, jc.ErrorIsNil) 3009 info, err := existingUnit.ContainerInfo() 3010 c.Assert(err, jc.ErrorIsNil) 3011 c.Assert(info.Unit(), gc.Equals, existingUnit.Name()) 3012 c.Assert(info.ProviderId(), gc.Equals, "another-uuid") 3013 addr := network.NewSpaceAddress("192.168.1.1", network.WithScope(network.ScopeMachineLocal)) 3014 c.Assert(info.Address(), gc.DeepEquals, &addr) 3015 c.Assert(info.Ports(), jc.DeepEquals, []string{"80"}) 3016 } 3017 3018 func (s *CAASUnitSuite) TestAddCAASUnitProviderId(c *gc.C) { 3019 existingUnit, err := s.application.AddUnit(state.AddUnitParams{ 3020 Address: strPtr("192.168.1.1"), 3021 Ports: &[]string{"80"}, 3022 }) 3023 c.Assert(err, jc.ErrorIsNil) 3024 var updateUnits state.UpdateUnitsOperation 3025 updateUnits.Updates = []*state.UpdateUnitOperation{ 3026 existingUnit.UpdateOperation(state.UnitUpdateProperties{ 3027 ProviderId: strPtr("another-uuid"), 3028 })} 3029 err = s.application.UpdateUnits(&updateUnits) 3030 c.Assert(err, jc.ErrorIsNil) 3031 info, err := existingUnit.ContainerInfo() 3032 c.Assert(err, jc.ErrorIsNil) 3033 c.Assert(info.Unit(), gc.Equals, existingUnit.Name()) 3034 c.Assert(info.ProviderId(), gc.Equals, "another-uuid") 3035 c.Check(info.Address(), gc.NotNil) 3036 c.Check(*info.Address(), jc.DeepEquals, 3037 network.NewSpaceAddress("192.168.1.1", network.WithScope(network.ScopeMachineLocal))) 3038 c.Assert(info.Ports(), jc.DeepEquals, []string{"80"}) 3039 } 3040 3041 func (s *CAASUnitSuite) TestUpdateCAASUnitAddress(c *gc.C) { 3042 existingUnit, err := s.application.AddUnit(state.AddUnitParams{ 3043 ProviderId: strPtr("unit-uuid"), 3044 Address: strPtr("192.168.1.1"), 3045 Ports: &[]string{"80"}, 3046 }) 3047 c.Assert(err, jc.ErrorIsNil) 3048 var updateUnits state.UpdateUnitsOperation 3049 updateUnits.Updates = []*state.UpdateUnitOperation{ 3050 existingUnit.UpdateOperation(state.UnitUpdateProperties{ 3051 Address: strPtr("192.168.1.2"), 3052 })} 3053 err = s.application.UpdateUnits(&updateUnits) 3054 c.Assert(err, jc.ErrorIsNil) 3055 info, err := existingUnit.ContainerInfo() 3056 c.Assert(err, jc.ErrorIsNil) 3057 c.Assert(info.Unit(), gc.Equals, existingUnit.Name()) 3058 c.Assert(info.ProviderId(), gc.Equals, "unit-uuid") 3059 addr := network.NewSpaceAddress("192.168.1.2", network.WithScope(network.ScopeMachineLocal)) 3060 c.Assert(info.Address(), jc.DeepEquals, &addr) 3061 c.Assert(info.Ports(), jc.DeepEquals, []string{"80"}) 3062 } 3063 3064 func (s *CAASUnitSuite) TestUpdateCAASUnitPorts(c *gc.C) { 3065 existingUnit, err := s.application.AddUnit(state.AddUnitParams{ 3066 ProviderId: strPtr("unit-uuid"), 3067 Address: strPtr("192.168.1.1"), 3068 Ports: &[]string{"80"}, 3069 }) 3070 c.Assert(err, jc.ErrorIsNil) 3071 var updateUnits state.UpdateUnitsOperation 3072 updateUnits.Updates = []*state.UpdateUnitOperation{ 3073 existingUnit.UpdateOperation(state.UnitUpdateProperties{ 3074 Ports: &[]string{"443"}, 3075 })} 3076 err = s.application.UpdateUnits(&updateUnits) 3077 c.Assert(err, jc.ErrorIsNil) 3078 info, err := existingUnit.ContainerInfo() 3079 c.Assert(err, jc.ErrorIsNil) 3080 c.Assert(info.Unit(), gc.Equals, existingUnit.Name()) 3081 c.Assert(info.ProviderId(), gc.Equals, "unit-uuid") 3082 addr := network.NewSpaceAddress("192.168.1.1", network.WithScope(network.ScopeMachineLocal)) 3083 c.Assert(info.Address(), jc.DeepEquals, &addr) 3084 c.Assert(info.Ports(), jc.DeepEquals, []string{"443"}) 3085 } 3086 3087 func (s *CAASUnitSuite) TestRemoveUnitDeletesContainerInfo(c *gc.C) { 3088 existingUnit, err := s.application.AddUnit(state.AddUnitParams{ 3089 ProviderId: strPtr("unit-uuid"), 3090 Address: strPtr("192.168.1.1"), 3091 Ports: &[]string{"80"}, 3092 }) 3093 c.Assert(err, jc.ErrorIsNil) 3094 err = existingUnit.Destroy() 3095 c.Assert(err, jc.ErrorIsNil) 3096 _, err = existingUnit.ContainerInfo() 3097 c.Assert(err, jc.Satisfies, errors.IsNotFound) 3098 } 3099 3100 func (s *CAASUnitSuite) TestPrivateAddress(c *gc.C) { 3101 existingUnit, err := s.application.AddUnit(state.AddUnitParams{}) 3102 c.Assert(err, jc.ErrorIsNil) 3103 err = s.application.UpdateCloudService("", network.SpaceAddresses{ 3104 network.NewSpaceAddress("192.168.1.2", network.WithScope(network.ScopeCloudLocal)), 3105 network.NewSpaceAddress("54.32.1.2", network.WithScope(network.ScopePublic)), 3106 }) 3107 c.Assert(err, jc.ErrorIsNil) 3108 3109 addr, err := existingUnit.PrivateAddress() 3110 c.Assert(err, jc.ErrorIsNil) 3111 c.Assert(addr, jc.DeepEquals, network.NewSpaceAddress("192.168.1.2", network.WithScope(network.ScopeCloudLocal))) 3112 } 3113 3114 func (s *CAASUnitSuite) TestPublicAddress(c *gc.C) { 3115 existingUnit, err := s.application.AddUnit(state.AddUnitParams{}) 3116 c.Assert(err, jc.ErrorIsNil) 3117 err = s.application.UpdateCloudService("", []network.SpaceAddress{ 3118 network.NewSpaceAddress("192.168.1.2", network.WithScope(network.ScopeCloudLocal)), 3119 network.NewSpaceAddress("54.32.1.2", network.WithScope(network.ScopePublic)), 3120 }) 3121 c.Assert(err, jc.ErrorIsNil) 3122 3123 addr, err := existingUnit.PublicAddress() 3124 c.Assert(err, jc.ErrorIsNil) 3125 c.Assert(addr, jc.DeepEquals, network.NewSpaceAddress("54.32.1.2", network.WithScope(network.ScopePublic))) 3126 } 3127 3128 func (s *CAASUnitSuite) TestAllAddresses(c *gc.C) { 3129 existingUnit, err := s.application.AddUnit(state.AddUnitParams{}) 3130 c.Assert(err, jc.ErrorIsNil) 3131 err = s.application.UpdateCloudService("", []network.SpaceAddress{ 3132 network.NewSpaceAddress("192.168.1.2", network.WithScope(network.ScopeCloudLocal)), 3133 network.NewSpaceAddress("54.32.1.2", network.WithScope(network.ScopePublic)), 3134 }) 3135 c.Assert(err, jc.ErrorIsNil) 3136 3137 var updateUnits state.UpdateUnitsOperation 3138 local := "10.0.0.1" 3139 updateUnits.Updates = []*state.UpdateUnitOperation{existingUnit.UpdateOperation(state.UnitUpdateProperties{ 3140 Address: &local, 3141 Ports: &[]string{"443"}, 3142 })} 3143 err = s.application.UpdateUnits(&updateUnits) 3144 c.Assert(err, jc.ErrorIsNil) 3145 3146 addrs, err := existingUnit.AllAddresses() 3147 c.Assert(err, jc.ErrorIsNil) 3148 c.Assert(addrs, jc.DeepEquals, network.SpaceAddresses{ 3149 network.NewSpaceAddress("192.168.1.2", network.WithScope(network.ScopeCloudLocal)), 3150 network.NewSpaceAddress("54.32.1.2", network.WithScope(network.ScopePublic)), 3151 network.NewSpaceAddress("10.0.0.1", network.WithScope(network.ScopeMachineLocal)), 3152 }) 3153 } 3154 3155 func (s *CAASUnitSuite) TestWatchContainerAddresses(c *gc.C) { 3156 unit, err := s.application.AddUnit(state.AddUnitParams{}) 3157 c.Assert(err, jc.ErrorIsNil) 3158 3159 s.WaitForModelWatchersIdle(c, s.Model.UUID()) 3160 w := unit.WatchContainerAddresses() 3161 defer w.Stop() 3162 wc := statetesting.NewNotifyWatcherC(c, w) 3163 wc.AssertOneChange() 3164 3165 // Change the container port: not reported. 3166 var updateUnits state.UpdateUnitsOperation 3167 updateUnits.Updates = []*state.UpdateUnitOperation{unit.UpdateOperation(state.UnitUpdateProperties{ 3168 Ports: &[]string{"443"}, 3169 })} 3170 err = s.application.UpdateUnits(&updateUnits) 3171 c.Assert(err, jc.ErrorIsNil) 3172 wc.AssertNoChange() 3173 3174 // Set container addresses: reported. 3175 addr := "10.0.0.1" 3176 updateUnits.Updates = []*state.UpdateUnitOperation{unit.UpdateOperation(state.UnitUpdateProperties{ 3177 Address: &addr, 3178 })} 3179 err = s.application.UpdateUnits(&updateUnits) 3180 c.Assert(err, jc.ErrorIsNil) 3181 c.Assert(err, jc.ErrorIsNil) 3182 wc.AssertOneChange() 3183 3184 // Set different machine addresses: reported. 3185 addr = "10.0.0.2" 3186 updateUnits.Updates = []*state.UpdateUnitOperation{unit.UpdateOperation(state.UnitUpdateProperties{ 3187 Address: &addr, 3188 })} 3189 err = s.application.UpdateUnits(&updateUnits) 3190 c.Assert(err, jc.ErrorIsNil) 3191 c.Assert(err, jc.ErrorIsNil) 3192 wc.AssertOneChange() 3193 3194 // Ensure the following operation to set the unit as Dying 3195 // is not short circuited to remove the unit. 3196 unitState := state.NewUnitState() 3197 unitState.SetUniterState("idle") 3198 err = unit.SetState(unitState, state.UnitStateSizeLimits{}) 3199 c.Assert(err, jc.ErrorIsNil) 3200 // Make it Dying: not reported. 3201 err = unit.Destroy() 3202 c.Assert(err, jc.ErrorIsNil) 3203 wc.AssertNoChange() 3204 // Double check the unit is dying and not removed. 3205 err = unit.Refresh() 3206 c.Assert(err, jc.ErrorIsNil) 3207 c.Assert(unit.Life(), gc.Equals, state.Dying) 3208 3209 // Make it Dead: not reported. 3210 err = unit.EnsureDead() 3211 c.Assert(err, jc.ErrorIsNil) 3212 wc.AssertNoChange() 3213 3214 // Remove it: watcher eventually closed and Err 3215 // returns an IsNotFound error. 3216 err = unit.Remove() 3217 c.Assert(err, jc.ErrorIsNil) 3218 select { 3219 case _, ok := <-w.Changes(): 3220 c.Assert(ok, jc.IsFalse) 3221 case <-time.After(coretesting.LongWait): 3222 c.Fatalf("watcher not closed") 3223 } 3224 c.Assert(w.Err(), jc.Satisfies, errors.IsNotFound) 3225 } 3226 3227 func (s *CAASUnitSuite) TestWatchServiceAddressesHash(c *gc.C) { 3228 unit, err := s.application.AddUnit(state.AddUnitParams{}) 3229 c.Assert(err, jc.ErrorIsNil) 3230 s.WaitForModelWatchersIdle(c, s.Model.UUID()) 3231 w := s.application.WatchServiceAddressesHash() 3232 defer w.Stop() 3233 wc := statetesting.NewStringsWatcherC(c, w) 3234 wc.AssertChange("") 3235 3236 // Set container addresses: not reported. 3237 var updateUnits state.UpdateUnitsOperation 3238 addr := "10.0.0.1" 3239 updateUnits.Updates = []*state.UpdateUnitOperation{unit.UpdateOperation(state.UnitUpdateProperties{ 3240 Address: &addr, 3241 })} 3242 err = s.application.UpdateUnits(&updateUnits) 3243 c.Assert(err, jc.ErrorIsNil) 3244 wc.AssertNoChange() 3245 3246 // Set service addresses: reported. 3247 err = s.application.UpdateCloudService("1", network.SpaceAddresses{network.NewSpaceAddress("10.0.0.2")}) 3248 c.Assert(err, jc.ErrorIsNil) 3249 wc.AssertChange("7fcdfefa54c49ed9dc8132b4a28491a02ec35b03c20b2d4cc95469fead847ff8") 3250 3251 // Set different container addresses: reported. 3252 err = s.application.UpdateCloudService("1", network.SpaceAddresses{network.NewSpaceAddress("10.0.0.3")}) 3253 c.Assert(err, jc.ErrorIsNil) 3254 wc.AssertChange("800892c5473f38623ef4856303b3458cfa81d0da803f228db69910949a13f458") 3255 3256 // Ensure the following operation to set the unit as Dying 3257 // is not short circuited to remove the unit. 3258 unitState := state.NewUnitState() 3259 unitState.SetUniterState("idle") 3260 err = unit.SetState(unitState, state.UnitStateSizeLimits{}) 3261 c.Assert(err, jc.ErrorIsNil) 3262 // Make it Dying: not reported. 3263 err = unit.Destroy() 3264 c.Assert(err, jc.ErrorIsNil) 3265 wc.AssertNoChange() 3266 // Double check the unit is dying and not removed. 3267 err = unit.Refresh() 3268 c.Assert(err, jc.ErrorIsNil) 3269 c.Assert(unit.Life(), gc.Equals, state.Dying) 3270 3271 // Make it Dead: not reported. 3272 err = unit.EnsureDead() 3273 c.Assert(err, jc.ErrorIsNil) 3274 wc.AssertNoChange() 3275 3276 // Remove it: watcher eventually closed and Err 3277 // returns an IsNotFound error. 3278 err = unit.Remove() 3279 c.Assert(err, jc.ErrorIsNil) 3280 err = s.application.Destroy() 3281 c.Assert(err, jc.ErrorIsNil) 3282 3283 // App removal requires cluster resources to be cleared. 3284 err = s.application.Refresh() 3285 c.Assert(err, jc.ErrorIsNil) 3286 err = s.application.ClearResources() 3287 c.Assert(err, jc.ErrorIsNil) 3288 assertCleanupCount(c, s.Model.State(), 2) 3289 3290 select { 3291 case _, ok := <-w.Changes(): 3292 c.Assert(ok, jc.IsFalse) 3293 case <-time.After(coretesting.LongWait): 3294 c.Fatalf("watcher not closed") 3295 } 3296 c.Assert(w.Err(), jc.Satisfies, errors.IsNotFound) 3297 } 3298 3299 func (s *CAASUnitSuite) TestOperatorAddAction(c *gc.C) { 3300 unit, err := s.operatorApp.AddUnit(state.AddUnitParams{}) 3301 c.Assert(err, jc.ErrorIsNil) 3302 operationID, err := s.Model.EnqueueOperation("a test", 1) 3303 c.Assert(err, jc.ErrorIsNil) 3304 action, err := s.Model.AddAction(unit, operationID, "snapshot", nil, nil, nil) 3305 c.Assert(err, jc.ErrorIsNil) 3306 c.Assert(action.Parameters(), jc.DeepEquals, map[string]interface{}{ 3307 "outfile": "abcd", "workload-context": false, 3308 }) 3309 }