github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/model_test.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state_test 5 6 import ( 7 "fmt" 8 "sort" 9 "time" 10 11 "github.com/juju/charm/v12" 12 "github.com/juju/clock" 13 "github.com/juju/errors" 14 "github.com/juju/mgo/v3/bson" 15 mgotesting "github.com/juju/mgo/v3/testing" 16 "github.com/juju/names/v5" 17 jc "github.com/juju/testing/checkers" 18 "github.com/juju/utils/v3" 19 gc "gopkg.in/check.v1" 20 21 "github.com/juju/juju/cloud" 22 "github.com/juju/juju/core/crossmodel" 23 "github.com/juju/juju/core/network" 24 "github.com/juju/juju/core/permission" 25 "github.com/juju/juju/environs/config" 26 "github.com/juju/juju/state" 27 stateerrors "github.com/juju/juju/state/errors" 28 "github.com/juju/juju/storage" 29 "github.com/juju/juju/testing" 30 "github.com/juju/juju/testing/factory" 31 ) 32 33 type ModelSuite struct { 34 ConnSuite 35 } 36 37 var _ = gc.Suite(&ModelSuite{}) 38 39 func (s *ModelSuite) TestModel(c *gc.C) { 40 model, err := s.State.Model() 41 c.Assert(err, jc.ErrorIsNil) 42 c.Check(model.IsControllerModel(), jc.IsTrue) 43 44 expectedTag := names.NewModelTag(model.UUID()) 45 c.Assert(model.Tag(), gc.Equals, expectedTag) 46 c.Assert(model.ControllerTag(), gc.Equals, s.State.ControllerTag()) 47 c.Assert(model.Name(), gc.Equals, "testmodel") 48 c.Assert(model.Owner(), gc.Equals, s.Owner) 49 c.Assert(model.Life(), gc.Equals, state.Alive) 50 c.Assert(model.MigrationMode(), gc.Equals, state.MigrationModeNone) 51 } 52 53 func (s *ModelSuite) TestModelDestroy(c *gc.C) { 54 model, err := s.State.Model() 55 c.Assert(err, jc.ErrorIsNil) 56 57 err = model.Destroy(state.DestroyModelParams{}) 58 c.Assert(err, jc.ErrorIsNil) 59 err = model.Refresh() 60 c.Assert(err, jc.ErrorIsNil) 61 c.Assert(model.Life(), gc.Equals, state.Dying) 62 } 63 64 func (s *ModelSuite) TestModelDestroyWithoutVolumes(c *gc.C) { 65 //https://bugs.launchpad.net/juju/+bug/1800872 66 // Models introduced in 2.1 and then upgraded to 2.2 don't have Volumes or Filesystem attributes 67 // on their modelEntitiesRefs documents 68 model, err := s.State.Model() 69 c.Assert(err, jc.ErrorIsNil) 70 modelEntities, closer := state.GetCollection(s.State, state.ModelEntityRefsC) 71 defer closer() 72 rawModelEntities := modelEntities.Writeable().Underlying() 73 err = rawModelEntities.Update(bson.M{"_id": model.UUID()}, bson.M{"$unset": bson.M{"volumes": 1, "filesystems": 1}}) 74 c.Assert(err, jc.ErrorIsNil) 75 c.Assert(model.Destroy(state.DestroyModelParams{}), jc.ErrorIsNil) 76 c.Assert(model.Refresh(), jc.ErrorIsNil) 77 c.Assert(model.Life(), gc.Equals, state.Dying) 78 } 79 80 func (s *ModelSuite) TestSetPassword(c *gc.C) { 81 testSetPassword(c, func() (state.Authenticator, error) { 82 return s.State.Model() 83 }) 84 } 85 86 func (s *ModelSuite) TestNewModelSameUserSameNameFails(c *gc.C) { 87 cfg, _ := s.createTestModelConfig(c) 88 owner := s.Factory.MakeUser(c, nil).UserTag() 89 90 // Create the first model. 91 model, st1, err := s.Controller.NewModel(state.ModelArgs{ 92 Type: state.ModelTypeIAAS, 93 CloudName: "dummy", 94 CloudRegion: "dummy-region", 95 Config: cfg, 96 Owner: owner, 97 StorageProviderRegistry: storage.StaticProviderRegistry{}, 98 }) 99 c.Assert(err, jc.ErrorIsNil) 100 defer st1.Close() 101 c.Assert(model.UniqueIndexExists(), jc.IsTrue) 102 103 // Attempt to create another model with a different UUID but the 104 // same owner and name as the first. 105 newUUID, err := utils.NewUUID() 106 c.Assert(err, jc.ErrorIsNil) 107 cfg2 := testing.CustomModelConfig(c, testing.Attrs{ 108 "name": cfg.Name(), 109 "uuid": newUUID.String(), 110 }) 111 _, _, err = s.Controller.NewModel(state.ModelArgs{ 112 Type: state.ModelTypeIAAS, 113 CloudName: "dummy", 114 CloudRegion: "dummy-region", 115 Config: cfg2, 116 Owner: owner, 117 StorageProviderRegistry: storage.StaticProviderRegistry{}, 118 }) 119 errMsg := fmt.Sprintf("model %q for %s already exists", cfg2.Name(), owner.Id()) 120 c.Assert(err, gc.ErrorMatches, errMsg) 121 c.Assert(errors.IsAlreadyExists(err), jc.IsTrue) 122 123 // Remove the first model. 124 model1, err := st1.Model() 125 c.Assert(err, jc.ErrorIsNil) 126 err = model1.Destroy(state.DestroyModelParams{}) 127 c.Assert(err, jc.ErrorIsNil) 128 // Destroy only sets the model to dying and RemoveDyingModel can 129 // only be called on a dead model. Normally, the environ's lifecycle 130 // would be set to dead after machines and applications have been cleaned up. 131 err = model1.SetDead() 132 c.Assert(err, jc.ErrorIsNil) 133 err = st1.RemoveDyingModel() 134 c.Assert(err, jc.ErrorIsNil) 135 136 // We should now be able to create the other model. 137 model2, st2, err := s.Controller.NewModel(state.ModelArgs{ 138 Type: state.ModelTypeIAAS, 139 CloudName: "dummy", 140 CloudRegion: "dummy-region", 141 Config: cfg2, 142 Owner: owner, 143 StorageProviderRegistry: storage.StaticProviderRegistry{}, 144 }) 145 c.Assert(err, jc.ErrorIsNil) 146 defer st2.Close() 147 c.Assert(model2, gc.NotNil) 148 c.Assert(st2, gc.NotNil) 149 } 150 151 func (s *ModelSuite) TestNewCAASModelDifferentUser(c *gc.C) { 152 cfg, _ := s.createTestModelConfig(c) 153 owner := s.Factory.MakeUser(c, nil).UserTag() 154 owner2 := s.Factory.MakeUser(c, nil).UserTag() 155 156 // Create the first model. 157 model, st1, err := s.Controller.NewModel(state.ModelArgs{ 158 Type: state.ModelTypeCAAS, 159 CloudName: "dummy", 160 CloudRegion: "dummy-region", 161 Config: cfg, 162 Owner: owner, 163 StorageProviderRegistry: storage.StaticProviderRegistry{}, 164 }) 165 c.Assert(err, jc.ErrorIsNil) 166 defer st1.Close() 167 c.Assert(model.UniqueIndexExists(), jc.IsTrue) 168 169 // Attempt to create another model with a different UUID and owner 170 // but the name as the first. 171 newUUID, err := utils.NewUUID() 172 c.Assert(err, jc.ErrorIsNil) 173 cfg2 := testing.CustomModelConfig(c, testing.Attrs{ 174 "name": cfg.Name(), 175 "uuid": newUUID.String(), 176 }) 177 178 // We should now be able to create the other model. 179 model2, st2, err := s.Controller.NewModel(state.ModelArgs{ 180 Type: state.ModelTypeCAAS, 181 CloudName: "dummy", 182 CloudRegion: "dummy-region", 183 Config: cfg2, 184 Owner: owner2, 185 StorageProviderRegistry: storage.StaticProviderRegistry{}, 186 }) 187 c.Assert(err, jc.ErrorIsNil) 188 defer st2.Close() 189 c.Assert(model2.UniqueIndexExists(), jc.IsTrue) 190 } 191 192 func (s *ModelSuite) TestNewCAASModelSameUserFails(c *gc.C) { 193 cfg, _ := s.createTestModelConfig(c) 194 owner := s.Factory.MakeUser(c, nil).UserTag() 195 196 // Create the first model. 197 model, st1, err := s.Controller.NewModel(state.ModelArgs{ 198 Type: state.ModelTypeCAAS, 199 CloudName: "dummy", 200 CloudRegion: "dummy-region", 201 Config: cfg, 202 Owner: owner, 203 StorageProviderRegistry: storage.StaticProviderRegistry{}, 204 }) 205 c.Assert(err, jc.ErrorIsNil) 206 defer st1.Close() 207 c.Assert(model.UniqueIndexExists(), jc.IsTrue) 208 209 // Attempt to create another model with a different UUID but the 210 // same owner and name as the first. 211 newUUID, err := utils.NewUUID() 212 c.Assert(err, jc.ErrorIsNil) 213 cfg2 := testing.CustomModelConfig(c, testing.Attrs{ 214 "name": cfg.Name(), 215 "uuid": newUUID.String(), 216 }) 217 _, _, err = s.Controller.NewModel(state.ModelArgs{ 218 Type: state.ModelTypeCAAS, 219 CloudName: "dummy", 220 CloudRegion: "dummy-region", 221 Config: cfg2, 222 Owner: owner, 223 StorageProviderRegistry: storage.StaticProviderRegistry{}, 224 }) 225 errMsg := fmt.Sprintf("model %q for %s already exists", cfg2.Name(), owner.Name()) 226 c.Assert(err, gc.ErrorMatches, errMsg) 227 c.Assert(errors.IsAlreadyExists(err), jc.IsTrue) 228 229 // Remove the first model. 230 model1, err := st1.Model() 231 c.Assert(err, jc.ErrorIsNil) 232 err = model1.Destroy(state.DestroyModelParams{}) 233 c.Assert(err, jc.ErrorIsNil) 234 // Destroy only sets the model to dying and RemoveDyingModel can 235 // only be called on a dead model. Normally, the environ's lifecycle 236 // would be set to dead after machines and applications have been cleaned up. 237 err = model1.SetDead() 238 c.Assert(err, jc.ErrorIsNil) 239 err = st1.RemoveDyingModel() 240 c.Assert(err, jc.ErrorIsNil) 241 242 // We should now be able to create the other model. 243 model2, st2, err := s.Controller.NewModel(state.ModelArgs{ 244 Type: state.ModelTypeCAAS, 245 CloudName: "dummy", 246 CloudRegion: "dummy-region", 247 Config: cfg2, 248 Owner: owner, 249 StorageProviderRegistry: storage.StaticProviderRegistry{}, 250 }) 251 c.Assert(err, jc.ErrorIsNil) 252 defer st2.Close() 253 c.Assert(model2, gc.NotNil) 254 c.Assert(st2, gc.NotNil) 255 } 256 257 func (s *ModelSuite) TestNewModelMissingType(c *gc.C) { 258 cfg, _ := s.createTestModelConfig(c) 259 owner := names.NewUserTag("test@remote") 260 _, _, err := s.Controller.NewModel(state.ModelArgs{ 261 // No type 262 CloudName: "dummy", 263 CloudRegion: "dummy-region", 264 Config: cfg, 265 Owner: owner, 266 StorageProviderRegistry: storage.StaticProviderRegistry{}, 267 }) 268 c.Assert(err, gc.ErrorMatches, "empty Type not valid") 269 270 } 271 272 func (s *ModelSuite) TestNewModel(c *gc.C) { 273 cfg, uuid := s.createTestModelConfig(c) 274 owner := names.NewUserTag("test@remote") 275 276 model, st, err := s.Controller.NewModel(state.ModelArgs{ 277 Type: state.ModelTypeIAAS, 278 CloudName: "dummy", 279 CloudRegion: "dummy-region", 280 Config: cfg, 281 Owner: owner, 282 StorageProviderRegistry: storage.StaticProviderRegistry{}, 283 }) 284 c.Assert(err, jc.ErrorIsNil) 285 c.Check(model.IsControllerModel(), jc.IsFalse) 286 defer st.Close() 287 288 modelTag := names.NewModelTag(uuid) 289 assertModelMatches := func(model *state.Model) { 290 c.Assert(model.UUID(), gc.Equals, modelTag.Id()) 291 c.Assert(model.Type(), gc.Equals, state.ModelTypeIAAS) 292 c.Assert(model.Tag(), gc.Equals, modelTag) 293 c.Assert(model.ControllerTag(), gc.Equals, s.State.ControllerTag()) 294 c.Assert(model.Owner(), gc.Equals, owner) 295 c.Assert(model.Name(), gc.Equals, "testing") 296 c.Assert(model.Life(), gc.Equals, state.Alive) 297 c.Assert(model.CloudRegion(), gc.Equals, "dummy-region") 298 } 299 assertModelMatches(model) 300 301 model, ph, err := s.StatePool.GetModel(uuid) 302 c.Assert(err, jc.ErrorIsNil) 303 defer ph.Release() 304 assertModelMatches(model) 305 306 model, err = st.Model() 307 c.Assert(err, jc.ErrorIsNil) 308 assertModelMatches(model) 309 310 // Check that the cloud's model count is incremented. 311 testCloud, err := s.State.Cloud("dummy") 312 c.Assert(err, jc.ErrorIsNil) 313 refCount, err := state.CloudModelRefCount(st, testCloud.Name) 314 c.Assert(err, jc.ErrorIsNil) 315 c.Assert(refCount, gc.Equals, 2) 316 317 // Since the model tag for the State connection is different, 318 // asking for this model through FindEntity returns a not found error. 319 _, err = s.State.FindEntity(modelTag) 320 c.Assert(err, jc.Satisfies, errors.IsNotFound) 321 322 entity, err := st.FindEntity(modelTag) 323 c.Assert(err, jc.ErrorIsNil) 324 c.Assert(entity.Tag(), gc.Equals, modelTag) 325 326 // Ensure the model is functional by adding a machine 327 _, err = st.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits) 328 c.Assert(err, jc.ErrorIsNil) 329 330 // Ensure the default model was created. 331 _, err = st.SpaceByName(network.AlphaSpaceName) 332 c.Assert(err, jc.ErrorIsNil) 333 } 334 335 func (s *ModelSuite) TestNewModelRegionNameEscaped(c *gc.C) { 336 cfg, _ := s.createTestModelConfig(c) 337 model, st, err := s.Controller.NewModel(state.ModelArgs{ 338 Type: state.ModelTypeIAAS, 339 CloudName: "dummy", 340 CloudRegion: "dotty.region", 341 Config: cfg, 342 Owner: names.NewUserTag("test@remote"), 343 StorageProviderRegistry: storage.StaticProviderRegistry{}, 344 }) 345 c.Assert(err, jc.ErrorIsNil) 346 defer st.Close() 347 c.Assert(model.CloudRegion(), gc.Equals, "dotty.region") 348 } 349 350 func (s *ModelSuite) TestNewModelImportingMode(c *gc.C) { 351 cfg, _ := s.createTestModelConfig(c) 352 owner := names.NewUserTag("test@remote") 353 354 model, st, err := s.Controller.NewModel(state.ModelArgs{ 355 Type: state.ModelTypeIAAS, 356 CloudName: "dummy", 357 CloudRegion: "dummy-region", 358 Config: cfg, 359 Owner: owner, 360 MigrationMode: state.MigrationModeImporting, 361 StorageProviderRegistry: storage.StaticProviderRegistry{}, 362 }) 363 c.Assert(err, jc.ErrorIsNil) 364 defer st.Close() 365 366 c.Assert(model.MigrationMode(), gc.Equals, state.MigrationModeImporting) 367 } 368 369 func (s *ModelSuite) TestSetMigrationMode(c *gc.C) { 370 cfg, _ := s.createTestModelConfig(c) 371 owner := names.NewUserTag("test@remote") 372 373 model, st, err := s.Controller.NewModel(state.ModelArgs{ 374 Type: state.ModelTypeIAAS, 375 CloudName: "dummy", 376 CloudRegion: "dummy-region", 377 Config: cfg, 378 Owner: owner, 379 StorageProviderRegistry: storage.StaticProviderRegistry{}, 380 }) 381 c.Assert(err, jc.ErrorIsNil) 382 defer st.Close() 383 384 err = model.SetMigrationMode(state.MigrationModeExporting) 385 c.Assert(err, jc.ErrorIsNil) 386 c.Assert(model.MigrationMode(), gc.Equals, state.MigrationModeExporting) 387 } 388 389 func (s *ModelSuite) TestModelExists(c *gc.C) { 390 modelExists, err := s.State.ModelExists(s.State.ModelUUID()) 391 c.Assert(err, jc.ErrorIsNil) 392 c.Check(modelExists, jc.IsTrue) 393 } 394 395 func (s *ModelSuite) TestModelExistsNoModel(c *gc.C) { 396 modelExists, err := s.State.ModelExists("foo") 397 c.Assert(err, jc.ErrorIsNil) 398 c.Check(modelExists, jc.IsFalse) 399 } 400 401 func (s *ModelSuite) TestSLA(c *gc.C) { 402 cfg, _ := s.createTestModelConfig(c) 403 owner := names.NewUserTag("test@remote") 404 405 model, st, err := s.Controller.NewModel(state.ModelArgs{ 406 Type: state.ModelTypeIAAS, 407 CloudName: "dummy", 408 CloudRegion: "dummy-region", 409 Config: cfg, 410 Owner: owner, 411 StorageProviderRegistry: storage.StaticProviderRegistry{}, 412 }) 413 c.Assert(err, jc.ErrorIsNil) 414 defer st.Close() 415 416 level, err := st.SLALevel() 417 c.Assert(err, jc.ErrorIsNil) 418 c.Assert(level, gc.Equals, "unsupported") 419 c.Assert(model.SLACredential(), gc.DeepEquals, []byte{}) 420 for _, goodLevel := range []string{"unsupported", "essential", "standard", "advanced"} { 421 err = st.SetSLA(goodLevel, "bob", []byte("auth "+goodLevel)) 422 c.Assert(err, jc.ErrorIsNil) 423 c.Assert(model.Refresh(), jc.ErrorIsNil) 424 level, err = st.SLALevel() 425 c.Assert(err, jc.ErrorIsNil) 426 c.Assert(level, gc.Equals, goodLevel) 427 c.Assert(model.SLALevel(), gc.Equals, goodLevel) 428 c.Assert(model.SLAOwner(), gc.Equals, "bob") 429 c.Assert(model.SLACredential(), gc.DeepEquals, []byte("auth "+goodLevel)) 430 } 431 432 defaultLevel, err := state.NewSLALevel("") 433 c.Assert(err, jc.ErrorIsNil) 434 c.Assert(defaultLevel, gc.Equals, state.SLAUnsupported) 435 436 err = model.SetSLA("nope", "nobody", []byte("auth nope")) 437 c.Assert(err, gc.ErrorMatches, `.*SLA level "nope" not valid.*`) 438 439 c.Assert(model.SLALevel(), gc.Equals, "advanced") 440 c.Assert(model.SLAOwner(), gc.Equals, "bob") 441 c.Assert(model.SLACredential(), gc.DeepEquals, []byte("auth advanced")) 442 slaCreds, err := st.SLACredential() 443 c.Assert(err, jc.ErrorIsNil) 444 c.Assert(slaCreds, gc.DeepEquals, []byte("auth advanced")) 445 } 446 447 func (s *ModelSuite) TestMeterStatus(c *gc.C) { 448 cfg, _ := s.createTestModelConfig(c) 449 owner := names.NewUserTag("test@remote") 450 451 model, st, err := s.Controller.NewModel(state.ModelArgs{ 452 Type: state.ModelTypeIAAS, 453 CloudName: "dummy", 454 CloudRegion: "dummy-region", 455 Config: cfg, 456 Owner: owner, 457 StorageProviderRegistry: storage.StaticProviderRegistry{}, 458 }) 459 c.Assert(err, jc.ErrorIsNil) 460 defer st.Close() 461 462 ms, err := st.ModelMeterStatus() 463 c.Assert(err, jc.ErrorIsNil) 464 c.Assert(ms.Code, gc.Equals, state.MeterNotAvailable) 465 c.Assert(ms.Info, gc.Equals, "") 466 467 for i, validStatus := range []string{"RED", "GREEN", "AMBER"} { 468 info := fmt.Sprintf("info setting %d", i) 469 err = st.SetModelMeterStatus(validStatus, info) 470 c.Assert(err, jc.ErrorIsNil) 471 c.Assert(model.Refresh(), jc.ErrorIsNil) 472 ms, err = st.ModelMeterStatus() 473 c.Assert(err, jc.ErrorIsNil) 474 c.Assert(ms.Code.String(), gc.Equals, validStatus) 475 c.Assert(ms.Info, gc.Equals, info) 476 } 477 478 err = model.SetMeterStatus("PURPLE", "foobar") 479 c.Assert(err, gc.ErrorMatches, `meter status "PURPLE" not valid`) 480 481 c.Assert(ms.Code, gc.Equals, state.MeterAmber) 482 c.Assert(ms.Info, gc.Equals, "info setting 2") 483 } 484 485 func (s *ModelSuite) TestConfigForOtherModel(c *gc.C) { 486 otherState := s.Factory.MakeModel(c, &factory.ModelParams{Name: "other"}) 487 defer otherState.Close() 488 otherModel, err := otherState.Model() 489 c.Assert(err, jc.ErrorIsNil) 490 491 // Obtain another instance of the model via the StatePool 492 model, ph, err := s.StatePool.GetModel(otherModel.UUID()) 493 c.Assert(err, jc.ErrorIsNil) 494 defer ph.Release() 495 496 conf, err := model.Config() 497 c.Assert(err, jc.ErrorIsNil) 498 c.Assert(conf.Name(), gc.Equals, "other") 499 c.Assert(conf.UUID(), gc.Equals, otherModel.UUID()) 500 } 501 502 func (s *ModelSuite) TestAllUnits(c *gc.C) { 503 wordpress := s.Factory.MakeApplication(c, &factory.ApplicationParams{ 504 Name: "wordpress", 505 }) 506 mysql := s.Factory.MakeApplication(c, &factory.ApplicationParams{ 507 Name: "mysql", 508 }) 509 s.Factory.MakeUnit(c, &factory.UnitParams{Application: wordpress}) 510 s.Factory.MakeUnit(c, &factory.UnitParams{Application: wordpress}) 511 s.Factory.MakeUnit(c, &factory.UnitParams{Application: mysql}) 512 513 model, err := s.State.Model() 514 c.Assert(err, jc.ErrorIsNil) 515 units, err := model.AllUnits() 516 c.Assert(err, jc.ErrorIsNil) 517 518 var unitNames []string 519 for _, u := range units { 520 if !u.ShouldBeAssigned() { 521 c.Fail() 522 } 523 unitNames = append(unitNames, u.Name()) 524 } 525 sort.Strings(unitNames) 526 c.Assert(unitNames, jc.DeepEquals, []string{ 527 "mysql/0", "wordpress/0", "wordpress/1", 528 }) 529 } 530 531 func (s *ModelSuite) TestMetrics(c *gc.C) { 532 wordpress := s.Factory.MakeApplication(c, &factory.ApplicationParams{ 533 Name: "wordpress", 534 }) 535 mysql := s.Factory.MakeApplication(c, &factory.ApplicationParams{ 536 Name: "mysql", 537 }) 538 s.Factory.MakeUnit(c, &factory.UnitParams{Application: wordpress}) 539 s.Factory.MakeUnit(c, &factory.UnitParams{Application: wordpress}) 540 s.Factory.MakeUnit(c, &factory.UnitParams{Application: mysql}) 541 542 // Add a machine/unit/application and destroy it, to 543 // ensure we're only counting entities that are alive. 544 m := s.Factory.MakeMachine(c, &factory.MachineParams{}) 545 err := m.Destroy() 546 c.Assert(err, jc.ErrorIsNil) 547 one := s.Factory.MakeApplication(c, &factory.ApplicationParams{ 548 Name: "one", 549 }) 550 u := s.Factory.MakeUnit(c, &factory.UnitParams{Application: mysql}) 551 err = one.Destroy() 552 c.Assert(err, jc.ErrorIsNil) 553 err = u.Destroy() 554 c.Assert(err, jc.ErrorIsNil) 555 556 model, err := s.State.Model() 557 c.Assert(err, jc.ErrorIsNil) 558 559 obtained, err := model.Metrics() 560 c.Assert(err, jc.ErrorIsNil) 561 562 expected := state.ModelMetrics{ 563 ApplicationCount: "2", 564 MachineCount: "3", 565 UnitCount: "3", 566 CloudName: "dummy", 567 CloudRegion: "dummy-region", 568 Provider: "dummy", 569 UUID: s.Model.UUID(), 570 ControllerUUID: s.Model.ControllerUUID(), 571 } 572 573 c.Assert(obtained, jc.DeepEquals, expected) 574 } 575 576 func (s *ModelSuite) TestAllEndpointBindings(c *gc.C) { 577 oneSpace := s.Factory.MakeSpace(c, &factory.SpaceParams{ 578 Name: "one", ProviderID: network.Id("provider"), IsPublic: true}) 579 app := state.AddTestingApplicationWithBindings( 580 c, s.State, "wordpress", state.AddTestingCharm(c, s.State, "wordpress"), 581 map[string]string{"db": oneSpace.Id()}) 582 583 model, err := s.State.Model() 584 c.Assert(err, jc.ErrorIsNil) 585 586 listBindings, err := model.AllEndpointBindings() 587 c.Assert(err, jc.ErrorIsNil) 588 c.Assert(listBindings, gc.HasLen, 1) 589 590 expected := map[string]string{ 591 "": network.AlphaSpaceId, 592 "cache": network.AlphaSpaceId, 593 "foo-bar": network.AlphaSpaceId, 594 "db-client": network.AlphaSpaceId, 595 "admin-api": network.AlphaSpaceId, 596 "url": network.AlphaSpaceId, 597 "logging-dir": network.AlphaSpaceId, 598 "monitoring-port": network.AlphaSpaceId, 599 "db": oneSpace.Id(), 600 } 601 c.Assert(listBindings[app.Name()].Map(), gc.DeepEquals, expected) 602 } 603 604 func (s *ModelSuite) TestAllEndpointBindingsSpaceNames(c *gc.C) { 605 oneSpace := s.Factory.MakeSpace(c, &factory.SpaceParams{ 606 Name: "one", ProviderID: network.Id("provider"), IsPublic: true}) 607 state.AddTestingApplicationWithBindings( 608 c, s.State, "wordpress", state.AddTestingCharm(c, s.State, "wordpress"), 609 map[string]string{"db": oneSpace.Id()}) 610 611 spaceNames, err := s.State.AllEndpointBindingsSpaceNames() 612 c.Assert(err, jc.ErrorIsNil) 613 c.Assert(spaceNames.Size(), gc.Equals, 2) 614 c.Assert(spaceNames.SortedValues(), gc.DeepEquals, []string{"alpha", "one"}) 615 } 616 617 func (s *ModelSuite) TestAllEndpointBindingsSpaceNamesWithoutAnySpaces(c *gc.C) { 618 spaceNames, err := s.State.AllEndpointBindingsSpaceNames() 619 c.Assert(err, jc.ErrorIsNil) 620 c.Assert(spaceNames.Size(), gc.Equals, 0) 621 } 622 623 // createTestModelConfig returns a new model config and its UUID for testing. 624 func (s *ModelSuite) createTestModelConfig(c *gc.C) (*config.Config, string) { 625 return createTestModelConfig(c, s.modelTag.Id()) 626 } 627 628 func createTestModelConfig(c *gc.C, controllerUUID string) (*config.Config, string) { 629 uuid, err := utils.NewUUID() 630 c.Assert(err, jc.ErrorIsNil) 631 return testing.CustomModelConfig(c, testing.Attrs{ 632 "name": "testing", 633 "uuid": uuid.String(), 634 }), uuid.String() 635 } 636 637 func (s *ModelSuite) TestModelConfigSameModelAsState(c *gc.C) { 638 model, err := s.State.Model() 639 c.Assert(err, jc.ErrorIsNil) 640 cfg, err := model.Config() 641 c.Assert(err, jc.ErrorIsNil) 642 c.Assert(cfg.UUID(), gc.Equals, s.State.ModelUUID()) 643 } 644 645 func (s *ModelSuite) TestModelConfigDifferentModelThanState(c *gc.C) { 646 otherState := s.Factory.MakeModel(c, nil) 647 defer otherState.Close() 648 model, err := otherState.Model() 649 c.Assert(err, jc.ErrorIsNil) 650 cfg, err := model.Config() 651 c.Assert(err, jc.ErrorIsNil) 652 uuid := cfg.UUID() 653 c.Assert(uuid, gc.Equals, model.UUID()) 654 c.Assert(uuid, gc.Not(gc.Equals), s.State.ModelUUID()) 655 } 656 657 func (s *ModelSuite) TestDestroyControllerModel(c *gc.C) { 658 model, err := s.State.Model() 659 c.Assert(err, jc.ErrorIsNil) 660 err = model.Destroy(state.DestroyModelParams{}) 661 c.Assert(err, jc.ErrorIsNil) 662 c.Assert(model.Refresh(), jc.ErrorIsNil) 663 c.Assert(model.Life(), gc.Equals, state.Dying) 664 } 665 666 func (s *ModelSuite) TestDestroyOtherModel(c *gc.C) { 667 st2 := s.Factory.MakeModel(c, nil) 668 defer st2.Close() 669 model, err := st2.Model() 670 c.Assert(err, jc.ErrorIsNil) 671 c.Assert(model.Destroy(state.DestroyModelParams{}), jc.ErrorIsNil) 672 c.Assert(model.Refresh(), jc.ErrorIsNil) 673 c.Assert(model.Life(), gc.Equals, state.Dying) 674 c.Assert(st2.RemoveDyingModel(), jc.ErrorIsNil) 675 c.Assert(model.Refresh(), jc.Satisfies, errors.IsNotFound) 676 // Destroying an empty model also removes the name index doc. 677 c.Assert(model.UniqueIndexExists(), jc.IsFalse) 678 } 679 680 func (s *ModelSuite) TestDestroyControllerNonEmptyModelFails(c *gc.C) { 681 s.assertDestroyControllerNonEmptyModelFails(c, nil) 682 } 683 684 func (s *ModelSuite) TestDestroyControllerNonEmptyModelWithForceFails(c *gc.C) { 685 force := true 686 s.assertDestroyControllerNonEmptyModelFails(c, &force) 687 } 688 689 func (s *ModelSuite) assertDestroyControllerNonEmptyModelFails(c *gc.C, force *bool) { 690 st2 := s.Factory.MakeModel(c, nil) 691 defer st2.Close() 692 factory.NewFactory(st2, s.StatePool).MakeApplication(c, nil) 693 694 model, err := s.State.Model() 695 c.Assert(err, jc.ErrorIsNil) 696 c.Assert(model.Destroy(state.DestroyModelParams{Force: force}), gc.ErrorMatches, "failed to destroy model: hosting 1 other model") 697 c.Assert(model.Refresh(), jc.ErrorIsNil) 698 c.Assert(model.Life(), gc.Equals, state.Alive) 699 model2, err := st2.Model() 700 c.Assert(err, jc.ErrorIsNil) 701 c.Assert(model2.Refresh(), jc.ErrorIsNil) 702 c.Assert(model2.Life(), gc.Equals, state.Alive) 703 } 704 705 func (s *ModelSuite) TestDestroyControllerWithEmptyModel(c *gc.C) { 706 st2 := s.Factory.MakeModel(c, nil) 707 defer st2.Close() 708 709 controllerModel, err := s.State.Model() 710 c.Assert(err, jc.ErrorIsNil) 711 c.Assert(controllerModel.Destroy(state.DestroyModelParams{}), jc.ErrorIsNil) 712 c.Assert(controllerModel.Refresh(), jc.ErrorIsNil) 713 c.Assert(controllerModel.Life(), gc.Equals, state.Dying) 714 assertNeedsCleanup(c, s.State) 715 assertCleanupRuns(c, s.State) 716 717 hostedModel, err := st2.Model() 718 c.Assert(err, jc.ErrorIsNil) 719 c.Assert(hostedModel.Refresh(), jc.ErrorIsNil) 720 c.Logf("model %s, life %s", hostedModel.UUID(), hostedModel.Life()) 721 c.Assert(hostedModel.Life(), gc.Equals, state.Dying) 722 c.Assert(st2.RemoveDyingModel(), jc.ErrorIsNil) 723 c.Assert(hostedModel.Refresh(), jc.Satisfies, errors.IsNotFound) 724 } 725 726 func (s *ModelSuite) TestDestroyControllerAndHostedModels(c *gc.C) { 727 st2 := s.Factory.MakeModel(c, nil) 728 defer st2.Close() 729 factory.NewFactory(st2, s.StatePool).MakeApplication(c, nil) 730 731 controllerModel, err := s.State.Model() 732 c.Assert(err, jc.ErrorIsNil) 733 destroyStorage := true 734 c.Assert(controllerModel.Destroy(state.DestroyModelParams{ 735 DestroyHostedModels: true, 736 DestroyStorage: &destroyStorage, 737 }), jc.ErrorIsNil) 738 739 model, err := s.State.Model() 740 c.Assert(err, jc.ErrorIsNil) 741 c.Assert(model.Life(), gc.Equals, state.Dying) 742 743 assertNeedsCleanup(c, s.State) 744 assertCleanupRuns(c, s.State) 745 746 // Cleanups for hosted model enqueued by controller model cleanups. 747 assertNeedsCleanup(c, st2) 748 assertCleanupRuns(c, st2) 749 750 model2, err := st2.Model() 751 c.Assert(err, jc.ErrorIsNil) 752 c.Assert(model2.Life(), gc.Equals, state.Dying) 753 754 c.Assert(st2.ProcessDyingModel(), jc.ErrorIsNil) 755 c.Assert(st2.RemoveDyingModel(), jc.ErrorIsNil) 756 757 c.Assert(model2.Refresh(), jc.Satisfies, errors.IsNotFound) 758 759 c.Assert(s.State.ProcessDyingModel(), jc.ErrorIsNil) 760 c.Assert(s.State.RemoveDyingModel(), jc.ErrorIsNil) 761 c.Assert(model.Refresh(), jc.Satisfies, errors.IsNotFound) 762 } 763 764 func (s *ModelSuite) TestDestroyControllerAndHostedModelsWithResources(c *gc.C) { 765 otherSt := s.Factory.MakeModel(c, nil) 766 defer otherSt.Close() 767 768 assertModel := func(model *state.Model, st *state.State, life state.Life, expectedMachines, expectedApplications int) { 769 c.Assert(model.Refresh(), jc.ErrorIsNil) 770 c.Assert(model.Life(), gc.Equals, life) 771 772 machines, err := st.AllMachines() 773 c.Assert(err, jc.ErrorIsNil) 774 c.Assert(machines, gc.HasLen, expectedMachines) 775 776 applications, err := st.AllApplications() 777 c.Assert(err, jc.ErrorIsNil) 778 c.Assert(applications, gc.HasLen, expectedApplications) 779 } 780 781 // add some machines and applications 782 otherModel, err := otherSt.Model() 783 c.Assert(err, jc.ErrorIsNil) 784 _, err = otherSt.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits) 785 c.Assert(err, jc.ErrorIsNil) 786 application := s.Factory.MakeApplication(c, nil) 787 788 ch := state.AddTestingCharm(c, otherSt, "dummy") 789 args := state.AddApplicationArgs{ 790 Name: application.Name(), 791 Charm: ch, 792 CharmOrigin: &state.CharmOrigin{Platform: &state.Platform{ 793 OS: "ubuntu", 794 Channel: "12.10/stable", 795 }}, 796 } 797 _, err = otherSt.AddApplication(args) 798 c.Assert(err, jc.ErrorIsNil) 799 800 controllerModel, err := s.State.Model() 801 c.Assert(err, jc.ErrorIsNil) 802 destroyStorage := true 803 force := true 804 c.Assert(controllerModel.Destroy(state.DestroyModelParams{ 805 Force: &force, 806 DestroyHostedModels: true, 807 DestroyStorage: &destroyStorage, 808 }), jc.ErrorIsNil) 809 810 assertCleanupCountDirty(c, s.State, 4) 811 assertAllMachinesDeadAndRemove(c, s.State) 812 assertModel(controllerModel, s.State, state.Dying, 0, 0) 813 814 err = s.State.ProcessDyingModel() 815 c.Assert(errors.Is(err, stateerrors.HasHostedModelsError), jc.IsTrue) 816 c.Assert(err, gc.ErrorMatches, `hosting 1 other model`) 817 818 assertCleanupCount(c, otherSt, 3) 819 assertAllMachinesDeadAndRemove(c, otherSt) 820 assertModel(otherModel, otherSt, state.Dying, 0, 0) 821 c.Assert(otherSt.ProcessDyingModel(), jc.ErrorIsNil) 822 c.Assert(otherSt.RemoveDyingModel(), jc.ErrorIsNil) 823 824 c.Assert(otherModel.Refresh(), jc.Satisfies, errors.IsNotFound) 825 826 c.Assert(s.State.ProcessDyingModel(), jc.ErrorIsNil) 827 c.Assert(s.State.RemoveDyingModel(), jc.ErrorIsNil) 828 c.Assert(controllerModel.Refresh(), jc.Satisfies, errors.IsNotFound) 829 } 830 831 func (s *ModelSuite) assertDestroyControllerAndHostedModelsWithPersistentStorage(c *gc.C, force *bool) { 832 otherSt := s.Factory.MakeModel(c, nil) 833 defer otherSt.Close() 834 835 // Add a unit with persistent storage, which will prevent Destroy 836 // from succeeding on account of DestroyStorage being nil. 837 otherFactory := factory.NewFactory(otherSt, s.StatePool) 838 otherFactory.MakeUnit(c, &factory.UnitParams{ 839 Application: otherFactory.MakeApplication(c, &factory.ApplicationParams{ 840 Charm: otherFactory.MakeCharm(c, &factory.CharmParams{ 841 Name: "storage-block", 842 URL: "ch:quantal/storage-block-1", 843 }), 844 Storage: map[string]state.StorageConstraints{ 845 "data": {Count: 1, Size: 1024, Pool: "modelscoped"}, 846 }, 847 }), 848 }) 849 850 controllerModel, err := s.State.Model() 851 c.Assert(err, jc.ErrorIsNil) 852 err = controllerModel.Destroy(state.DestroyModelParams{ 853 DestroyHostedModels: true, 854 Force: force, 855 }) 856 c.Assert(errors.Is(err, stateerrors.PersistentStorageError), jc.IsTrue) 857 } 858 859 func (s *ModelSuite) TestDestroyControllerAndHostedModelsWithPersistentStorage(c *gc.C) { 860 s.assertDestroyControllerAndHostedModelsWithPersistentStorage(c, nil) 861 } 862 863 func (s *ModelSuite) TestDestroyControllerAndHostedModelsWithPersistentStorageWithForce(c *gc.C) { 864 force := true 865 s.assertDestroyControllerAndHostedModelsWithPersistentStorage(c, &force) 866 } 867 868 func (s *ModelSuite) TestDestroyControllerEmptyModelRace(c *gc.C) { 869 defer s.Factory.MakeModel(c, nil).Close() 870 871 // Simulate an empty model being added just before the 872 // remove txn is called. 873 defer state.SetBeforeHooks(c, s.State, func() { 874 s.Factory.MakeModel(c, nil).Close() 875 }).Check() 876 877 model, err := s.State.Model() 878 c.Assert(err, jc.ErrorIsNil) 879 c.Assert(model.Destroy(state.DestroyModelParams{}), jc.ErrorIsNil) 880 } 881 882 func (s *ModelSuite) TestDestroyControllerRemoveEmptyAddNonEmptyModel(c *gc.C) { 883 st2 := s.Factory.MakeModel(c, nil) 884 defer st2.Close() 885 886 // Simulate an empty model being removed, and a new non-empty 887 // model being added, just before the remove txn is called. 888 defer state.SetBeforeHooks(c, s.State, func() { 889 // Destroy the empty model, which should move it right 890 // along to Dead, and then remove it. 891 model, err := st2.Model() 892 c.Assert(err, jc.ErrorIsNil) 893 c.Assert(model.Destroy(state.DestroyModelParams{}), jc.ErrorIsNil) 894 err = st2.RemoveDyingModel() 895 c.Assert(err, jc.ErrorIsNil) 896 897 // Add a new, non-empty model. This should still prevent 898 // the controller from being destroyed. 899 st3 := s.Factory.MakeModel(c, nil) 900 defer st3.Close() 901 factory.NewFactory(st3, s.StatePool).MakeApplication(c, nil) 902 }).Check() 903 904 model, err := s.State.Model() 905 c.Assert(err, jc.ErrorIsNil) 906 c.Assert(model.Destroy(state.DestroyModelParams{}), gc.ErrorMatches, "failed to destroy model: hosting 1 other model") 907 } 908 909 func (s *ModelSuite) TestDestroyControllerNonEmptyModelRace(c *gc.C) { 910 // Simulate an empty model being added just before the 911 // remove txn is called. 912 defer state.SetBeforeHooks(c, s.State, func() { 913 st := s.Factory.MakeModel(c, nil) 914 defer st.Close() 915 factory.NewFactory(st, s.StatePool).MakeApplication(c, nil) 916 }).Check() 917 918 model, err := s.State.Model() 919 c.Assert(err, jc.ErrorIsNil) 920 c.Assert(model.Destroy(state.DestroyModelParams{}), gc.ErrorMatches, "failed to destroy model: hosting 1 other model") 921 } 922 923 func (s *ModelSuite) TestDestroyControllerAlreadyDyingRaceNoOp(c *gc.C) { 924 model, err := s.State.Model() 925 c.Assert(err, jc.ErrorIsNil) 926 927 // Simulate an model being destroyed by another client just before 928 // the remove txn is called. 929 defer state.SetBeforeHooks(c, s.State, func() { 930 c.Assert(model.Destroy(state.DestroyModelParams{}), jc.ErrorIsNil) 931 }).Check() 932 933 c.Assert(model.Destroy(state.DestroyModelParams{}), jc.ErrorIsNil) 934 } 935 936 func (s *ModelSuite) TestDestroyControllerAlreadyDyingNoOp(c *gc.C) { 937 model, err := s.State.Model() 938 c.Assert(err, jc.ErrorIsNil) 939 940 c.Assert(model.Destroy(state.DestroyModelParams{}), jc.ErrorIsNil) 941 c.Assert(model.Destroy(state.DestroyModelParams{}), jc.ErrorIsNil) 942 } 943 944 func (s *ModelSuite) TestDestroyModelNonEmpty(c *gc.C) { 945 m, err := s.State.Model() 946 c.Assert(err, jc.ErrorIsNil) 947 948 // Add a application to prevent the model from transitioning directly to Dead. 949 s.Factory.MakeApplication(c, nil) 950 951 c.Assert(m.Destroy(state.DestroyModelParams{}), jc.ErrorIsNil) 952 c.Assert(m.Refresh(), jc.ErrorIsNil) 953 c.Assert(m.Life(), gc.Equals, state.Dying) 954 955 // Since the model is only dying and not dead, the unique index is still there. 956 c.Assert(m.UniqueIndexExists(), jc.IsTrue) 957 } 958 959 func (s *ModelSuite) assertDestroyModelPersistentStorage(c *gc.C, force *bool) { 960 m, err := s.State.Model() 961 c.Assert(err, jc.ErrorIsNil) 962 963 // Add a unit with persistent storage, which will prevent Destroy 964 // from succeeding on account of DestroyStorage being nil. 965 s.Factory.MakeUnit(c, &factory.UnitParams{ 966 Application: s.Factory.MakeApplication(c, &factory.ApplicationParams{ 967 Charm: s.AddTestingCharm(c, "storage-block"), 968 Storage: map[string]state.StorageConstraints{ 969 "data": {Count: 1, Size: 1024, Pool: "modelscoped"}, 970 }, 971 }), 972 }) 973 974 err = m.Destroy(state.DestroyModelParams{Force: force}) 975 c.Assert(errors.Is(err, stateerrors.PersistentStorageError), jc.IsTrue) 976 c.Assert(m.Refresh(), jc.ErrorIsNil) 977 c.Assert(m.Life(), gc.Equals, state.Alive) 978 } 979 980 func (s *ModelSuite) TestDestroyModelPersistentStorage(c *gc.C) { 981 s.assertDestroyModelPersistentStorage(c, nil) 982 } 983 984 func (s *ModelSuite) TestDestroyModelPersistentStorageWithForce(c *gc.C) { 985 force := true 986 s.assertDestroyModelPersistentStorage(c, &force) 987 } 988 989 func (s *ModelSuite) TestDestroyModelNonPersistentStorage(c *gc.C) { 990 m, err := s.State.Model() 991 c.Assert(err, jc.ErrorIsNil) 992 993 // Add a unit with non-persistent storage, which should not prevent 994 // Destroy from succeeding. 995 s.Factory.MakeUnit(c, &factory.UnitParams{ 996 Application: s.Factory.MakeApplication(c, &factory.ApplicationParams{ 997 Charm: s.AddTestingCharm(c, "storage-block"), 998 Storage: map[string]state.StorageConstraints{ 999 "data": {Count: 1, Size: 1024, Pool: "loop"}, 1000 }, 1001 }), 1002 }) 1003 1004 err = m.Destroy(state.DestroyModelParams{}) 1005 c.Assert(err, jc.ErrorIsNil) 1006 c.Assert(m.Refresh(), jc.ErrorIsNil) 1007 c.Assert(m.Life(), gc.Equals, state.Dying) 1008 } 1009 1010 func (s *ModelSuite) TestDestroyModelDestroyStorage(c *gc.C) { 1011 s.testDestroyModelDestroyStorage(c, true) 1012 } 1013 1014 func (s *ModelSuite) TestDestroyModelReleaseStorage(c *gc.C) { 1015 s.testDestroyModelDestroyStorage(c, false) 1016 } 1017 1018 func (s *ModelSuite) testDestroyModelDestroyStorage(c *gc.C, destroyStorage bool) { 1019 s.Factory.MakeUnit(c, &factory.UnitParams{ 1020 Application: s.Factory.MakeApplication(c, &factory.ApplicationParams{ 1021 Charm: s.AddTestingCharm(c, "storage-block"), 1022 Storage: map[string]state.StorageConstraints{ 1023 "data": {Count: 1, Size: 1024, Pool: "modelscoped"}, 1024 }, 1025 }), 1026 }) 1027 1028 err := s.Model.Destroy(state.DestroyModelParams{DestroyStorage: &destroyStorage}) 1029 c.Assert(err, jc.ErrorIsNil) 1030 c.Assert(s.Model.Refresh(), jc.ErrorIsNil) 1031 c.Assert(s.Model.Life(), gc.Equals, state.Dying) 1032 1033 assertNeedsCleanup(c, s.State) 1034 assertCleanupRuns(c, s.State) // destroy application 1035 assertCleanupRuns(c, s.State) // destroy unit 1036 assertCleanupRuns(c, s.State) // destroy/release storage 1037 1038 sb, err := state.NewStorageBackend(s.State) 1039 c.Assert(err, jc.ErrorIsNil) 1040 volume, err := sb.Volume(names.NewVolumeTag("0")) 1041 c.Assert(err, jc.ErrorIsNil) 1042 c.Assert(volume.Life(), gc.Equals, state.Dying) 1043 c.Assert(volume.Releasing(), gc.Equals, !destroyStorage) 1044 } 1045 1046 func (s *ModelSuite) assertDestroyModelReleaseStorageUnreleasable(c *gc.C, force *bool) { 1047 s.Factory.MakeUnit(c, &factory.UnitParams{ 1048 Application: s.Factory.MakeApplication(c, &factory.ApplicationParams{ 1049 Charm: s.AddTestingCharm(c, "storage-block"), 1050 Storage: map[string]state.StorageConstraints{ 1051 "data": {Count: 1, Size: 1024, Pool: "modelscoped-unreleasable"}, 1052 }, 1053 }), 1054 }) 1055 1056 destroyStorage := false 1057 err := s.Model.Destroy(state.DestroyModelParams{DestroyStorage: &destroyStorage, Force: force}) 1058 expectedErr := fmt.Sprintf(`failed to destroy model: cannot release volume 0: ` + 1059 `storage provider "modelscoped-unreleasable" does not support releasing storage`) 1060 c.Assert(err, gc.ErrorMatches, expectedErr) 1061 c.Assert(s.Model.Refresh(), jc.ErrorIsNil) 1062 c.Assert(s.Model.Life(), gc.Equals, state.Alive) 1063 assertDoesNotNeedCleanup(c, s.State) 1064 } 1065 1066 func (s *ModelSuite) TestDestroyModelReleaseStorageUnreleasable(c *gc.C) { 1067 s.assertDestroyModelReleaseStorageUnreleasable(c, nil) 1068 } 1069 1070 func (s *ModelSuite) TestDestroyModelReleaseStorageUnreleasableWithForce(c *gc.C) { 1071 force := true 1072 s.assertDestroyModelReleaseStorageUnreleasable(c, &force) 1073 } 1074 1075 func (s *ModelSuite) TestDestroyModelAddApplicationConcurrently(c *gc.C) { 1076 st := s.Factory.MakeModel(c, nil) 1077 defer st.Close() 1078 m, err := st.Model() 1079 c.Assert(err, jc.ErrorIsNil) 1080 1081 defer state.SetBeforeHooks(c, st, func() { 1082 factory.NewFactory(st, s.StatePool).MakeApplication(c, nil) 1083 }).Check() 1084 1085 c.Assert(m.Destroy(state.DestroyModelParams{}), jc.ErrorIsNil) 1086 c.Assert(m.Refresh(), jc.ErrorIsNil) 1087 c.Assert(m.Life(), gc.Equals, state.Dying) 1088 } 1089 1090 func (s *ModelSuite) TestDestroyModelAddMachineConcurrently(c *gc.C) { 1091 st := s.Factory.MakeModel(c, nil) 1092 defer st.Close() 1093 m, err := st.Model() 1094 c.Assert(err, jc.ErrorIsNil) 1095 1096 defer state.SetBeforeHooks(c, st, func() { 1097 factory.NewFactory(st, s.StatePool).MakeMachine(c, nil) 1098 }).Check() 1099 1100 c.Assert(m.Destroy(state.DestroyModelParams{}), jc.ErrorIsNil) 1101 c.Assert(m.Refresh(), jc.ErrorIsNil) 1102 c.Assert(m.Life(), gc.Equals, state.Dying) 1103 } 1104 1105 func (s *ModelSuite) TestDestroyModelEmpty(c *gc.C) { 1106 st := s.Factory.MakeModel(c, nil) 1107 defer st.Close() 1108 m, err := st.Model() 1109 c.Assert(err, jc.ErrorIsNil) 1110 1111 c.Assert(m.Destroy(state.DestroyModelParams{}), jc.ErrorIsNil) 1112 c.Assert(m.Refresh(), jc.ErrorIsNil) 1113 c.Assert(m.Life(), gc.Equals, state.Dying) 1114 c.Assert(st.RemoveDyingModel(), jc.ErrorIsNil) 1115 c.Assert(m.Refresh(), jc.Satisfies, errors.IsNotFound) 1116 } 1117 1118 func (s *ModelSuite) TestDestroyModelWithApplicationOffers(c *gc.C) { 1119 m, err := s.State.Model() 1120 c.Assert(err, jc.ErrorIsNil) 1121 1122 app := s.AddTestingApplication(c, "mysql", s.AddTestingCharm(c, "mysql")) 1123 1124 ao := state.NewApplicationOffers(s.State) 1125 offer, err := ao.AddOffer(crossmodel.AddApplicationOfferArgs{ 1126 OfferName: "hosted-mysql", 1127 ApplicationName: "mysql", 1128 Endpoints: map[string]string{"server": "server"}, 1129 Owner: s.Owner.Id(), 1130 }) 1131 c.Assert(err, jc.ErrorIsNil) 1132 1133 err = m.Destroy(state.DestroyModelParams{}) 1134 c.Assert(err, jc.ErrorIsNil) 1135 c.Assert(m.Refresh(), jc.ErrorIsNil) 1136 c.Assert(m.Life(), gc.Equals, state.Dying) 1137 1138 // Run the cleanups, check that the application and offer are 1139 // both removed. 1140 assertCleanupCount(c, s.State, 2) 1141 1142 _, err = ao.ApplicationOffer(offer.OfferName) 1143 c.Assert(err, jc.Satisfies, errors.IsNotFound) 1144 err = app.Refresh() 1145 c.Assert(err, jc.Satisfies, errors.IsNotFound) 1146 } 1147 1148 func (s *ModelSuite) TestForceDestroySetsForceDestroyed(c *gc.C) { 1149 st := s.Factory.MakeModel(c, nil) 1150 defer st.Close() 1151 1152 model, err := st.Model() 1153 c.Assert(err, jc.ErrorIsNil) 1154 1155 c.Assert(model.ForceDestroyed(), gc.Equals, false) 1156 1157 force := true 1158 err = model.Destroy(state.DestroyModelParams{ 1159 Force: &force, 1160 }) 1161 c.Assert(err, jc.ErrorIsNil) 1162 1163 err = model.Refresh() 1164 c.Assert(err, jc.ErrorIsNil) 1165 1166 c.Assert(model.Life(), gc.Equals, state.Dying) 1167 c.Assert(model.ForceDestroyed(), gc.Equals, true) 1168 } 1169 1170 func (s *ModelSuite) TestDestroyWithTimeoutSetsTimeout(c *gc.C) { 1171 st := s.Factory.MakeModel(c, nil) 1172 defer st.Close() 1173 1174 model, err := st.Model() 1175 c.Assert(err, jc.ErrorIsNil) 1176 1177 c.Assert(model.DestroyTimeout(), gc.IsNil) 1178 1179 timeout := time.Minute 1180 err = model.Destroy(state.DestroyModelParams{ 1181 Timeout: &timeout, 1182 }) 1183 c.Assert(err, jc.ErrorIsNil) 1184 1185 err = model.Refresh() 1186 c.Assert(err, jc.ErrorIsNil) 1187 1188 c.Assert(model.Life(), gc.Equals, state.Dying) 1189 got := model.DestroyTimeout() 1190 c.Assert(got, gc.NotNil) 1191 c.Assert(*got, gc.Equals, time.Minute) 1192 } 1193 1194 func (s *ModelSuite) TestNonForceDestroy(c *gc.C) { 1195 st := s.Factory.MakeModel(c, nil) 1196 defer st.Close() 1197 model, err := st.Model() 1198 c.Assert(err, jc.ErrorIsNil) 1199 1200 noForce := false 1201 err = model.Destroy(state.DestroyModelParams{ 1202 Force: &noForce, 1203 }) 1204 c.Assert(err, jc.ErrorIsNil) 1205 1206 err = model.Refresh() 1207 c.Assert(err, jc.ErrorIsNil) 1208 1209 c.Assert(model.Life(), gc.Equals, state.Dying) 1210 c.Assert(model.ForceDestroyed(), gc.Equals, false) 1211 } 1212 1213 func (s *ModelSuite) TestProcessDyingServerModelTransitionDyingToDead(c *gc.C) { 1214 s.assertDyingModelTransitionDyingToDead(c, s.State) 1215 } 1216 1217 func (s *ModelSuite) TestProcessDyingHostedModelTransitionDyingToDead(c *gc.C) { 1218 st := s.Factory.MakeModel(c, nil) 1219 defer st.Close() 1220 s.assertDyingModelTransitionDyingToDead(c, st) 1221 } 1222 1223 func (s *ModelSuite) assertDyingModelTransitionDyingToDead(c *gc.C, st *state.State) { 1224 // Add a application to prevent the model from transitioning directly to Dead. 1225 // Add the application before getting the Model, otherwise we'll have to run 1226 // the transaction twice, and hit the hook point too early. 1227 app := factory.NewFactory(st, s.StatePool).MakeApplication(c, nil) 1228 model, err := st.Model() 1229 c.Assert(err, jc.ErrorIsNil) 1230 1231 // ProcessDyingModel is called by a worker after Destroy is called. To 1232 // avoid a race, we jump the gun here and test immediately after the 1233 // environement was set to dead. 1234 defer state.SetAfterHooks(c, st, func() { 1235 c.Assert(model.Refresh(), jc.ErrorIsNil) 1236 c.Assert(model.Life(), gc.Equals, state.Dying) 1237 1238 err := app.Destroy() 1239 c.Assert(err, jc.ErrorIsNil) 1240 1241 c.Check(model.UniqueIndexExists(), jc.IsTrue) 1242 c.Assert(st.ProcessDyingModel(), jc.ErrorIsNil) 1243 c.Assert(st.RemoveDyingModel(), jc.ErrorIsNil) 1244 1245 c.Assert(model.Refresh(), jc.Satisfies, errors.IsNotFound) 1246 c.Check(model.UniqueIndexExists(), jc.IsFalse) 1247 }).Check() 1248 1249 c.Assert(model.Destroy(state.DestroyModelParams{}), jc.ErrorIsNil) 1250 } 1251 1252 func (s *ModelSuite) TestProcessDyingModelWithMachinesAndApplicationsNoOp(c *gc.C) { 1253 st := s.Factory.MakeModel(c, nil) 1254 defer st.Close() 1255 1256 // calling ProcessDyingModel on a live environ should fail. 1257 err := st.ProcessDyingModel() 1258 c.Assert(err, gc.ErrorMatches, "model is not dying") 1259 1260 // add some machines and applications 1261 model, err := st.Model() 1262 c.Assert(err, jc.ErrorIsNil) 1263 _, err = st.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits) 1264 c.Assert(err, jc.ErrorIsNil) 1265 application := s.Factory.MakeApplication(c, nil) 1266 1267 ch := state.AddTestingCharm(c, st, "dummy") 1268 args := state.AddApplicationArgs{ 1269 Name: application.Name(), 1270 Charm: ch, 1271 CharmOrigin: &state.CharmOrigin{Platform: &state.Platform{ 1272 OS: "ubuntu", 1273 Channel: "12.10/stable", 1274 }}, 1275 } 1276 _, err = st.AddApplication(args) 1277 c.Assert(err, jc.ErrorIsNil) 1278 1279 assertModel := func(life state.Life, expectedMachines, expectedApplications int) { 1280 c.Assert(model.Refresh(), jc.ErrorIsNil) 1281 c.Assert(model.Life(), gc.Equals, life) 1282 1283 machines, err := st.AllMachines() 1284 c.Assert(err, jc.ErrorIsNil) 1285 c.Assert(machines, gc.HasLen, expectedMachines) 1286 1287 applications, err := st.AllApplications() 1288 c.Assert(err, jc.ErrorIsNil) 1289 c.Assert(applications, gc.HasLen, expectedApplications) 1290 } 1291 1292 // Simulate processing a dying model after an model is set to 1293 // dying, but before the cleanup has removed machines and applications. 1294 defer state.SetAfterHooks(c, st, func() { 1295 assertModel(state.Dying, 1, 1) 1296 err := st.ProcessDyingModel() 1297 c.Assert(errors.Is(err, stateerrors.ModelNotEmptyError), jc.IsTrue) 1298 c.Assert(err, gc.ErrorMatches, `model not empty, found 1 machine, 1 application`) 1299 }).Check() 1300 1301 c.Assert(model.Refresh(), jc.ErrorIsNil) 1302 c.Assert(model.Destroy(state.DestroyModelParams{}), jc.ErrorIsNil) 1303 } 1304 1305 func (s *ModelSuite) TestProcessDyingModelWithVolumeBackedFilesystems(c *gc.C) { 1306 st := s.Factory.MakeModel(c, nil) 1307 defer st.Close() 1308 1309 model, err := st.Model() 1310 c.Assert(err, jc.ErrorIsNil) 1311 1312 machine, err := st.AddOneMachine(state.MachineTemplate{ 1313 Base: state.UbuntuBase("12.10"), 1314 Jobs: []state.MachineJob{state.JobHostUnits}, 1315 Filesystems: []state.HostFilesystemParams{{ 1316 Filesystem: state.FilesystemParams{ 1317 Pool: "modelscoped-block", 1318 Size: 123, 1319 }, 1320 }}, 1321 }) 1322 c.Assert(err, jc.ErrorIsNil) 1323 1324 sb, err := state.NewStorageBackend(st) 1325 c.Assert(err, jc.ErrorIsNil) 1326 filesystems, err := sb.AllFilesystems() 1327 c.Assert(err, jc.ErrorIsNil) 1328 c.Assert(filesystems, gc.HasLen, 1) 1329 1330 err = model.Destroy(state.DestroyModelParams{}) 1331 c.Assert(errors.Is(err, stateerrors.PersistentStorageError), jc.IsTrue) 1332 1333 destroyStorage := true 1334 c.Assert(model.Destroy(state.DestroyModelParams{ 1335 DestroyStorage: &destroyStorage, 1336 }), jc.ErrorIsNil) 1337 1338 err = sb.DetachFilesystem(machine.MachineTag(), names.NewFilesystemTag("0")) 1339 c.Assert(err, jc.ErrorIsNil) 1340 err = sb.RemoveFilesystemAttachment(machine.MachineTag(), names.NewFilesystemTag("0"), false) 1341 c.Assert(err, jc.ErrorIsNil) 1342 err = sb.DetachVolume(machine.MachineTag(), names.NewVolumeTag("0"), false) 1343 c.Assert(err, jc.ErrorIsNil) 1344 err = sb.RemoveVolumeAttachment(machine.MachineTag(), names.NewVolumeTag("0"), false) 1345 c.Assert(err, jc.ErrorIsNil) 1346 c.Assert(machine.EnsureDead(), jc.ErrorIsNil) 1347 c.Assert(machine.Remove(), jc.ErrorIsNil) 1348 1349 // The filesystem will be gone, but the volume is persistent and should 1350 // not have been removed. 1351 err = st.ProcessDyingModel() 1352 c.Assert(errors.Is(err, stateerrors.ModelNotEmptyError), jc.IsTrue) 1353 c.Assert(err, gc.ErrorMatches, `model not empty, found 1 volume, 1 filesystem`) 1354 } 1355 1356 func (s *ModelSuite) TestProcessDyingModelWithVolumes(c *gc.C) { 1357 st := s.Factory.MakeModel(c, nil) 1358 defer st.Close() 1359 1360 model, err := st.Model() 1361 c.Assert(err, jc.ErrorIsNil) 1362 1363 machine, err := st.AddOneMachine(state.MachineTemplate{ 1364 Base: state.UbuntuBase("12.10"), 1365 Jobs: []state.MachineJob{state.JobHostUnits}, 1366 Volumes: []state.HostVolumeParams{{ 1367 Volume: state.VolumeParams{ 1368 Pool: "modelscoped", 1369 Size: 123, 1370 }, 1371 }}, 1372 }) 1373 c.Assert(err, jc.ErrorIsNil) 1374 1375 sb, err := state.NewStorageBackend(st) 1376 c.Assert(err, jc.ErrorIsNil) 1377 volumes, err := sb.AllVolumes() 1378 c.Assert(err, jc.ErrorIsNil) 1379 c.Assert(volumes, gc.HasLen, 1) 1380 volumeTag := volumes[0].VolumeTag() 1381 1382 err = model.Destroy(state.DestroyModelParams{}) 1383 c.Assert(errors.Is(err, stateerrors.PersistentStorageError), jc.IsTrue) 1384 1385 destroyStorage := true 1386 c.Assert(model.Destroy(state.DestroyModelParams{ 1387 DestroyStorage: &destroyStorage, 1388 }), jc.ErrorIsNil) 1389 1390 err = sb.DetachVolume(machine.MachineTag(), volumeTag, false) 1391 c.Assert(err, jc.ErrorIsNil) 1392 err = sb.RemoveVolumeAttachment(machine.MachineTag(), volumeTag, false) 1393 c.Assert(err, jc.ErrorIsNil) 1394 c.Assert(machine.EnsureDead(), jc.ErrorIsNil) 1395 c.Assert(machine.Remove(), jc.ErrorIsNil) 1396 1397 // The volume is persistent and should not have been removed along with 1398 // the machine it was attached to. 1399 err = st.ProcessDyingModel() 1400 c.Assert(errors.Is(err, stateerrors.ModelNotEmptyError), jc.IsTrue) 1401 c.Assert(err, gc.ErrorMatches, `model not empty, found 1 volume`) 1402 } 1403 1404 func (s *ModelSuite) TestProcessDyingControllerModelWithHostedModelsNoOp(c *gc.C) { 1405 // Add a non-empty model to the controller. 1406 st := s.Factory.MakeModel(c, nil) 1407 defer st.Close() 1408 factory.NewFactory(st, s.StatePool).MakeApplication(c, nil) 1409 1410 controllerModel, err := s.State.Model() 1411 c.Assert(err, jc.ErrorIsNil) 1412 c.Assert(controllerModel.Destroy(state.DestroyModelParams{ 1413 DestroyHostedModels: true, 1414 }), jc.ErrorIsNil) 1415 1416 err = s.State.ProcessDyingModel() 1417 c.Assert(errors.Is(err, stateerrors.HasHostedModelsError), jc.IsTrue) 1418 c.Assert(err, gc.ErrorMatches, `hosting 1 other model`) 1419 1420 c.Assert(controllerModel.Refresh(), jc.ErrorIsNil) 1421 c.Assert(controllerModel.Life(), gc.Equals, state.Dying) 1422 } 1423 1424 func (s *ModelSuite) TestListModelUsers(c *gc.C) { 1425 model, err := s.State.Model() 1426 c.Assert(err, jc.ErrorIsNil) 1427 1428 expected := s.addModelUsers(c, s.State) 1429 obtained, err := model.Users() 1430 c.Assert(err, gc.IsNil) 1431 1432 assertObtainedUsersMatchExpectedUsers(c, obtained, expected) 1433 } 1434 1435 func (s *ModelSuite) TestListUsersIgnoredDeletedUsers(c *gc.C) { 1436 model, err := s.State.Model() 1437 c.Assert(err, jc.ErrorIsNil) 1438 1439 expectedUsers := s.addModelUsers(c, s.State) 1440 1441 obtainedUsers, err := model.Users() 1442 c.Assert(err, jc.ErrorIsNil) 1443 assertObtainedUsersMatchExpectedUsers(c, obtainedUsers, expectedUsers) 1444 1445 lastUser := obtainedUsers[len(obtainedUsers)-1] 1446 err = s.State.RemoveUser(lastUser.UserTag) 1447 c.Assert(err, jc.ErrorIsNil) 1448 expectedAfterDeletion := obtainedUsers[:len(obtainedUsers)-1] 1449 1450 obtainedUsers, err = model.Users() 1451 c.Assert(err, jc.ErrorIsNil) 1452 assertObtainedUsersMatchExpectedUsers(c, obtainedUsers, expectedAfterDeletion) 1453 } 1454 1455 func (s *ModelSuite) TestListUsersTwoModels(c *gc.C) { 1456 model, err := s.State.Model() 1457 c.Assert(err, jc.ErrorIsNil) 1458 1459 otherModelState := s.Factory.MakeModel(c, nil) 1460 defer otherModelState.Close() 1461 otherModel, err := otherModelState.Model() 1462 c.Assert(err, jc.ErrorIsNil) 1463 1464 // Add users to both models 1465 expectedUsers := s.addModelUsers(c, s.State) 1466 expectedUsersOtherModel := s.addModelUsers(c, otherModelState) 1467 1468 // test that only the expected users are listed for each model 1469 obtainedUsers, err := model.Users() 1470 c.Assert(err, jc.ErrorIsNil) 1471 assertObtainedUsersMatchExpectedUsers(c, obtainedUsers, expectedUsers) 1472 1473 obtainedUsersOtherModel, err := otherModel.Users() 1474 c.Assert(err, jc.ErrorIsNil) 1475 assertObtainedUsersMatchExpectedUsers(c, obtainedUsersOtherModel, expectedUsersOtherModel) 1476 1477 // It doesn't matter how you obtain the Model. 1478 otherModel2, ph, err := s.StatePool.GetModel(otherModel.UUID()) 1479 c.Assert(err, jc.ErrorIsNil) 1480 defer ph.Release() 1481 obtainedUsersOtherModel2, err := otherModel2.Users() 1482 c.Assert(err, jc.ErrorIsNil) 1483 assertObtainedUsersMatchExpectedUsers(c, obtainedUsersOtherModel2, expectedUsersOtherModel) 1484 } 1485 1486 func (s *ModelSuite) addModelUsers(c *gc.C, st *state.State) (expected []permission.UserAccess) { 1487 // get the model owner 1488 testAdmin := names.NewUserTag("test-admin") 1489 m, err := st.Model() 1490 c.Assert(err, jc.ErrorIsNil) 1491 owner, err := st.UserAccess(testAdmin, m.ModelTag()) 1492 c.Assert(err, jc.ErrorIsNil) 1493 1494 f := factory.NewFactory(st, s.StatePool) 1495 return []permission.UserAccess{ 1496 // we expect the owner to be an existing model user 1497 owner, 1498 // add new users to the model 1499 f.MakeModelUser(c, nil), 1500 f.MakeModelUser(c, nil), 1501 f.MakeModelUser(c, nil), 1502 } 1503 } 1504 1505 func assertObtainedUsersMatchExpectedUsers(c *gc.C, obtainedUsers, expectedUsers []permission.UserAccess) { 1506 c.Assert(len(obtainedUsers), gc.Equals, len(expectedUsers)) 1507 expectedByUser := make(map[string]permission.UserAccess, len(expectedUsers)) 1508 for _, access := range expectedUsers { 1509 expectedByUser[access.UserName] = access 1510 } 1511 for _, obtained := range obtainedUsers { 1512 expect := expectedByUser[obtained.UserName] 1513 // We shouldn't get the same entry again 1514 delete(expectedByUser, obtained.UserName) 1515 c.Check(obtained.Object.Id(), gc.Equals, expect.Object.Id()) 1516 c.Check(obtained.UserTag, gc.Equals, expect.UserTag) 1517 c.Check(obtained.DisplayName, gc.Equals, expect.DisplayName) 1518 c.Check(obtained.CreatedBy, gc.Equals, expect.CreatedBy) 1519 } 1520 c.Check(expectedByUser, jc.DeepEquals, map[string]permission.UserAccess{}) 1521 } 1522 1523 func (s *ModelSuite) TestAllModelUUIDs(c *gc.C) { 1524 st1 := s.Factory.MakeModel(c, nil) 1525 defer st1.Close() 1526 1527 st2 := s.Factory.MakeModel(c, nil) 1528 defer st2.Close() 1529 1530 obtained, err := s.State.AllModelUUIDs() 1531 c.Assert(err, jc.ErrorIsNil) 1532 expected := []string{ 1533 s.State.ModelUUID(), 1534 st1.ModelUUID(), 1535 st2.ModelUUID(), 1536 } 1537 c.Assert(obtained, jc.DeepEquals, expected) 1538 } 1539 1540 func (s *ModelSuite) TestAllModelUUIDsExcludesDead(c *gc.C) { 1541 expected := []string{ 1542 s.State.ModelUUID(), 1543 } 1544 1545 st1 := s.Factory.MakeModel(c, nil) 1546 defer st1.Close() 1547 1548 m1, err := st1.Model() 1549 c.Assert(err, jc.ErrorIsNil) 1550 expectedWithAddition := append(expected, m1.UUID()) 1551 obtained, err := s.State.AllModelUUIDs() 1552 c.Assert(err, jc.ErrorIsNil) 1553 c.Assert(obtained, jc.DeepEquals, expectedWithAddition) 1554 1555 err = m1.SetDead() 1556 c.Assert(err, jc.ErrorIsNil) 1557 1558 obtained, err = s.State.AllModelUUIDs() 1559 c.Assert(err, jc.ErrorIsNil) 1560 c.Assert(obtained, jc.DeepEquals, expected) 1561 1562 obtained, err = s.State.AllModelUUIDsIncludingDead() 1563 c.Assert(err, jc.ErrorIsNil) 1564 c.Assert(obtained, jc.DeepEquals, expectedWithAddition) 1565 } 1566 1567 func (s *ModelSuite) TestHostedModelCount(c *gc.C) { 1568 c.Assert(state.HostedModelCount(c, s.State), gc.Equals, 0) 1569 1570 st1 := s.Factory.MakeModel(c, nil) 1571 defer st1.Close() 1572 c.Assert(state.HostedModelCount(c, s.State), gc.Equals, 1) 1573 1574 st2 := s.Factory.MakeModel(c, nil) 1575 defer st2.Close() 1576 c.Assert(state.HostedModelCount(c, s.State), gc.Equals, 2) 1577 1578 model1, err := st1.Model() 1579 c.Assert(err, jc.ErrorIsNil) 1580 c.Assert(model1.Destroy(state.DestroyModelParams{}), jc.ErrorIsNil) 1581 c.Assert(st1.RemoveDyingModel(), jc.ErrorIsNil) 1582 c.Assert(state.HostedModelCount(c, s.State), gc.Equals, 1) 1583 1584 model2, err := st2.Model() 1585 c.Assert(err, jc.ErrorIsNil) 1586 c.Assert(model2.Destroy(state.DestroyModelParams{}), jc.ErrorIsNil) 1587 c.Assert(st2.RemoveDyingModel(), jc.ErrorIsNil) 1588 c.Assert(state.HostedModelCount(c, s.State), gc.Equals, 0) 1589 } 1590 1591 func (s *ModelSuite) TestNewModelEnvironVersion(c *gc.C) { 1592 v := 123 1593 st := s.Factory.MakeModel(c, &factory.ModelParams{ 1594 EnvironVersion: v, 1595 }) 1596 defer st.Close() 1597 1598 m, err := st.Model() 1599 c.Assert(err, jc.ErrorIsNil) 1600 c.Assert(m.EnvironVersion(), gc.Equals, v) 1601 } 1602 1603 func (s *ModelSuite) TestSetEnvironVersion(c *gc.C) { 1604 v := 123 1605 m, err := s.State.Model() 1606 c.Assert(err, jc.ErrorIsNil) 1607 1608 defer state.SetBeforeHooks(c, s.State, func() { 1609 m, err := s.State.Model() 1610 c.Assert(err, jc.ErrorIsNil) 1611 c.Assert(m.EnvironVersion(), gc.Equals, 0) 1612 err = m.SetEnvironVersion(v) 1613 c.Assert(err, jc.ErrorIsNil) 1614 c.Assert(m.EnvironVersion(), gc.Equals, v) 1615 }).Check() 1616 1617 err = m.SetEnvironVersion(v) 1618 c.Assert(err, jc.ErrorIsNil) 1619 c.Assert(m.EnvironVersion(), gc.Equals, v) 1620 } 1621 1622 func (s *ModelSuite) TestSetEnvironVersionCannotDecrease(c *gc.C) { 1623 m, err := s.State.Model() 1624 c.Assert(err, jc.ErrorIsNil) 1625 1626 defer state.SetBeforeHooks(c, s.State, func() { 1627 m, err := s.State.Model() 1628 c.Assert(err, jc.ErrorIsNil) 1629 err = m.SetEnvironVersion(2) 1630 c.Assert(err, jc.ErrorIsNil) 1631 c.Assert(m.EnvironVersion(), gc.Equals, 2) 1632 }).Check() 1633 1634 err = m.SetEnvironVersion(1) 1635 c.Assert(err, gc.ErrorMatches, `cannot set environ version to 1, which is less than the current version 2`) 1636 // m's cached version is only updated on success 1637 c.Assert(m.EnvironVersion(), gc.Equals, 0) 1638 1639 err = m.Refresh() 1640 c.Assert(err, jc.ErrorIsNil) 1641 c.Assert(m.EnvironVersion(), gc.Equals, 2) 1642 } 1643 1644 func (s *ModelSuite) TestDestroyForceWorksWhenRemoteRelationScopesAreStuck(c *gc.C) { 1645 mysqlEps := []charm.Relation{ 1646 { 1647 Interface: "mysql", 1648 Name: "db", 1649 Role: charm.RoleProvider, 1650 Scope: charm.ScopeGlobal, 1651 }, 1652 } 1653 ms := s.Factory.MakeModel(c, nil) 1654 defer ms.Close() 1655 remoteApp, err := ms.AddRemoteApplication(state.AddRemoteApplicationParams{ 1656 Name: "mysql", 1657 SourceModel: s.Model.ModelTag(), 1658 Token: "t0", 1659 Endpoints: mysqlEps, 1660 }) 1661 c.Assert(err, jc.ErrorIsNil) 1662 1663 wordpress := state.AddTestingApplication(c, ms, "wordpress", state.AddTestingCharm(c, ms, "wordpress")) 1664 eps, err := ms.InferEndpoints("wordpress", "mysql") 1665 c.Assert(err, jc.ErrorIsNil) 1666 rel, err := ms.AddRelation(eps...) 1667 c.Assert(err, jc.ErrorIsNil) 1668 1669 unit, err := wordpress.AddUnit(state.AddUnitParams{}) 1670 c.Assert(err, jc.ErrorIsNil) 1671 f := factory.NewFactory(ms, s.StatePool) 1672 machine := f.MakeMachine(c, nil) 1673 err = unit.AssignToMachine(machine) 1674 c.Assert(err, jc.ErrorIsNil) 1675 localRelUnit, err := rel.Unit(unit) 1676 c.Assert(err, jc.ErrorIsNil) 1677 err = localRelUnit.EnterScope(nil) 1678 c.Assert(err, jc.ErrorIsNil) 1679 1680 remoteRelUnit, err := rel.RemoteUnit("mysql/0") 1681 c.Assert(err, jc.ErrorIsNil) 1682 err = remoteRelUnit.EnterScope(nil) 1683 c.Assert(err, jc.ErrorIsNil) 1684 1685 // Refetch the remoteapp to ensure that its relationcount is 1686 // current. Otherwise it just silently fails? (See errRefresh 1687 // handling in DestroyRemoteApplicationOperation.Build) 1688 err = remoteApp.Refresh() 1689 c.Assert(err, jc.ErrorIsNil) 1690 err = remoteApp.Destroy() 1691 c.Assert(err, jc.ErrorIsNil) 1692 assertLife(c, remoteApp, state.Dying) 1693 1694 err = wordpress.Destroy() 1695 c.Assert(err, jc.ErrorIsNil) 1696 1697 err = localRelUnit.LeaveScope() 1698 c.Assert(err, jc.ErrorIsNil) 1699 err = unit.EnsureDead() 1700 c.Assert(err, jc.ErrorIsNil) 1701 err = unit.Remove() 1702 c.Assert(err, jc.ErrorIsNil) 1703 1704 // Cleanups 1705 assertCleanupCount(c, ms, 1) 1706 1707 // wordpress is kept around because the relation can't be removed. 1708 assertLife(c, wordpress, state.Dying) 1709 1710 // Force-destroying the model cleans them up. 1711 model, err := ms.Model() 1712 c.Assert(err, jc.ErrorIsNil) 1713 force := true 1714 err = model.Destroy(state.DestroyModelParams{ 1715 Force: &force, 1716 }) 1717 c.Assert(err, jc.ErrorIsNil) 1718 1719 assertCleanupCount(c, ms, 4) 1720 assertRemoved(c, wordpress) 1721 c.Assert(model.Refresh(), jc.ErrorIsNil) 1722 c.Assert(model.Life(), gc.Equals, state.Dying) 1723 c.Assert(ms.ProcessDyingModel(), jc.ErrorIsNil) 1724 c.Assert(ms.RemoveDyingModel(), jc.ErrorIsNil) 1725 } 1726 1727 type ModelCloudValidationSuite struct { 1728 mgotesting.MgoSuite 1729 } 1730 1731 var _ = gc.Suite(&ModelCloudValidationSuite{}) 1732 1733 // TODO(axw) concurrency tests when we can modify the cloud definition, 1734 // and update/remove credentials. 1735 1736 func (s *ModelCloudValidationSuite) TestNewModelDifferentCloud(c *gc.C) { 1737 controller, owner := s.initializeState(c, []cloud.Region{{Name: "some-region"}}, []cloud.AuthType{cloud.EmptyAuthType}, nil) 1738 defer controller.Close() 1739 st, err := controller.SystemState() 1740 c.Assert(err, jc.ErrorIsNil) 1741 aCloud := cloud.Cloud{ 1742 Name: "another", 1743 Type: "dummy", 1744 AuthTypes: cloud.AuthTypes{"empty", "userpass"}, 1745 } 1746 err = st.AddCloud(aCloud, owner.Name()) 1747 c.Assert(err, jc.ErrorIsNil) 1748 cfg, _ := createTestModelConfig(c, st.ModelUUID()) 1749 cfg, err = cfg.Apply(map[string]interface{}{"name": "whatever"}) 1750 c.Assert(err, jc.ErrorIsNil) 1751 m, newSt, err := controller.NewModel(state.ModelArgs{ 1752 Type: state.ModelTypeIAAS, 1753 CloudName: "another", 1754 Config: cfg, 1755 Owner: owner, 1756 StorageProviderRegistry: storage.StaticProviderRegistry{}, 1757 }) 1758 c.Assert(err, jc.ErrorIsNil) 1759 defer newSt.Close() 1760 c.Assert(m.CloudName(), gc.Equals, "another") 1761 cloudValue, err := m.Cloud() 1762 c.Assert(err, jc.ErrorIsNil) 1763 c.Assert(cloudValue, jc.DeepEquals, aCloud) 1764 } 1765 1766 func (s *ModelCloudValidationSuite) TestNewModelUnknownCloudRegion(c *gc.C) { 1767 controller, owner := s.initializeState(c, []cloud.Region{{Name: "some-region"}}, []cloud.AuthType{cloud.EmptyAuthType}, nil) 1768 defer controller.Close() 1769 st, err := controller.SystemState() 1770 c.Assert(err, jc.ErrorIsNil) 1771 cfg, _ := createTestModelConfig(c, st.ModelUUID()) 1772 _, _, err = controller.NewModel(state.ModelArgs{ 1773 Type: state.ModelTypeIAAS, 1774 CloudName: "dummy", 1775 CloudRegion: "dummy-region", 1776 Config: cfg, 1777 Owner: owner, 1778 StorageProviderRegistry: storage.StaticProviderRegistry{}, 1779 }) 1780 c.Assert(err, gc.ErrorMatches, `region "dummy-region" not found \(expected one of \["some-region"\]\)`) 1781 } 1782 1783 func (s *ModelCloudValidationSuite) TestNewModelDefaultCloudRegion(c *gc.C) { 1784 controller, owner := s.initializeState(c, []cloud.Region{{Name: "dummy-region"}}, []cloud.AuthType{cloud.EmptyAuthType}, nil) 1785 defer controller.Close() 1786 st, err := controller.SystemState() 1787 c.Assert(err, jc.ErrorIsNil) 1788 cfg, _ := createTestModelConfig(c, st.ModelUUID()) 1789 cfg, err = cfg.Apply(map[string]interface{}{"name": "whatever"}) 1790 c.Assert(err, jc.ErrorIsNil) 1791 m, newSt, err := controller.NewModel(state.ModelArgs{ 1792 Type: state.ModelTypeIAAS, 1793 CloudName: "dummy", 1794 Config: cfg, 1795 Owner: owner, 1796 StorageProviderRegistry: storage.StaticProviderRegistry{}, 1797 }) 1798 c.Assert(err, jc.ErrorIsNil) 1799 defer newSt.Close() 1800 c.Assert(m.CloudRegion(), gc.Equals, "dummy-region") 1801 } 1802 1803 func (s *ModelCloudValidationSuite) TestNewModelMissingCloudRegion(c *gc.C) { 1804 controller, owner := s.initializeState(c, []cloud.Region{{Name: "dummy-region"}, {Name: "dummy-region2"}}, []cloud.AuthType{cloud.EmptyAuthType}, nil) 1805 defer controller.Close() 1806 st, err := controller.SystemState() 1807 c.Assert(err, jc.ErrorIsNil) 1808 cfg, _ := createTestModelConfig(c, st.ModelUUID()) 1809 _, _, err = controller.NewModel(state.ModelArgs{ 1810 Type: state.ModelTypeIAAS, 1811 CloudName: "dummy", 1812 Config: cfg, 1813 Owner: owner, 1814 StorageProviderRegistry: storage.StaticProviderRegistry{}, 1815 }) 1816 c.Assert(err, gc.ErrorMatches, "missing cloud region not valid") 1817 } 1818 1819 func (s *ModelCloudValidationSuite) TestNewModelUnknownCloudCredential(c *gc.C) { 1820 regions := []cloud.Region{{Name: "dummy-region"}} 1821 controllerCredentialTag := names.NewCloudCredentialTag("dummy/test@remote/controller-credential") 1822 controller, owner := s.initializeState( 1823 c, regions, []cloud.AuthType{cloud.UserPassAuthType}, map[names.CloudCredentialTag]cloud.Credential{ 1824 controllerCredentialTag: cloud.NewCredential(cloud.UserPassAuthType, nil), 1825 }, 1826 ) 1827 defer controller.Close() 1828 st, err := controller.SystemState() 1829 c.Assert(err, jc.ErrorIsNil) 1830 unknownCredentialTag := names.NewCloudCredentialTag("dummy/" + owner.Id() + "/unknown-credential") 1831 cfg, _ := createTestModelConfig(c, st.ModelUUID()) 1832 _, _, err = controller.NewModel(state.ModelArgs{ 1833 Type: state.ModelTypeIAAS, 1834 CloudName: "dummy", 1835 CloudRegion: "dummy-region", 1836 Config: cfg, 1837 Owner: owner, 1838 CloudCredential: unknownCredentialTag, 1839 StorageProviderRegistry: storage.StaticProviderRegistry{}, 1840 }) 1841 c.Assert(err, gc.ErrorMatches, `credential "dummy/test@remote/unknown-credential" not found`) 1842 } 1843 1844 func (s *ModelCloudValidationSuite) TestNewModelMissingCloudCredential(c *gc.C) { 1845 regions := []cloud.Region{{Name: "dummy-region"}} 1846 controllerCredentialTag := names.NewCloudCredentialTag("dummy/test@remote/controller-credential") 1847 controller, owner := s.initializeState( 1848 c, regions, []cloud.AuthType{cloud.UserPassAuthType}, map[names.CloudCredentialTag]cloud.Credential{ 1849 controllerCredentialTag: cloud.NewCredential(cloud.UserPassAuthType, nil), 1850 }, 1851 ) 1852 defer controller.Close() 1853 st, err := controller.SystemState() 1854 c.Assert(err, jc.ErrorIsNil) 1855 cfg, _ := createTestModelConfig(c, st.ModelUUID()) 1856 _, _, err = controller.NewModel(state.ModelArgs{ 1857 Type: state.ModelTypeIAAS, 1858 CloudName: "dummy", 1859 CloudRegion: "dummy-region", 1860 Config: cfg, 1861 Owner: owner, 1862 StorageProviderRegistry: storage.StaticProviderRegistry{}, 1863 }) 1864 c.Assert(err, gc.ErrorMatches, "missing CloudCredential not valid") 1865 } 1866 1867 func (s *ModelCloudValidationSuite) TestNewModelMissingCloudCredentialSupportsEmptyAuth(c *gc.C) { 1868 regions := []cloud.Region{ 1869 { 1870 Name: "dummy-region", 1871 Endpoint: "dummy-endpoint", 1872 IdentityEndpoint: "dummy-identity-endpoint", 1873 StorageEndpoint: "dummy-storage-endpoint", 1874 }, 1875 } 1876 controller, owner := s.initializeState(c, regions, []cloud.AuthType{cloud.EmptyAuthType}, nil) 1877 defer controller.Close() 1878 st, err := controller.SystemState() 1879 c.Assert(err, jc.ErrorIsNil) 1880 cfg, _ := createTestModelConfig(c, st.ModelUUID()) 1881 cfg, err = cfg.Apply(map[string]interface{}{"name": "whatever"}) 1882 c.Assert(err, jc.ErrorIsNil) 1883 _, newSt, err := controller.NewModel(state.ModelArgs{ 1884 Type: state.ModelTypeIAAS, 1885 CloudName: "dummy", CloudRegion: "dummy-region", Config: cfg, Owner: owner, 1886 StorageProviderRegistry: storage.StaticProviderRegistry{}, 1887 }) 1888 c.Assert(err, jc.ErrorIsNil) 1889 newSt.Close() 1890 } 1891 1892 func (s *ModelCloudValidationSuite) TestNewModelOtherUserCloudCredential(c *gc.C) { 1893 controllerCredentialTag := names.NewCloudCredentialTag("dummy/test@remote/controller-credential") 1894 controller, _ := s.initializeState( 1895 c, nil, []cloud.AuthType{cloud.UserPassAuthType}, map[names.CloudCredentialTag]cloud.Credential{ 1896 controllerCredentialTag: cloud.NewCredential(cloud.UserPassAuthType, nil), 1897 }, 1898 ) 1899 defer controller.Close() 1900 st, err := controller.SystemState() 1901 c.Assert(err, jc.ErrorIsNil) 1902 owner := factory.NewFactory(st, controller.StatePool()).MakeUser(c, nil).UserTag() 1903 cfg, _ := createTestModelConfig(c, st.ModelUUID()) 1904 _, _, err = controller.NewModel(state.ModelArgs{ 1905 Type: state.ModelTypeIAAS, 1906 CloudName: "dummy", 1907 Config: cfg, 1908 Owner: owner, 1909 CloudCredential: controllerCredentialTag, 1910 StorageProviderRegistry: storage.StaticProviderRegistry{}, 1911 }) 1912 c.Assert(err, gc.ErrorMatches, `credential "dummy/test@remote/controller-credential" not found`) 1913 } 1914 1915 func (s *ModelCloudValidationSuite) initializeState( 1916 c *gc.C, 1917 regions []cloud.Region, 1918 authTypes []cloud.AuthType, 1919 credentials map[names.CloudCredentialTag]cloud.Credential, 1920 ) (*state.Controller, names.UserTag) { 1921 owner := names.NewUserTag("test@remote") 1922 cfg, _ := createTestModelConfig(c, "") 1923 var controllerRegion string 1924 var controllerCredential names.CloudCredentialTag 1925 if len(regions) > 0 { 1926 controllerRegion = regions[0].Name 1927 } 1928 if len(credentials) > 0 { 1929 // pick an arbitrary credential 1930 for controllerCredential = range credentials { 1931 } 1932 } 1933 controllerCfg := testing.FakeControllerConfig() 1934 controller, err := state.Initialize(state.InitializeParams{ 1935 Clock: clock.WallClock, 1936 ControllerConfig: controllerCfg, 1937 ControllerModelArgs: state.ModelArgs{ 1938 Type: state.ModelTypeIAAS, 1939 Owner: owner, 1940 Config: cfg, 1941 CloudName: "dummy", 1942 CloudRegion: controllerRegion, 1943 CloudCredential: controllerCredential, 1944 StorageProviderRegistry: storage.StaticProviderRegistry{}, 1945 }, 1946 Cloud: cloud.Cloud{ 1947 Name: "dummy", 1948 Type: "dummy", 1949 AuthTypes: authTypes, 1950 Regions: regions, 1951 }, 1952 CloudCredentials: credentials, 1953 MongoSession: s.Session, 1954 AdminPassword: "dummy-secret", 1955 }) 1956 c.Assert(err, jc.ErrorIsNil) 1957 return controller, owner 1958 } 1959 1960 func assertCleanupRuns(c *gc.C, st *state.State) { 1961 err := st.Cleanup() 1962 c.Assert(err, jc.ErrorIsNil) 1963 } 1964 1965 func assertNeedsCleanup(c *gc.C, st *state.State) { 1966 actual, err := st.NeedsCleanup() 1967 c.Assert(err, jc.ErrorIsNil) 1968 c.Assert(actual, jc.IsTrue) 1969 } 1970 1971 func assertDoesNotNeedCleanup(c *gc.C, st *state.State) { 1972 actual, err := st.NeedsCleanup() 1973 c.Assert(err, jc.ErrorIsNil) 1974 c.Assert(actual, jc.IsFalse) 1975 } 1976 1977 // assertCleanupCount is useful because certain cleanups cause other cleanups 1978 // to be queued; it makes more sense to just run cleanup again than to unpick 1979 // object destruction so that we run the cleanups inline while running cleanups. 1980 func assertCleanupCount(c *gc.C, st *state.State, count int) { 1981 for i := 0; i < count; i++ { 1982 c.Logf("checking cleanups %d", i) 1983 assertNeedsCleanup(c, st) 1984 assertCleanupRuns(c, st) 1985 } 1986 assertDoesNotNeedCleanup(c, st) 1987 } 1988 1989 // assertCleanupCountDirty is the same as assertCleanupCount, but it 1990 // checks that there are still cleanups to run. 1991 func assertCleanupCountDirty(c *gc.C, st *state.State, count int) { 1992 for i := 0; i < count; i++ { 1993 c.Logf("checking cleanups %d", i) 1994 assertNeedsCleanup(c, st) 1995 assertCleanupRuns(c, st) 1996 } 1997 assertNeedsCleanup(c, st) 1998 } 1999 2000 // The provisioner will remove dead machines once their backing instances are 2001 // stopped. For the tests, we remove them directly. 2002 func assertAllMachinesDeadAndRemove(c *gc.C, st *state.State) { 2003 machines, err := st.AllMachines() 2004 c.Assert(err, jc.ErrorIsNil) 2005 for _, m := range machines { 2006 if m.IsManager() { 2007 continue 2008 } 2009 if _, isContainer := m.ParentId(); isContainer { 2010 continue 2011 } 2012 manual, err := m.IsManual() 2013 c.Assert(err, jc.ErrorIsNil) 2014 if manual { 2015 continue 2016 } 2017 2018 c.Assert(m.Life(), gc.Equals, state.Dead) 2019 c.Assert(m.Remove(), jc.ErrorIsNil) 2020 } 2021 }