github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/state/state_test.go (about) 1 // Copyright 2012-2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state_test 5 6 import ( 7 "fmt" 8 "path/filepath" 9 "sort" 10 "strconv" 11 "time" 12 13 "github.com/juju/errors" 14 "github.com/juju/loggo" 15 "github.com/juju/names" 16 "github.com/juju/replicaset" 17 gitjujutesting "github.com/juju/testing" 18 jc "github.com/juju/testing/checkers" 19 "github.com/juju/txn" 20 "github.com/juju/utils" 21 "github.com/juju/utils/arch" 22 "github.com/juju/utils/series" 23 "github.com/juju/version" 24 gc "gopkg.in/check.v1" 25 "gopkg.in/juju/charm.v6-unstable" 26 "gopkg.in/macaroon.v1" 27 "gopkg.in/mgo.v2" 28 "gopkg.in/mgo.v2/bson" 29 mgotxn "gopkg.in/mgo.v2/txn" 30 31 "github.com/juju/juju/agent" 32 "github.com/juju/juju/constraints" 33 "github.com/juju/juju/environs/config" 34 "github.com/juju/juju/instance" 35 "github.com/juju/juju/mongo" 36 "github.com/juju/juju/mongo/mongotest" 37 "github.com/juju/juju/network" 38 "github.com/juju/juju/state" 39 "github.com/juju/juju/state/multiwatcher" 40 statetesting "github.com/juju/juju/state/testing" 41 "github.com/juju/juju/status" 42 "github.com/juju/juju/storage/poolmanager" 43 "github.com/juju/juju/storage/provider" 44 "github.com/juju/juju/storage/provider/registry" 45 "github.com/juju/juju/testcharms" 46 "github.com/juju/juju/testing" 47 "github.com/juju/juju/testing/factory" 48 jujuversion "github.com/juju/juju/version" 49 ) 50 51 var goodPassword = "foo-12345678901234567890" 52 var alternatePassword = "bar-12345678901234567890" 53 54 // preventUnitDestroyRemove sets a non-pending status on the unit, and hence 55 // prevents it from being unceremoniously removed from state on Destroy. This 56 // is useful because several tests go through a unit's lifecycle step by step, 57 // asserting the behaviour of a given method in each state, and the unit quick- 58 // remove change caused many of these to fail. 59 func preventUnitDestroyRemove(c *gc.C, u *state.Unit) { 60 err := u.SetAgentStatus(status.StatusIdle, "", nil) 61 c.Assert(err, jc.ErrorIsNil) 62 } 63 64 type StateSuite struct { 65 ConnSuite 66 } 67 68 var _ = gc.Suite(&StateSuite{}) 69 70 func (s *StateSuite) SetUpTest(c *gc.C) { 71 s.ConnSuite.SetUpTest(c) 72 s.policy.GetConstraintsValidator = func(*config.Config, state.SupportedArchitecturesQuerier) (constraints.Validator, error) { 73 validator := constraints.NewValidator() 74 validator.RegisterConflicts([]string{constraints.InstanceType}, []string{constraints.Mem}) 75 validator.RegisterUnsupported([]string{constraints.CpuPower}) 76 return validator, nil 77 } 78 } 79 80 func (s *StateSuite) TestIsController(c *gc.C) { 81 c.Assert(s.State.IsController(), jc.IsTrue) 82 st2 := s.Factory.MakeModel(c, nil) 83 defer st2.Close() 84 c.Assert(st2.IsController(), jc.IsFalse) 85 } 86 87 func (s *StateSuite) TestUserModelNameIndex(c *gc.C) { 88 index := state.UserModelNameIndex("BoB", "testing") 89 c.Assert(index, gc.Equals, "bob:testing") 90 } 91 92 func (s *StateSuite) TestDocID(c *gc.C) { 93 id := "wordpress" 94 docID := state.DocID(s.State, id) 95 c.Assert(docID, gc.Equals, s.State.ModelUUID()+":"+id) 96 97 // Ensure that the prefix isn't added if it's already there. 98 docID2 := state.DocID(s.State, docID) 99 c.Assert(docID2, gc.Equals, docID) 100 } 101 102 func (s *StateSuite) TestLocalID(c *gc.C) { 103 id := s.State.ModelUUID() + ":wordpress" 104 localID := state.LocalID(s.State, id) 105 c.Assert(localID, gc.Equals, "wordpress") 106 } 107 108 func (s *StateSuite) TestIDHelpersAreReversible(c *gc.C) { 109 id := "wordpress" 110 docID := state.DocID(s.State, id) 111 localID := state.LocalID(s.State, docID) 112 c.Assert(localID, gc.Equals, id) 113 } 114 115 func (s *StateSuite) TestStrictLocalID(c *gc.C) { 116 id := state.DocID(s.State, "wordpress") 117 localID, err := state.StrictLocalID(s.State, id) 118 c.Assert(localID, gc.Equals, "wordpress") 119 c.Assert(err, jc.ErrorIsNil) 120 } 121 122 func (s *StateSuite) TestStrictLocalIDWithWrongPrefix(c *gc.C) { 123 localID, err := state.StrictLocalID(s.State, "foo:wordpress") 124 c.Assert(localID, gc.Equals, "") 125 c.Assert(err, gc.ErrorMatches, `unexpected id: "foo:wordpress"`) 126 } 127 128 func (s *StateSuite) TestStrictLocalIDWithNoPrefix(c *gc.C) { 129 localID, err := state.StrictLocalID(s.State, "wordpress") 130 c.Assert(localID, gc.Equals, "") 131 c.Assert(err, gc.ErrorMatches, `unexpected id: "wordpress"`) 132 } 133 134 func (s *StateSuite) TestDialAgain(c *gc.C) { 135 // Ensure idempotent operations on Dial are working fine. 136 for i := 0; i < 2; i++ { 137 st, err := state.Open(s.modelTag, statetesting.NewMongoInfo(), mongotest.DialOpts(), state.Policy(nil)) 138 c.Assert(err, jc.ErrorIsNil) 139 c.Assert(st.Close(), gc.IsNil) 140 } 141 } 142 143 func (s *StateSuite) TestOpenAcceptsMissingModelTag(c *gc.C) { 144 st, err := state.Open(names.ModelTag{}, statetesting.NewMongoInfo(), mongotest.DialOpts(), state.Policy(nil)) 145 c.Assert(err, jc.ErrorIsNil) 146 147 c.Check(st.ModelTag(), gc.Equals, s.modelTag) 148 c.Check(st.Close(), jc.ErrorIsNil) 149 } 150 151 func (s *StateSuite) TestOpenRequiresExtantModelTag(c *gc.C) { 152 uuid := utils.MustNewUUID() 153 tag := names.NewModelTag(uuid.String()) 154 st, err := state.Open(tag, statetesting.NewMongoInfo(), mongotest.DialOpts(), state.Policy(nil)) 155 if !c.Check(st, gc.IsNil) { 156 c.Check(st.Close(), jc.ErrorIsNil) 157 } 158 expect := fmt.Sprintf("cannot read model %s: model not found", uuid) 159 c.Check(err, gc.ErrorMatches, expect) 160 } 161 162 func (s *StateSuite) TestOpenSetsModelTag(c *gc.C) { 163 st, err := state.Open(s.modelTag, statetesting.NewMongoInfo(), mongotest.DialOpts(), state.Policy(nil)) 164 c.Assert(err, jc.ErrorIsNil) 165 defer st.Close() 166 167 c.Assert(st.ModelTag(), gc.Equals, s.modelTag) 168 } 169 170 func (s *StateSuite) TestModelUUID(c *gc.C) { 171 c.Assert(s.State.ModelUUID(), gc.Equals, s.modelTag.Id()) 172 } 173 174 func (s *StateSuite) TestNoModelDocs(c *gc.C) { 175 c.Assert(s.State.EnsureModelRemoved(), gc.ErrorMatches, 176 fmt.Sprintf("found documents for model with uuid %s: 1 constraints doc, 2 leases doc, 1 modelusers doc, 1 settings doc, 1 statuses doc", s.State.ModelUUID())) 177 } 178 179 func (s *StateSuite) TestMongoSession(c *gc.C) { 180 session := s.State.MongoSession() 181 c.Assert(session.Ping(), gc.IsNil) 182 } 183 184 func (s *StateSuite) TestWatch(c *gc.C) { 185 // The allWatcher infrastructure is comprehensively tested 186 // elsewhere. This just ensures things are hooked up correctly in 187 // State.Watch() 188 189 w := s.State.Watch() 190 defer w.Stop() 191 deltasC := makeMultiwatcherOutput(w) 192 s.State.StartSync() 193 194 select { 195 case deltas := <-deltasC: 196 // The Watch() call results in an empty "change" reflecting 197 // the initially empty model. 198 c.Assert(deltas, gc.HasLen, 0) 199 case <-time.After(testing.LongWait): 200 c.Fatal("timed out") 201 } 202 203 m := s.Factory.MakeMachine(c, nil) // Generate event 204 s.State.StartSync() 205 206 select { 207 case deltas := <-deltasC: 208 c.Assert(deltas, gc.HasLen, 1) 209 info := deltas[0].Entity.(*multiwatcher.MachineInfo) 210 c.Assert(info.ModelUUID, gc.Equals, s.State.ModelUUID()) 211 c.Assert(info.Id, gc.Equals, m.Id()) 212 case <-time.After(testing.LongWait): 213 c.Fatal("timed out") 214 } 215 } 216 217 func makeMultiwatcherOutput(w *state.Multiwatcher) chan []multiwatcher.Delta { 218 deltasC := make(chan []multiwatcher.Delta) 219 go func() { 220 for { 221 deltas, err := w.Next() 222 if err != nil { 223 return 224 } 225 deltasC <- deltas 226 } 227 }() 228 return deltasC 229 } 230 231 func (s *StateSuite) TestWatchAllModels(c *gc.C) { 232 // The allModelWatcher infrastructure is comprehensively tested 233 // elsewhere. This just ensures things are hooked up correctly in 234 // State.WatchAllModels() 235 236 w := s.State.WatchAllModels() 237 defer w.Stop() 238 deltasC := makeMultiwatcherOutput(w) 239 240 m := s.Factory.MakeMachine(c, nil) 241 242 envSeen := false 243 machineSeen := false 244 timeout := time.After(testing.LongWait) 245 for !envSeen || !machineSeen { 246 select { 247 case deltas := <-deltasC: 248 for _, delta := range deltas { 249 switch e := delta.Entity.(type) { 250 case *multiwatcher.ModelInfo: 251 c.Assert(e.ModelUUID, gc.Equals, s.State.ModelUUID()) 252 envSeen = true 253 case *multiwatcher.MachineInfo: 254 c.Assert(e.ModelUUID, gc.Equals, s.State.ModelUUID()) 255 c.Assert(e.Id, gc.Equals, m.Id()) 256 machineSeen = true 257 } 258 } 259 case <-timeout: 260 c.Fatal("timed out") 261 } 262 } 263 c.Assert(envSeen, jc.IsTrue) 264 c.Assert(machineSeen, jc.IsTrue) 265 } 266 267 type MultiEnvStateSuite struct { 268 ConnSuite 269 OtherState *state.State 270 } 271 272 func (s *MultiEnvStateSuite) SetUpTest(c *gc.C) { 273 s.ConnSuite.SetUpTest(c) 274 s.policy.GetConstraintsValidator = func(*config.Config, state.SupportedArchitecturesQuerier) (constraints.Validator, error) { 275 validator := constraints.NewValidator() 276 validator.RegisterConflicts([]string{constraints.InstanceType}, []string{constraints.Mem}) 277 validator.RegisterUnsupported([]string{constraints.CpuPower}) 278 return validator, nil 279 } 280 s.OtherState = s.Factory.MakeModel(c, nil) 281 } 282 283 func (s *MultiEnvStateSuite) TearDownTest(c *gc.C) { 284 if s.OtherState != nil { 285 s.OtherState.Close() 286 } 287 s.ConnSuite.TearDownTest(c) 288 } 289 290 func (s *MultiEnvStateSuite) Reset(c *gc.C) { 291 s.TearDownTest(c) 292 s.SetUpTest(c) 293 } 294 295 var _ = gc.Suite(&MultiEnvStateSuite{}) 296 297 func (s *MultiEnvStateSuite) TestWatchTwoEnvironments(c *gc.C) { 298 for i, test := range []struct { 299 about string 300 getWatcher func(*state.State) interface{} 301 setUpState func(*state.State) (assertChanges bool) 302 triggerEvent func(*state.State) 303 }{ 304 { 305 about: "machines", 306 getWatcher: func(st *state.State) interface{} { 307 return st.WatchModelMachines() 308 }, 309 triggerEvent: func(st *state.State) { 310 f := factory.NewFactory(st) 311 m := f.MakeMachine(c, nil) 312 c.Assert(m.Id(), gc.Equals, "0") 313 }, 314 }, 315 { 316 about: "containers", 317 getWatcher: func(st *state.State) interface{} { 318 f := factory.NewFactory(st) 319 m := f.MakeMachine(c, nil) 320 c.Assert(m.Id(), gc.Equals, "0") 321 return m.WatchAllContainers() 322 }, 323 triggerEvent: func(st *state.State) { 324 m, err := st.Machine("0") 325 _, err = st.AddMachineInsideMachine( 326 state.MachineTemplate{ 327 Series: "trusty", 328 Jobs: []state.MachineJob{state.JobHostUnits}, 329 }, 330 m.Id(), 331 instance.KVM, 332 ) 333 c.Assert(err, jc.ErrorIsNil) 334 }, 335 }, { 336 about: "LXC only containers", 337 getWatcher: func(st *state.State) interface{} { 338 f := factory.NewFactory(st) 339 m := f.MakeMachine(c, nil) 340 c.Assert(m.Id(), gc.Equals, "0") 341 return m.WatchContainers(instance.LXC) 342 }, 343 triggerEvent: func(st *state.State) { 344 m, err := st.Machine("0") 345 c.Assert(err, jc.ErrorIsNil) 346 _, err = st.AddMachineInsideMachine( 347 state.MachineTemplate{ 348 Series: "trusty", 349 Jobs: []state.MachineJob{state.JobHostUnits}, 350 }, 351 m.Id(), 352 instance.LXC, 353 ) 354 c.Assert(err, jc.ErrorIsNil) 355 }, 356 }, { 357 about: "units", 358 getWatcher: func(st *state.State) interface{} { 359 f := factory.NewFactory(st) 360 m := f.MakeMachine(c, nil) 361 c.Assert(m.Id(), gc.Equals, "0") 362 return m.WatchUnits() 363 }, 364 triggerEvent: func(st *state.State) { 365 m, err := st.Machine("0") 366 c.Assert(err, jc.ErrorIsNil) 367 f := factory.NewFactory(st) 368 f.MakeUnit(c, &factory.UnitParams{Machine: m}) 369 }, 370 }, { 371 about: "services", 372 getWatcher: func(st *state.State) interface{} { 373 return st.WatchServices() 374 }, 375 triggerEvent: func(st *state.State) { 376 f := factory.NewFactory(st) 377 f.MakeService(c, nil) 378 }, 379 }, { 380 about: "relations", 381 getWatcher: func(st *state.State) interface{} { 382 f := factory.NewFactory(st) 383 wordpressCharm := f.MakeCharm(c, &factory.CharmParams{Name: "wordpress"}) 384 wordpress := f.MakeService(c, &factory.ServiceParams{Name: "wordpress", Charm: wordpressCharm}) 385 return wordpress.WatchRelations() 386 }, 387 setUpState: func(st *state.State) bool { 388 f := factory.NewFactory(st) 389 mysqlCharm := f.MakeCharm(c, &factory.CharmParams{Name: "mysql"}) 390 f.MakeService(c, &factory.ServiceParams{Name: "mysql", Charm: mysqlCharm}) 391 return false 392 }, 393 triggerEvent: func(st *state.State) { 394 eps, err := st.InferEndpoints("wordpress", "mysql") 395 c.Assert(err, jc.ErrorIsNil) 396 _, err = st.AddRelation(eps...) 397 c.Assert(err, jc.ErrorIsNil) 398 }, 399 }, { 400 about: "open ports", 401 getWatcher: func(st *state.State) interface{} { 402 return st.WatchOpenedPorts() 403 }, 404 setUpState: func(st *state.State) bool { 405 f := factory.NewFactory(st) 406 mysql := f.MakeService(c, &factory.ServiceParams{Name: "mysql"}) 407 f.MakeUnit(c, &factory.UnitParams{Service: mysql}) 408 return false 409 }, 410 triggerEvent: func(st *state.State) { 411 u, err := st.Unit("mysql/0") 412 c.Assert(err, jc.ErrorIsNil) 413 err = u.OpenPorts("TCP", 100, 200) 414 c.Assert(err, jc.ErrorIsNil) 415 }, 416 }, { 417 about: "cleanups", 418 getWatcher: func(st *state.State) interface{} { 419 return st.WatchCleanups() 420 }, 421 setUpState: func(st *state.State) bool { 422 f := factory.NewFactory(st) 423 wordpressCharm := f.MakeCharm(c, &factory.CharmParams{Name: "wordpress"}) 424 f.MakeService(c, &factory.ServiceParams{Name: "wordpress", Charm: wordpressCharm}) 425 mysqlCharm := f.MakeCharm(c, &factory.CharmParams{Name: "mysql"}) 426 f.MakeService(c, &factory.ServiceParams{Name: "mysql", Charm: mysqlCharm}) 427 428 // add and destroy a relation, so there is something to cleanup. 429 eps, err := st.InferEndpoints("wordpress", "mysql") 430 c.Assert(err, jc.ErrorIsNil) 431 r := f.MakeRelation(c, &factory.RelationParams{Endpoints: eps}) 432 err = r.Destroy() 433 c.Assert(err, jc.ErrorIsNil) 434 435 return false 436 }, 437 triggerEvent: func(st *state.State) { 438 err := st.Cleanup() 439 c.Assert(err, jc.ErrorIsNil) 440 }, 441 }, { 442 about: "reboots", 443 getWatcher: func(st *state.State) interface{} { 444 f := factory.NewFactory(st) 445 m := f.MakeMachine(c, &factory.MachineParams{}) 446 c.Assert(m.Id(), gc.Equals, "0") 447 w, err := m.WatchForRebootEvent() 448 c.Assert(err, jc.ErrorIsNil) 449 return w 450 }, 451 triggerEvent: func(st *state.State) { 452 m, err := st.Machine("0") 453 c.Assert(err, jc.ErrorIsNil) 454 err = m.SetRebootFlag(true) 455 c.Assert(err, jc.ErrorIsNil) 456 }, 457 }, { 458 about: "block devices", 459 getWatcher: func(st *state.State) interface{} { 460 f := factory.NewFactory(st) 461 m := f.MakeMachine(c, &factory.MachineParams{}) 462 c.Assert(m.Id(), gc.Equals, "0") 463 return st.WatchBlockDevices(m.MachineTag()) 464 }, 465 setUpState: func(st *state.State) bool { 466 m, err := st.Machine("0") 467 c.Assert(err, jc.ErrorIsNil) 468 sdb := state.BlockDeviceInfo{DeviceName: "sdb"} 469 err = m.SetMachineBlockDevices(sdb) 470 c.Assert(err, jc.ErrorIsNil) 471 return false 472 }, 473 triggerEvent: func(st *state.State) { 474 m, err := st.Machine("0") 475 c.Assert(err, jc.ErrorIsNil) 476 sdb := state.BlockDeviceInfo{DeviceName: "sdb", Label: "fatty"} 477 err = m.SetMachineBlockDevices(sdb) 478 c.Assert(err, jc.ErrorIsNil) 479 }, 480 }, { 481 about: "statuses", 482 getWatcher: func(st *state.State) interface{} { 483 m, err := st.AddMachine("trusty", state.JobHostUnits) 484 c.Assert(err, jc.ErrorIsNil) 485 c.Assert(m.Id(), gc.Equals, "0") 486 return m.Watch() 487 }, 488 setUpState: func(st *state.State) bool { 489 m, err := st.Machine("0") 490 c.Assert(err, jc.ErrorIsNil) 491 m.SetProvisioned("inst-id", "fake_nonce", nil) 492 return false 493 }, 494 triggerEvent: func(st *state.State) { 495 m, err := st.Machine("0") 496 c.Assert(err, jc.ErrorIsNil) 497 498 err = m.SetStatus("error", "some status", nil) 499 c.Assert(err, jc.ErrorIsNil) 500 }, 501 }, { 502 about: "settings", 503 getWatcher: func(st *state.State) interface{} { 504 return st.WatchServices() 505 }, 506 setUpState: func(st *state.State) bool { 507 f := factory.NewFactory(st) 508 wordpressCharm := f.MakeCharm(c, &factory.CharmParams{Name: "wordpress"}) 509 f.MakeService(c, &factory.ServiceParams{Name: "wordpress", Charm: wordpressCharm}) 510 return false 511 }, 512 triggerEvent: func(st *state.State) { 513 svc, err := st.Service("wordpress") 514 c.Assert(err, jc.ErrorIsNil) 515 516 err = svc.UpdateConfigSettings(charm.Settings{"blog-title": "awesome"}) 517 c.Assert(err, jc.ErrorIsNil) 518 }, 519 }, { 520 about: "action status", 521 getWatcher: func(st *state.State) interface{} { 522 f := factory.NewFactory(st) 523 dummyCharm := f.MakeCharm(c, &factory.CharmParams{Name: "dummy"}) 524 service := f.MakeService(c, &factory.ServiceParams{Name: "dummy", Charm: dummyCharm}) 525 526 unit, err := service.AddUnit() 527 c.Assert(err, jc.ErrorIsNil) 528 return unit.WatchActionNotifications() 529 }, 530 triggerEvent: func(st *state.State) { 531 unit, err := st.Unit("dummy/0") 532 c.Assert(err, jc.ErrorIsNil) 533 _, err = unit.AddAction("snapshot", nil) 534 c.Assert(err, jc.ErrorIsNil) 535 }, 536 }, { 537 about: "min units", 538 getWatcher: func(st *state.State) interface{} { 539 return st.WatchMinUnits() 540 }, 541 setUpState: func(st *state.State) bool { 542 f := factory.NewFactory(st) 543 wordpressCharm := f.MakeCharm(c, &factory.CharmParams{Name: "wordpress"}) 544 _ = f.MakeService(c, &factory.ServiceParams{Name: "wordpress", Charm: wordpressCharm}) 545 return false 546 }, 547 triggerEvent: func(st *state.State) { 548 wordpress, err := st.Service("wordpress") 549 c.Assert(err, jc.ErrorIsNil) 550 err = wordpress.SetMinUnits(2) 551 c.Assert(err, jc.ErrorIsNil) 552 }, 553 }, 554 } { 555 c.Logf("Test %d: %s", i, test.about) 556 func() { 557 getTestWatcher := func(st *state.State) TestWatcherC { 558 var wc interface{} 559 switch w := test.getWatcher(st).(type) { 560 case statetesting.StringsWatcher: 561 wc = statetesting.NewStringsWatcherC(c, st, w) 562 swc := wc.(statetesting.StringsWatcherC) 563 // consume initial event 564 swc.AssertChange() 565 swc.AssertNoChange() 566 case statetesting.NotifyWatcher: 567 wc = statetesting.NewNotifyWatcherC(c, st, w) 568 nwc := wc.(statetesting.NotifyWatcherC) 569 // consume initial event 570 nwc.AssertOneChange() 571 default: 572 c.Fatalf("unknown watcher type %T", w) 573 } 574 return TestWatcherC{ 575 c: c, 576 State: st, 577 Watcher: wc, 578 } 579 } 580 581 checkIsolationForEnv := func(w1, w2 TestWatcherC) { 582 c.Logf("Making changes to model %s", w1.State.ModelUUID()) 583 // switch on type of watcher here 584 if test.setUpState != nil { 585 586 assertChanges := test.setUpState(w1.State) 587 if assertChanges { 588 // Consume events from setup. 589 w1.AssertChanges() 590 w1.AssertNoChange() 591 w2.AssertNoChange() 592 } 593 } 594 test.triggerEvent(w1.State) 595 w1.AssertChanges() 596 w1.AssertNoChange() 597 w2.AssertNoChange() 598 } 599 600 wc1 := getTestWatcher(s.State) 601 defer wc1.Stop() 602 wc2 := getTestWatcher(s.OtherState) 603 defer wc2.Stop() 604 wc2.AssertNoChange() 605 wc1.AssertNoChange() 606 checkIsolationForEnv(wc1, wc2) 607 checkIsolationForEnv(wc2, wc1) 608 }() 609 s.Reset(c) 610 } 611 } 612 613 type TestWatcherC struct { 614 c *gc.C 615 State *state.State 616 Watcher interface{} 617 } 618 619 func (tw *TestWatcherC) AssertChanges() { 620 switch wc := tw.Watcher.(type) { 621 case statetesting.StringsWatcherC: 622 wc.AssertChanges() 623 case statetesting.NotifyWatcherC: 624 wc.AssertOneChange() 625 default: 626 tw.c.Fatalf("unknown watcher type %T", wc) 627 } 628 } 629 630 func (tw *TestWatcherC) AssertNoChange() { 631 switch wc := tw.Watcher.(type) { 632 case statetesting.StringsWatcherC: 633 wc.AssertNoChange() 634 case statetesting.NotifyWatcherC: 635 wc.AssertNoChange() 636 default: 637 tw.c.Fatalf("unknown watcher type %T", wc) 638 } 639 } 640 641 func (tw *TestWatcherC) Stop() { 642 switch wc := tw.Watcher.(type) { 643 case statetesting.StringsWatcherC: 644 statetesting.AssertStop(tw.c, wc.Watcher) 645 case statetesting.NotifyWatcherC: 646 statetesting.AssertStop(tw.c, wc.Watcher) 647 default: 648 tw.c.Fatalf("unknown watcher type %T", wc) 649 } 650 } 651 652 func (s *StateSuite) TestAddresses(c *gc.C) { 653 var err error 654 machines := make([]*state.Machine, 4) 655 machines[0], err = s.State.AddMachine("quantal", state.JobManageModel, state.JobHostUnits) 656 c.Assert(err, jc.ErrorIsNil) 657 machines[1], err = s.State.AddMachine("quantal", state.JobHostUnits) 658 c.Assert(err, jc.ErrorIsNil) 659 changes, err := s.State.EnableHA(3, constraints.Value{}, "quantal", nil) 660 c.Assert(err, jc.ErrorIsNil) 661 c.Assert(changes.Added, gc.HasLen, 3) 662 663 machines[2], err = s.State.Machine("2") 664 c.Assert(err, jc.ErrorIsNil) 665 machines[3], err = s.State.Machine("3") 666 c.Assert(err, jc.ErrorIsNil) 667 668 for i, m := range machines { 669 err := m.SetProviderAddresses(network.Address{ 670 Type: network.IPv4Address, 671 Scope: network.ScopeCloudLocal, 672 Value: fmt.Sprintf("10.0.0.%d", i), 673 }, network.Address{ 674 Type: network.IPv6Address, 675 Scope: network.ScopeCloudLocal, 676 Value: "::1", 677 }, network.Address{ 678 Type: network.IPv4Address, 679 Scope: network.ScopeMachineLocal, 680 Value: "127.0.0.1", 681 }, network.Address{ 682 Type: network.IPv4Address, 683 Scope: network.ScopePublic, 684 Value: "5.4.3.2", 685 }) 686 c.Assert(err, jc.ErrorIsNil) 687 } 688 envConfig, err := s.State.ModelConfig() 689 c.Assert(err, jc.ErrorIsNil) 690 691 addrs, err := s.State.Addresses() 692 c.Assert(err, jc.ErrorIsNil) 693 c.Assert(addrs, gc.HasLen, 3) 694 c.Assert(addrs, jc.SameContents, []string{ 695 fmt.Sprintf("10.0.0.0:%d", envConfig.StatePort()), 696 fmt.Sprintf("10.0.0.2:%d", envConfig.StatePort()), 697 fmt.Sprintf("10.0.0.3:%d", envConfig.StatePort()), 698 }) 699 700 addrs, err = s.State.APIAddressesFromMachines() 701 c.Assert(err, jc.ErrorIsNil) 702 c.Assert(addrs, gc.HasLen, 3) 703 c.Assert(addrs, jc.SameContents, []string{ 704 fmt.Sprintf("10.0.0.0:%d", envConfig.APIPort()), 705 fmt.Sprintf("10.0.0.2:%d", envConfig.APIPort()), 706 fmt.Sprintf("10.0.0.3:%d", envConfig.APIPort()), 707 }) 708 } 709 710 func (s *StateSuite) TestPing(c *gc.C) { 711 c.Assert(s.State.Ping(), gc.IsNil) 712 gitjujutesting.MgoServer.Restart() 713 c.Assert(s.State.Ping(), gc.NotNil) 714 } 715 716 func (s *StateSuite) TestIsNotFound(c *gc.C) { 717 err1 := fmt.Errorf("unrelated error") 718 err2 := errors.NotFoundf("foo") 719 c.Assert(err1, gc.Not(jc.Satisfies), errors.IsNotFound) 720 c.Assert(err2, jc.Satisfies, errors.IsNotFound) 721 } 722 723 func (s *StateSuite) dummyCharm(c *gc.C, curlOverride string) state.CharmInfo { 724 info := state.CharmInfo{ 725 Charm: testcharms.Repo.CharmDir("dummy"), 726 StoragePath: "dummy-1", 727 SHA256: "dummy-1-sha256", 728 } 729 if curlOverride != "" { 730 info.ID = charm.MustParseURL(curlOverride) 731 } else { 732 info.ID = charm.MustParseURL( 733 fmt.Sprintf("local:quantal/%s-%d", info.Charm.Meta().Name, info.Charm.Revision()), 734 ) 735 } 736 return info 737 } 738 739 func (s *StateSuite) TestAddCharm(c *gc.C) { 740 // Check that adding charms from scratch works correctly. 741 info := s.dummyCharm(c, "") 742 dummy, err := s.State.AddCharm(info) 743 c.Assert(err, jc.ErrorIsNil) 744 c.Assert(dummy.URL().String(), gc.Equals, info.ID.String()) 745 746 doc := state.CharmDoc{} 747 err = s.charms.FindId(state.DocID(s.State, info.ID.String())).One(&doc) 748 c.Assert(err, jc.ErrorIsNil) 749 c.Logf("%#v", doc) 750 c.Assert(doc.URL, gc.DeepEquals, info.ID) 751 } 752 753 func (s *StateSuite) TestAddCharmWithAuth(c *gc.C) { 754 // Check that adding charms from scratch works correctly. 755 info := s.dummyCharm(c, "") 756 m, err := macaroon.New([]byte("rootkey"), "id", "loc") 757 c.Assert(err, jc.ErrorIsNil) 758 info.Macaroon = macaroon.Slice{m} 759 dummy, err := s.State.AddCharm(info) 760 c.Assert(err, jc.ErrorIsNil) 761 ms, err := dummy.Macaroon() 762 c.Assert(err, jc.ErrorIsNil) 763 c.Assert(ms, gc.DeepEquals, info.Macaroon) 764 } 765 766 func (s *StateSuite) TestAddCharmUpdatesPlaceholder(c *gc.C) { 767 // Check that adding charms updates any existing placeholder charm 768 // with the same URL. 769 ch := testcharms.Repo.CharmDir("dummy") 770 771 // Add a placeholder charm. 772 curl := charm.MustParseURL("cs:quantal/dummy-1") 773 err := s.State.AddStoreCharmPlaceholder(curl) 774 c.Assert(err, jc.ErrorIsNil) 775 776 // Add a deployed charm. 777 info := state.CharmInfo{ 778 Charm: ch, 779 ID: curl, 780 StoragePath: "dummy-1", 781 SHA256: "dummy-1-sha256", 782 } 783 dummy, err := s.State.AddCharm(info) 784 c.Assert(err, jc.ErrorIsNil) 785 c.Assert(dummy.URL().String(), gc.Equals, curl.String()) 786 787 // Charm doc has been updated. 788 var docs []state.CharmDoc 789 err = s.charms.FindId(state.DocID(s.State, curl.String())).All(&docs) 790 c.Assert(err, jc.ErrorIsNil) 791 c.Assert(docs, gc.HasLen, 1) 792 c.Assert(docs[0].URL, gc.DeepEquals, curl) 793 c.Assert(docs[0].StoragePath, gc.DeepEquals, info.StoragePath) 794 795 // No more placeholder charm. 796 _, err = s.State.LatestPlaceholderCharm(curl) 797 c.Assert(err, jc.Satisfies, errors.IsNotFound) 798 } 799 800 func (s *StateSuite) assertPendingCharmExists(c *gc.C, curl *charm.URL) { 801 // Find charm directly and verify only the charm URL and 802 // PendingUpload are set. 803 doc := state.CharmDoc{} 804 err := s.charms.FindId(state.DocID(s.State, curl.String())).One(&doc) 805 c.Assert(err, jc.ErrorIsNil) 806 c.Logf("%#v", doc) 807 c.Assert(doc.URL, gc.DeepEquals, curl) 808 c.Assert(doc.PendingUpload, jc.IsTrue) 809 c.Assert(doc.Placeholder, jc.IsFalse) 810 c.Assert(doc.Meta, gc.IsNil) 811 c.Assert(doc.Config, gc.IsNil) 812 c.Assert(doc.StoragePath, gc.Equals, "") 813 c.Assert(doc.BundleSha256, gc.Equals, "") 814 815 // Make sure we can't find it with st.Charm(). 816 _, err = s.State.Charm(curl) 817 c.Assert(err, jc.Satisfies, errors.IsNotFound) 818 } 819 820 func (s *StateSuite) TestPrepareLocalCharmUpload(c *gc.C) { 821 // First test the sanity checks. 822 curl, err := s.State.PrepareLocalCharmUpload(charm.MustParseURL("local:quantal/dummy")) 823 c.Assert(err, gc.ErrorMatches, "expected charm URL with revision, got .*") 824 c.Assert(curl, gc.IsNil) 825 curl, err = s.State.PrepareLocalCharmUpload(charm.MustParseURL("cs:quantal/dummy")) 826 c.Assert(err, gc.ErrorMatches, "expected charm URL with local schema, got .*") 827 c.Assert(curl, gc.IsNil) 828 829 // No charm in state, so the call should respect given revision. 830 testCurl := charm.MustParseURL("local:quantal/missing-123") 831 curl, err = s.State.PrepareLocalCharmUpload(testCurl) 832 c.Assert(err, jc.ErrorIsNil) 833 c.Assert(curl, gc.DeepEquals, testCurl) 834 s.assertPendingCharmExists(c, curl) 835 836 // Try adding it again with the same revision and ensure it gets bumped. 837 curl, err = s.State.PrepareLocalCharmUpload(curl) 838 c.Assert(err, jc.ErrorIsNil) 839 c.Assert(curl.Revision, gc.Equals, 124) 840 841 // Also ensure the revision cannot decrease. 842 curl, err = s.State.PrepareLocalCharmUpload(curl.WithRevision(42)) 843 c.Assert(err, jc.ErrorIsNil) 844 c.Assert(curl.Revision, gc.Equals, 125) 845 846 // Check the given revision is respected. 847 curl, err = s.State.PrepareLocalCharmUpload(curl.WithRevision(1234)) 848 c.Assert(err, jc.ErrorIsNil) 849 c.Assert(curl.Revision, gc.Equals, 1234) 850 } 851 852 func (s *StateSuite) TestPrepareStoreCharmUpload(c *gc.C) { 853 // First test the sanity checks. 854 sch, err := s.State.PrepareStoreCharmUpload(charm.MustParseURL("cs:quantal/dummy")) 855 c.Assert(err, gc.ErrorMatches, "expected charm URL with revision, got .*") 856 c.Assert(sch, gc.IsNil) 857 sch, err = s.State.PrepareStoreCharmUpload(charm.MustParseURL("local:quantal/dummy")) 858 c.Assert(err, gc.ErrorMatches, "expected charm URL with cs schema, got .*") 859 c.Assert(sch, gc.IsNil) 860 861 // No charm in state, so the call should respect given revision. 862 testCurl := charm.MustParseURL("cs:quantal/missing-123") 863 sch, err = s.State.PrepareStoreCharmUpload(testCurl) 864 c.Assert(err, jc.ErrorIsNil) 865 c.Assert(sch.URL(), gc.DeepEquals, testCurl) 866 c.Assert(sch.IsUploaded(), jc.IsFalse) 867 868 s.assertPendingCharmExists(c, sch.URL()) 869 870 // Try adding it again with the same revision and ensure we get the same document. 871 schCopy, err := s.State.PrepareStoreCharmUpload(testCurl) 872 c.Assert(err, jc.ErrorIsNil) 873 c.Assert(sch, jc.DeepEquals, schCopy) 874 875 // Now add a charm and try again - we should get the same result 876 // as with AddCharm. 877 info := s.dummyCharm(c, "cs:precise/dummy-2") 878 sch, err = s.State.AddCharm(info) 879 c.Assert(err, jc.ErrorIsNil) 880 schCopy, err = s.State.PrepareStoreCharmUpload(info.ID) 881 c.Assert(err, jc.ErrorIsNil) 882 c.Assert(sch, jc.DeepEquals, schCopy) 883 884 // Finally, try poking around the state with a placeholder and 885 // bundlesha256 to make sure we do the right thing. 886 curl := info.ID.WithRevision(999) 887 first := txn.TestHook{ 888 Before: func() { 889 err := s.State.AddStoreCharmPlaceholder(curl) 890 c.Assert(err, jc.ErrorIsNil) 891 }, 892 After: func() { 893 err := s.charms.RemoveId(state.DocID(s.State, curl.String())) 894 c.Assert(err, jc.ErrorIsNil) 895 }, 896 } 897 second := txn.TestHook{ 898 Before: func() { 899 err := s.State.AddStoreCharmPlaceholder(curl) 900 c.Assert(err, jc.ErrorIsNil) 901 }, 902 After: func() { 903 err := s.charms.UpdateId(state.DocID(s.State, curl.String()), bson.D{{"$set", bson.D{ 904 {"bundlesha256", "fake"}}, 905 }}) 906 c.Assert(err, jc.ErrorIsNil) 907 }, 908 } 909 defer state.SetTestHooks(c, s.State, first, second, first).Check() 910 911 _, err = s.State.PrepareStoreCharmUpload(curl) 912 cause := errors.Cause(err) 913 c.Assert(cause, gc.Equals, txn.ErrExcessiveContention) 914 } 915 916 func (s *StateSuite) TestUpdateUploadedCharm(c *gc.C) { 917 info := s.dummyCharm(c, "") 918 _, err := s.State.AddCharm(info) 919 c.Assert(err, jc.ErrorIsNil) 920 921 // Test with already uploaded and a missing charms. 922 sch, err := s.State.UpdateUploadedCharm(info) 923 c.Assert(err, gc.ErrorMatches, fmt.Sprintf("charm %q already uploaded", info.ID)) 924 c.Assert(sch, gc.IsNil) 925 info.ID = charm.MustParseURL("local:quantal/missing-1") 926 info.SHA256 = "missing" 927 sch, err = s.State.UpdateUploadedCharm(info) 928 c.Assert(err, jc.Satisfies, errors.IsNotFound) 929 c.Assert(sch, gc.IsNil) 930 931 // Test with with an uploaded local charm. 932 _, err = s.State.PrepareLocalCharmUpload(info.ID) 933 c.Assert(err, jc.ErrorIsNil) 934 935 m, err := macaroon.New([]byte("rootkey"), "id", "loc") 936 c.Assert(err, jc.ErrorIsNil) 937 info.Macaroon = macaroon.Slice{m} 938 c.Assert(err, jc.ErrorIsNil) 939 sch, err = s.State.UpdateUploadedCharm(info) 940 c.Assert(err, jc.ErrorIsNil) 941 c.Assert(sch.URL(), gc.DeepEquals, info.ID) 942 c.Assert(sch.Revision(), gc.Equals, info.ID.Revision) 943 c.Assert(sch.IsUploaded(), jc.IsTrue) 944 c.Assert(sch.IsPlaceholder(), jc.IsFalse) 945 c.Assert(sch.Meta(), gc.DeepEquals, info.Charm.Meta()) 946 c.Assert(sch.Config(), gc.DeepEquals, info.Charm.Config()) 947 c.Assert(sch.StoragePath(), gc.DeepEquals, info.StoragePath) 948 c.Assert(sch.BundleSha256(), gc.Equals, "missing") 949 ms, err := sch.Macaroon() 950 c.Assert(err, jc.ErrorIsNil) 951 c.Assert(ms, gc.DeepEquals, info.Macaroon) 952 } 953 954 func (s *StateSuite) TestUpdateUploadedCharmEscapesSpecialCharsInConfig(c *gc.C) { 955 // Make sure when we have mongodb special characters like "$" and 956 // "." in the name of any charm config option, we do proper 957 // escaping before storing them and unescaping after loading. See 958 // also http://pad.lv/1308146. 959 960 // Clone the dummy charm and change the config. 961 configWithProblematicKeys := []byte(` 962 options: 963 $bad.key: {default: bad, description: bad, type: string} 964 not.ok.key: {description: not ok, type: int} 965 valid-key: {description: all good, type: boolean} 966 still$bad.: {description: not good, type: float} 967 $.$: {description: awful, type: string} 968 ...: {description: oh boy, type: int} 969 just$: {description: no no, type: float} 970 `[1:]) 971 chDir := testcharms.Repo.ClonedDirPath(c.MkDir(), "dummy") 972 err := utils.AtomicWriteFile( 973 filepath.Join(chDir, "config.yaml"), 974 configWithProblematicKeys, 975 0666, 976 ) 977 c.Assert(err, jc.ErrorIsNil) 978 ch, err := charm.ReadCharmDir(chDir) 979 c.Assert(err, jc.ErrorIsNil) 980 missingCurl := charm.MustParseURL("local:quantal/missing-1") 981 storagePath := "dummy-1" 982 983 preparedCurl, err := s.State.PrepareLocalCharmUpload(missingCurl) 984 c.Assert(err, jc.ErrorIsNil) 985 info := state.CharmInfo{ 986 Charm: ch, 987 ID: preparedCurl, 988 StoragePath: "dummy-1", 989 SHA256: "missing", 990 } 991 sch, err := s.State.UpdateUploadedCharm(info) 992 c.Assert(err, jc.ErrorIsNil) 993 c.Assert(sch.URL(), gc.DeepEquals, missingCurl) 994 c.Assert(sch.Revision(), gc.Equals, missingCurl.Revision) 995 c.Assert(sch.IsUploaded(), jc.IsTrue) 996 c.Assert(sch.IsPlaceholder(), jc.IsFalse) 997 c.Assert(sch.Meta(), gc.DeepEquals, ch.Meta()) 998 c.Assert(sch.Config(), gc.DeepEquals, ch.Config()) 999 c.Assert(sch.StoragePath(), gc.DeepEquals, storagePath) 1000 c.Assert(sch.BundleSha256(), gc.Equals, "missing") 1001 } 1002 1003 func (s *StateSuite) assertPlaceholderCharmExists(c *gc.C, curl *charm.URL) { 1004 // Find charm directly and verify only the charm URL and 1005 // Placeholder are set. 1006 doc := state.CharmDoc{} 1007 err := s.charms.FindId(state.DocID(s.State, curl.String())).One(&doc) 1008 c.Assert(err, jc.ErrorIsNil) 1009 c.Assert(doc.URL, gc.DeepEquals, curl) 1010 c.Assert(doc.PendingUpload, jc.IsFalse) 1011 c.Assert(doc.Placeholder, jc.IsTrue) 1012 c.Assert(doc.Meta, gc.IsNil) 1013 c.Assert(doc.Config, gc.IsNil) 1014 c.Assert(doc.StoragePath, gc.Equals, "") 1015 c.Assert(doc.BundleSha256, gc.Equals, "") 1016 1017 // Make sure we can't find it with st.Charm(). 1018 _, err = s.State.Charm(curl) 1019 c.Assert(err, jc.Satisfies, errors.IsNotFound) 1020 } 1021 1022 func (s *StateSuite) TestLatestPlaceholderCharm(c *gc.C) { 1023 // Add a deployed charm 1024 info := s.dummyCharm(c, "cs:quantal/dummy-1") 1025 _, err := s.State.AddCharm(info) 1026 c.Assert(err, jc.ErrorIsNil) 1027 1028 // Deployed charm not found. 1029 _, err = s.State.LatestPlaceholderCharm(info.ID) 1030 c.Assert(err, jc.Satisfies, errors.IsNotFound) 1031 1032 // Add a charm reference 1033 curl2 := charm.MustParseURL("cs:quantal/dummy-2") 1034 err = s.State.AddStoreCharmPlaceholder(curl2) 1035 c.Assert(err, jc.ErrorIsNil) 1036 s.assertPlaceholderCharmExists(c, curl2) 1037 1038 // Use a URL with an arbitrary rev to search. 1039 curl := charm.MustParseURL("cs:quantal/dummy-23") 1040 pending, err := s.State.LatestPlaceholderCharm(curl) 1041 c.Assert(err, jc.ErrorIsNil) 1042 c.Assert(pending.URL(), gc.DeepEquals, curl2) 1043 c.Assert(pending.IsPlaceholder(), jc.IsTrue) 1044 c.Assert(pending.Meta(), gc.IsNil) 1045 c.Assert(pending.Config(), gc.IsNil) 1046 c.Assert(pending.StoragePath(), gc.Equals, "") 1047 c.Assert(pending.BundleSha256(), gc.Equals, "") 1048 } 1049 1050 func (s *StateSuite) TestAddStoreCharmPlaceholderErrors(c *gc.C) { 1051 ch := testcharms.Repo.CharmDir("dummy") 1052 curl := charm.MustParseURL( 1053 fmt.Sprintf("local:quantal/%s-%d", ch.Meta().Name, ch.Revision()), 1054 ) 1055 err := s.State.AddStoreCharmPlaceholder(curl) 1056 c.Assert(err, gc.ErrorMatches, "expected charm URL with cs schema, got .*") 1057 1058 curl = charm.MustParseURL("cs:quantal/dummy") 1059 err = s.State.AddStoreCharmPlaceholder(curl) 1060 c.Assert(err, gc.ErrorMatches, "expected charm URL with revision, got .*") 1061 } 1062 1063 func (s *StateSuite) TestAddStoreCharmPlaceholder(c *gc.C) { 1064 curl := charm.MustParseURL("cs:quantal/dummy-1") 1065 err := s.State.AddStoreCharmPlaceholder(curl) 1066 c.Assert(err, jc.ErrorIsNil) 1067 s.assertPlaceholderCharmExists(c, curl) 1068 1069 // Add the same one again, should be a no-op 1070 err = s.State.AddStoreCharmPlaceholder(curl) 1071 c.Assert(err, jc.ErrorIsNil) 1072 s.assertPlaceholderCharmExists(c, curl) 1073 } 1074 1075 func (s *StateSuite) assertAddStoreCharmPlaceholder(c *gc.C) (*charm.URL, *charm.URL, *state.Charm) { 1076 // Add a deployed charm 1077 info := s.dummyCharm(c, "cs:quantal/dummy-1") 1078 dummy, err := s.State.AddCharm(info) 1079 c.Assert(err, jc.ErrorIsNil) 1080 1081 // Add a charm placeholder 1082 curl2 := charm.MustParseURL("cs:quantal/dummy-2") 1083 err = s.State.AddStoreCharmPlaceholder(curl2) 1084 c.Assert(err, jc.ErrorIsNil) 1085 s.assertPlaceholderCharmExists(c, curl2) 1086 1087 // Deployed charm is still there. 1088 existing, err := s.State.Charm(info.ID) 1089 c.Assert(err, jc.ErrorIsNil) 1090 c.Assert(existing, jc.DeepEquals, dummy) 1091 1092 return info.ID, curl2, dummy 1093 } 1094 1095 func (s *StateSuite) TestAddStoreCharmPlaceholderLeavesDeployedCharmsAlone(c *gc.C) { 1096 s.assertAddStoreCharmPlaceholder(c) 1097 } 1098 1099 func (s *StateSuite) TestAddStoreCharmPlaceholderDeletesOlder(c *gc.C) { 1100 curl, curlOldRef, dummy := s.assertAddStoreCharmPlaceholder(c) 1101 1102 // Add a new charm placeholder 1103 curl3 := charm.MustParseURL("cs:quantal/dummy-3") 1104 err := s.State.AddStoreCharmPlaceholder(curl3) 1105 c.Assert(err, jc.ErrorIsNil) 1106 s.assertPlaceholderCharmExists(c, curl3) 1107 1108 // Deployed charm is still there. 1109 existing, err := s.State.Charm(curl) 1110 c.Assert(err, jc.ErrorIsNil) 1111 c.Assert(existing, jc.DeepEquals, dummy) 1112 1113 // Older charm placeholder is gone. 1114 doc := state.CharmDoc{} 1115 err = s.charms.FindId(curlOldRef).One(&doc) 1116 c.Assert(err, gc.Equals, mgo.ErrNotFound) 1117 } 1118 1119 func (s *StateSuite) TestAllCharms(c *gc.C) { 1120 // Add a deployed charm 1121 info := s.dummyCharm(c, "cs:quantal/dummy-1") 1122 sch, err := s.State.AddCharm(info) 1123 c.Assert(err, jc.ErrorIsNil) 1124 1125 // Add a charm reference 1126 curl2 := charm.MustParseURL("cs:quantal/dummy-2") 1127 err = s.State.AddStoreCharmPlaceholder(curl2) 1128 c.Assert(err, jc.ErrorIsNil) 1129 1130 charms, err := s.State.AllCharms() 1131 c.Assert(err, jc.ErrorIsNil) 1132 c.Assert(charms, gc.HasLen, 2) 1133 1134 c.Assert(charms[0], gc.DeepEquals, sch) 1135 c.Assert(charms[1].URL(), gc.DeepEquals, curl2) 1136 } 1137 1138 func (s *StateSuite) AssertMachineCount(c *gc.C, expect int) { 1139 ms, err := s.State.AllMachines() 1140 c.Assert(err, jc.ErrorIsNil) 1141 c.Assert(len(ms), gc.Equals, expect) 1142 } 1143 1144 var jobStringTests = []struct { 1145 job state.MachineJob 1146 s string 1147 }{ 1148 {state.JobHostUnits, "JobHostUnits"}, 1149 {state.JobManageModel, "JobManageModel"}, 1150 {0, "<unknown job 0>"}, 1151 {5, "<unknown job 5>"}, 1152 } 1153 1154 func (s *StateSuite) TestJobString(c *gc.C) { 1155 for _, t := range jobStringTests { 1156 c.Check(t.job.String(), gc.Equals, t.s) 1157 } 1158 } 1159 1160 func (s *StateSuite) TestAddMachineErrors(c *gc.C) { 1161 _, err := s.State.AddMachine("") 1162 c.Assert(err, gc.ErrorMatches, "cannot add a new machine: no series specified") 1163 _, err = s.State.AddMachine("quantal") 1164 c.Assert(err, gc.ErrorMatches, "cannot add a new machine: no jobs specified") 1165 _, err = s.State.AddMachine("quantal", state.JobHostUnits, state.JobHostUnits) 1166 c.Assert(err, gc.ErrorMatches, "cannot add a new machine: duplicate job: .*") 1167 } 1168 1169 func (s *StateSuite) TestAddMachine(c *gc.C) { 1170 allJobs := []state.MachineJob{ 1171 state.JobHostUnits, 1172 state.JobManageModel, 1173 } 1174 m0, err := s.State.AddMachine("quantal", allJobs...) 1175 c.Assert(err, jc.ErrorIsNil) 1176 check := func(m *state.Machine, id, series string, jobs []state.MachineJob) { 1177 c.Assert(m.Id(), gc.Equals, id) 1178 c.Assert(m.Series(), gc.Equals, series) 1179 c.Assert(m.Jobs(), gc.DeepEquals, jobs) 1180 s.assertMachineContainers(c, m, nil) 1181 } 1182 check(m0, "0", "quantal", allJobs) 1183 m0, err = s.State.Machine("0") 1184 c.Assert(err, jc.ErrorIsNil) 1185 check(m0, "0", "quantal", allJobs) 1186 1187 oneJob := []state.MachineJob{state.JobHostUnits} 1188 m1, err := s.State.AddMachine("blahblah", oneJob...) 1189 c.Assert(err, jc.ErrorIsNil) 1190 check(m1, "1", "blahblah", oneJob) 1191 1192 m1, err = s.State.Machine("1") 1193 c.Assert(err, jc.ErrorIsNil) 1194 check(m1, "1", "blahblah", oneJob) 1195 1196 m, err := s.State.AllMachines() 1197 c.Assert(err, jc.ErrorIsNil) 1198 c.Assert(m, gc.HasLen, 2) 1199 check(m[0], "0", "quantal", allJobs) 1200 check(m[1], "1", "blahblah", oneJob) 1201 1202 st2 := s.Factory.MakeModel(c, nil) 1203 defer st2.Close() 1204 _, err = st2.AddMachine("quantal", state.JobManageModel) 1205 c.Assert(err, gc.ErrorMatches, "cannot add a new machine: controller jobs specified but not allowed") 1206 } 1207 1208 func (s *StateSuite) TestAddMachines(c *gc.C) { 1209 oneJob := []state.MachineJob{state.JobHostUnits} 1210 cons := constraints.MustParse("mem=4G") 1211 hc := instance.MustParseHardware("mem=2G") 1212 machineTemplate := state.MachineTemplate{ 1213 Series: "precise", 1214 Constraints: cons, 1215 HardwareCharacteristics: hc, 1216 InstanceId: "inst-id", 1217 Nonce: "nonce", 1218 Jobs: oneJob, 1219 } 1220 machines, err := s.State.AddMachines(machineTemplate) 1221 c.Assert(err, jc.ErrorIsNil) 1222 c.Assert(machines, gc.HasLen, 1) 1223 m, err := s.State.Machine(machines[0].Id()) 1224 c.Assert(err, jc.ErrorIsNil) 1225 instId, err := m.InstanceId() 1226 c.Assert(err, jc.ErrorIsNil) 1227 c.Assert(string(instId), gc.Equals, "inst-id") 1228 c.Assert(m.CheckProvisioned("nonce"), jc.IsTrue) 1229 c.Assert(m.Series(), gc.Equals, "precise") 1230 mcons, err := m.Constraints() 1231 c.Assert(err, jc.ErrorIsNil) 1232 c.Assert(mcons, gc.DeepEquals, cons) 1233 mhc, err := m.HardwareCharacteristics() 1234 c.Assert(err, jc.ErrorIsNil) 1235 c.Assert(*mhc, gc.DeepEquals, hc) 1236 instId, err = m.InstanceId() 1237 c.Assert(err, jc.ErrorIsNil) 1238 c.Assert(string(instId), gc.Equals, "inst-id") 1239 } 1240 1241 func (s *StateSuite) TestAddMachinesEnvironmentDying(c *gc.C) { 1242 env, err := s.State.Model() 1243 c.Assert(err, jc.ErrorIsNil) 1244 err = env.Destroy() 1245 c.Assert(err, jc.ErrorIsNil) 1246 // Check that machines cannot be added if the model is initially Dying. 1247 _, err = s.State.AddMachine("quantal", state.JobHostUnits) 1248 c.Assert(err, gc.ErrorMatches, `cannot add a new machine: model "testenv" is no longer alive`) 1249 } 1250 1251 func (s *StateSuite) TestAddMachinesEnvironmentDyingAfterInitial(c *gc.C) { 1252 env, err := s.State.Model() 1253 c.Assert(err, jc.ErrorIsNil) 1254 // Check that machines cannot be added if the model is initially 1255 // Alive but set to Dying immediately before the transaction is run. 1256 defer state.SetBeforeHooks(c, s.State, func() { 1257 c.Assert(env.Life(), gc.Equals, state.Alive) 1258 c.Assert(env.Destroy(), gc.IsNil) 1259 }).Check() 1260 _, err = s.State.AddMachine("quantal", state.JobHostUnits) 1261 c.Assert(err, gc.ErrorMatches, `cannot add a new machine: model "testenv" is no longer alive`) 1262 } 1263 1264 func (s *StateSuite) TestAddMachinesEnvironmentMigrating(c *gc.C) { 1265 model, err := s.State.Model() 1266 c.Assert(err, jc.ErrorIsNil) 1267 err = model.SetMigrationMode(state.MigrationModeExporting) 1268 c.Assert(err, jc.ErrorIsNil) 1269 // Check that machines cannot be added if the model is initially Dying. 1270 _, err = s.State.AddMachine("quantal", state.JobHostUnits) 1271 c.Assert(err, gc.ErrorMatches, `cannot add a new machine: model "testenv" is being migrated`) 1272 } 1273 1274 func (s *StateSuite) TestAddMachineExtraConstraints(c *gc.C) { 1275 err := s.State.SetModelConstraints(constraints.MustParse("mem=4G")) 1276 c.Assert(err, jc.ErrorIsNil) 1277 oneJob := []state.MachineJob{state.JobHostUnits} 1278 extraCons := constraints.MustParse("cpu-cores=4") 1279 m, err := s.State.AddOneMachine(state.MachineTemplate{ 1280 Series: "quantal", 1281 Constraints: extraCons, 1282 Jobs: oneJob, 1283 }) 1284 c.Assert(err, jc.ErrorIsNil) 1285 c.Assert(m.Id(), gc.Equals, "0") 1286 c.Assert(m.Series(), gc.Equals, "quantal") 1287 c.Assert(m.Jobs(), gc.DeepEquals, oneJob) 1288 expectedCons := constraints.MustParse("cpu-cores=4 mem=4G") 1289 mcons, err := m.Constraints() 1290 c.Assert(err, jc.ErrorIsNil) 1291 c.Assert(mcons, gc.DeepEquals, expectedCons) 1292 } 1293 1294 func (s *StateSuite) TestAddMachineWithVolumes(c *gc.C) { 1295 pm := poolmanager.New(state.NewStateSettings(s.State)) 1296 _, err := pm.Create("loop-pool", provider.LoopProviderType, map[string]interface{}{}) 1297 c.Assert(err, jc.ErrorIsNil) 1298 registry.RegisterEnvironStorageProviders("someprovider", provider.LoopProviderType) 1299 1300 oneJob := []state.MachineJob{state.JobHostUnits} 1301 cons := constraints.MustParse("mem=4G") 1302 hc := instance.MustParseHardware("mem=2G") 1303 1304 volume0 := state.VolumeParams{ 1305 Pool: "loop-pool", 1306 Size: 123, 1307 } 1308 volume1 := state.VolumeParams{ 1309 Pool: "", // use default 1310 Size: 456, 1311 } 1312 volumeAttachment0 := state.VolumeAttachmentParams{} 1313 volumeAttachment1 := state.VolumeAttachmentParams{ 1314 ReadOnly: true, 1315 } 1316 1317 machineTemplate := state.MachineTemplate{ 1318 Series: "precise", 1319 Constraints: cons, 1320 HardwareCharacteristics: hc, 1321 InstanceId: "inst-id", 1322 Nonce: "nonce", 1323 Jobs: oneJob, 1324 Volumes: []state.MachineVolumeParams{{ 1325 volume0, volumeAttachment0, 1326 }, { 1327 volume1, volumeAttachment1, 1328 }}, 1329 } 1330 machines, err := s.State.AddMachines(machineTemplate) 1331 c.Assert(err, jc.ErrorIsNil) 1332 c.Assert(machines, gc.HasLen, 1) 1333 m, err := s.State.Machine(machines[0].Id()) 1334 c.Assert(err, jc.ErrorIsNil) 1335 1336 // When adding the machine, the default pool should 1337 // have been set on the volume params. 1338 machineTemplate.Volumes[1].Volume.Pool = "loop" 1339 1340 volumeAttachments, err := s.State.MachineVolumeAttachments(m.MachineTag()) 1341 c.Assert(err, jc.ErrorIsNil) 1342 c.Assert(volumeAttachments, gc.HasLen, 2) 1343 if volumeAttachments[0].Volume() == names.NewVolumeTag(m.Id()+"/1") { 1344 va := volumeAttachments 1345 va[0], va[1] = va[1], va[0] 1346 } 1347 for i, att := range volumeAttachments { 1348 _, err = att.Info() 1349 c.Assert(err, jc.Satisfies, errors.IsNotProvisioned) 1350 attachmentParams, ok := att.Params() 1351 c.Assert(ok, jc.IsTrue) 1352 c.Check(attachmentParams, gc.Equals, machineTemplate.Volumes[i].Attachment) 1353 volume, err := s.State.Volume(att.Volume()) 1354 c.Assert(err, jc.ErrorIsNil) 1355 _, err = volume.Info() 1356 c.Assert(err, jc.Satisfies, errors.IsNotProvisioned) 1357 volumeParams, ok := volume.Params() 1358 c.Assert(ok, jc.IsTrue) 1359 c.Check(volumeParams, gc.Equals, machineTemplate.Volumes[i].Volume) 1360 } 1361 } 1362 1363 func (s *StateSuite) assertMachineContainers(c *gc.C, m *state.Machine, containers []string) { 1364 mc, err := m.Containers() 1365 c.Assert(err, jc.ErrorIsNil) 1366 c.Assert(mc, gc.DeepEquals, containers) 1367 } 1368 1369 func (s *StateSuite) TestAddContainerToNewMachine(c *gc.C) { 1370 oneJob := []state.MachineJob{state.JobHostUnits} 1371 1372 template := state.MachineTemplate{ 1373 Series: "quantal", 1374 Jobs: oneJob, 1375 } 1376 parentTemplate := state.MachineTemplate{ 1377 Series: "raring", 1378 Jobs: oneJob, 1379 } 1380 m, err := s.State.AddMachineInsideNewMachine(template, parentTemplate, instance.LXC) 1381 c.Assert(err, jc.ErrorIsNil) 1382 c.Assert(m.Id(), gc.Equals, "0/lxc/0") 1383 c.Assert(m.Series(), gc.Equals, "quantal") 1384 c.Assert(m.ContainerType(), gc.Equals, instance.LXC) 1385 mcons, err := m.Constraints() 1386 c.Assert(err, jc.ErrorIsNil) 1387 c.Assert(&mcons, jc.Satisfies, constraints.IsEmpty) 1388 c.Assert(m.Jobs(), gc.DeepEquals, oneJob) 1389 1390 m, err = s.State.Machine("0") 1391 c.Assert(err, jc.ErrorIsNil) 1392 s.assertMachineContainers(c, m, []string{"0/lxc/0"}) 1393 c.Assert(m.Series(), gc.Equals, "raring") 1394 1395 m, err = s.State.Machine("0/lxc/0") 1396 c.Assert(err, jc.ErrorIsNil) 1397 s.assertMachineContainers(c, m, nil) 1398 c.Assert(m.Jobs(), gc.DeepEquals, oneJob) 1399 } 1400 1401 func (s *StateSuite) TestAddContainerToExistingMachine(c *gc.C) { 1402 oneJob := []state.MachineJob{state.JobHostUnits} 1403 m0, err := s.State.AddMachine("quantal", oneJob...) 1404 c.Assert(err, jc.ErrorIsNil) 1405 m1, err := s.State.AddMachine("quantal", oneJob...) 1406 c.Assert(err, jc.ErrorIsNil) 1407 1408 // Add first container. 1409 m, err := s.State.AddMachineInsideMachine(state.MachineTemplate{ 1410 Series: "quantal", 1411 Jobs: []state.MachineJob{state.JobHostUnits}, 1412 }, "1", instance.LXC) 1413 c.Assert(err, jc.ErrorIsNil) 1414 c.Assert(m.Id(), gc.Equals, "1/lxc/0") 1415 c.Assert(m.Series(), gc.Equals, "quantal") 1416 c.Assert(m.ContainerType(), gc.Equals, instance.LXC) 1417 mcons, err := m.Constraints() 1418 c.Assert(err, jc.ErrorIsNil) 1419 c.Assert(&mcons, jc.Satisfies, constraints.IsEmpty) 1420 c.Assert(m.Jobs(), gc.DeepEquals, oneJob) 1421 s.assertMachineContainers(c, m1, []string{"1/lxc/0"}) 1422 1423 s.assertMachineContainers(c, m0, nil) 1424 s.assertMachineContainers(c, m1, []string{"1/lxc/0"}) 1425 m, err = s.State.Machine("1/lxc/0") 1426 c.Assert(err, jc.ErrorIsNil) 1427 s.assertMachineContainers(c, m, nil) 1428 1429 // Add second container. 1430 m, err = s.State.AddMachineInsideMachine(state.MachineTemplate{ 1431 Series: "quantal", 1432 Jobs: []state.MachineJob{state.JobHostUnits}, 1433 }, "1", instance.LXC) 1434 c.Assert(err, jc.ErrorIsNil) 1435 c.Assert(m.Id(), gc.Equals, "1/lxc/1") 1436 c.Assert(m.Series(), gc.Equals, "quantal") 1437 c.Assert(m.ContainerType(), gc.Equals, instance.LXC) 1438 c.Assert(m.Jobs(), gc.DeepEquals, oneJob) 1439 s.assertMachineContainers(c, m1, []string{"1/lxc/0", "1/lxc/1"}) 1440 } 1441 1442 func (s *StateSuite) TestAddContainerToMachineWithKnownSupportedContainers(c *gc.C) { 1443 oneJob := []state.MachineJob{state.JobHostUnits} 1444 host, err := s.State.AddMachine("quantal", oneJob...) 1445 c.Assert(err, jc.ErrorIsNil) 1446 err = host.SetSupportedContainers([]instance.ContainerType{instance.KVM}) 1447 c.Assert(err, jc.ErrorIsNil) 1448 1449 m, err := s.State.AddMachineInsideMachine(state.MachineTemplate{ 1450 Series: "quantal", 1451 Jobs: []state.MachineJob{state.JobHostUnits}, 1452 }, "0", instance.KVM) 1453 c.Assert(err, jc.ErrorIsNil) 1454 c.Assert(m.Id(), gc.Equals, "0/kvm/0") 1455 s.assertMachineContainers(c, host, []string{"0/kvm/0"}) 1456 } 1457 1458 func (s *StateSuite) TestAddInvalidContainerToMachineWithKnownSupportedContainers(c *gc.C) { 1459 oneJob := []state.MachineJob{state.JobHostUnits} 1460 host, err := s.State.AddMachine("quantal", oneJob...) 1461 c.Assert(err, jc.ErrorIsNil) 1462 err = host.SetSupportedContainers([]instance.ContainerType{instance.KVM}) 1463 c.Assert(err, jc.ErrorIsNil) 1464 1465 _, err = s.State.AddMachineInsideMachine(state.MachineTemplate{ 1466 Series: "quantal", 1467 Jobs: []state.MachineJob{state.JobHostUnits}, 1468 }, "0", instance.LXC) 1469 c.Assert(err, gc.ErrorMatches, "cannot add a new machine: machine 0 cannot host lxc containers") 1470 s.assertMachineContainers(c, host, nil) 1471 } 1472 1473 func (s *StateSuite) TestAddContainerToMachineSupportingNoContainers(c *gc.C) { 1474 oneJob := []state.MachineJob{state.JobHostUnits} 1475 host, err := s.State.AddMachine("quantal", oneJob...) 1476 c.Assert(err, jc.ErrorIsNil) 1477 err = host.SupportsNoContainers() 1478 c.Assert(err, jc.ErrorIsNil) 1479 1480 _, err = s.State.AddMachineInsideMachine(state.MachineTemplate{ 1481 Series: "quantal", 1482 Jobs: []state.MachineJob{state.JobHostUnits}, 1483 }, "0", instance.LXC) 1484 c.Assert(err, gc.ErrorMatches, "cannot add a new machine: machine 0 cannot host lxc containers") 1485 s.assertMachineContainers(c, host, nil) 1486 } 1487 1488 func (s *StateSuite) TestInvalidAddMachineParams(c *gc.C) { 1489 instIdTemplate := state.MachineTemplate{ 1490 Series: "quantal", 1491 Jobs: []state.MachineJob{state.JobHostUnits}, 1492 InstanceId: "i-foo", 1493 } 1494 normalTemplate := state.MachineTemplate{ 1495 Series: "quantal", 1496 Jobs: []state.MachineJob{state.JobHostUnits}, 1497 } 1498 _, err := s.State.AddMachineInsideMachine(instIdTemplate, "0", instance.LXC) 1499 c.Check(err, gc.ErrorMatches, "cannot add a new machine: cannot specify instance id for a new container") 1500 1501 _, err = s.State.AddMachineInsideNewMachine(instIdTemplate, normalTemplate, instance.LXC) 1502 c.Check(err, gc.ErrorMatches, "cannot add a new machine: cannot specify instance id for a new container") 1503 1504 _, err = s.State.AddMachineInsideNewMachine(normalTemplate, instIdTemplate, instance.LXC) 1505 c.Check(err, gc.ErrorMatches, "cannot add a new machine: cannot specify instance id for a new container") 1506 1507 _, err = s.State.AddOneMachine(instIdTemplate) 1508 c.Check(err, gc.ErrorMatches, "cannot add a new machine: cannot add a machine with an instance id and no nonce") 1509 1510 _, err = s.State.AddOneMachine(state.MachineTemplate{ 1511 Series: "quantal", 1512 Jobs: []state.MachineJob{state.JobHostUnits, state.JobHostUnits}, 1513 InstanceId: "i-foo", 1514 Nonce: "nonce", 1515 }) 1516 c.Check(err, gc.ErrorMatches, fmt.Sprintf("cannot add a new machine: duplicate job: %s", state.JobHostUnits)) 1517 1518 noSeriesTemplate := state.MachineTemplate{ 1519 Jobs: []state.MachineJob{state.JobHostUnits, state.JobHostUnits}, 1520 } 1521 _, err = s.State.AddOneMachine(noSeriesTemplate) 1522 c.Check(err, gc.ErrorMatches, "cannot add a new machine: no series specified") 1523 1524 _, err = s.State.AddMachineInsideNewMachine(noSeriesTemplate, normalTemplate, instance.LXC) 1525 c.Check(err, gc.ErrorMatches, "cannot add a new machine: no series specified") 1526 1527 _, err = s.State.AddMachineInsideNewMachine(normalTemplate, noSeriesTemplate, instance.LXC) 1528 c.Check(err, gc.ErrorMatches, "cannot add a new machine: no series specified") 1529 1530 _, err = s.State.AddMachineInsideMachine(noSeriesTemplate, "0", instance.LXC) 1531 c.Check(err, gc.ErrorMatches, "cannot add a new machine: no series specified") 1532 } 1533 1534 func (s *StateSuite) TestAddContainerErrors(c *gc.C) { 1535 template := state.MachineTemplate{ 1536 Series: "quantal", 1537 Jobs: []state.MachineJob{state.JobHostUnits}, 1538 } 1539 _, err := s.State.AddMachineInsideMachine(template, "10", instance.LXC) 1540 c.Assert(err, gc.ErrorMatches, "cannot add a new machine: machine 10 not found") 1541 _, err = s.State.AddMachineInsideMachine(template, "10", "") 1542 c.Assert(err, gc.ErrorMatches, "cannot add a new machine: no container type specified") 1543 } 1544 1545 func (s *StateSuite) TestInjectMachineErrors(c *gc.C) { 1546 injectMachine := func(series string, instanceId instance.Id, nonce string, jobs ...state.MachineJob) error { 1547 _, err := s.State.AddOneMachine(state.MachineTemplate{ 1548 Series: series, 1549 Jobs: jobs, 1550 InstanceId: instanceId, 1551 Nonce: nonce, 1552 }) 1553 return err 1554 } 1555 err := injectMachine("", "i-minvalid", agent.BootstrapNonce, state.JobHostUnits) 1556 c.Assert(err, gc.ErrorMatches, "cannot add a new machine: no series specified") 1557 err = injectMachine("quantal", "", agent.BootstrapNonce, state.JobHostUnits) 1558 c.Assert(err, gc.ErrorMatches, "cannot add a new machine: cannot specify a nonce without an instance id") 1559 err = injectMachine("quantal", "i-minvalid", "", state.JobHostUnits) 1560 c.Assert(err, gc.ErrorMatches, "cannot add a new machine: cannot add a machine with an instance id and no nonce") 1561 err = injectMachine("quantal", agent.BootstrapNonce, "i-mlazy") 1562 c.Assert(err, gc.ErrorMatches, "cannot add a new machine: no jobs specified") 1563 } 1564 1565 func (s *StateSuite) TestInjectMachine(c *gc.C) { 1566 cons := constraints.MustParse("mem=4G") 1567 arch := "amd64" 1568 mem := uint64(1024) 1569 disk := uint64(1024) 1570 tags := []string{"foo", "bar"} 1571 template := state.MachineTemplate{ 1572 Series: "quantal", 1573 Jobs: []state.MachineJob{state.JobHostUnits, state.JobManageModel}, 1574 Constraints: cons, 1575 InstanceId: "i-mindustrious", 1576 Nonce: agent.BootstrapNonce, 1577 HardwareCharacteristics: instance.HardwareCharacteristics{ 1578 Arch: &arch, 1579 Mem: &mem, 1580 RootDisk: &disk, 1581 Tags: &tags, 1582 }, 1583 } 1584 m, err := s.State.AddOneMachine(template) 1585 c.Assert(err, jc.ErrorIsNil) 1586 c.Assert(m.Jobs(), gc.DeepEquals, template.Jobs) 1587 instanceId, err := m.InstanceId() 1588 c.Assert(err, jc.ErrorIsNil) 1589 c.Assert(instanceId, gc.Equals, template.InstanceId) 1590 mcons, err := m.Constraints() 1591 c.Assert(err, jc.ErrorIsNil) 1592 c.Assert(cons, gc.DeepEquals, mcons) 1593 characteristics, err := m.HardwareCharacteristics() 1594 c.Assert(err, jc.ErrorIsNil) 1595 c.Assert(*characteristics, gc.DeepEquals, template.HardwareCharacteristics) 1596 1597 // Make sure the bootstrap nonce value is set. 1598 c.Assert(m.CheckProvisioned(template.Nonce), jc.IsTrue) 1599 } 1600 1601 func (s *StateSuite) TestAddContainerToInjectedMachine(c *gc.C) { 1602 oneJob := []state.MachineJob{state.JobHostUnits} 1603 template := state.MachineTemplate{ 1604 Series: "quantal", 1605 InstanceId: "i-mindustrious", 1606 Nonce: agent.BootstrapNonce, 1607 Jobs: []state.MachineJob{state.JobHostUnits, state.JobManageModel}, 1608 } 1609 m0, err := s.State.AddOneMachine(template) 1610 c.Assert(err, jc.ErrorIsNil) 1611 1612 // Add first container. 1613 template = state.MachineTemplate{ 1614 Series: "quantal", 1615 Jobs: []state.MachineJob{state.JobHostUnits}, 1616 } 1617 m, err := s.State.AddMachineInsideMachine(template, "0", instance.LXC) 1618 c.Assert(err, jc.ErrorIsNil) 1619 c.Assert(m.Id(), gc.Equals, "0/lxc/0") 1620 c.Assert(m.Series(), gc.Equals, "quantal") 1621 c.Assert(m.ContainerType(), gc.Equals, instance.LXC) 1622 mcons, err := m.Constraints() 1623 c.Assert(err, jc.ErrorIsNil) 1624 c.Assert(&mcons, jc.Satisfies, constraints.IsEmpty) 1625 c.Assert(m.Jobs(), gc.DeepEquals, oneJob) 1626 s.assertMachineContainers(c, m0, []string{"0/lxc/0"}) 1627 1628 // Add second container. 1629 m, err = s.State.AddMachineInsideMachine(template, "0", instance.LXC) 1630 c.Assert(err, jc.ErrorIsNil) 1631 c.Assert(m.Id(), gc.Equals, "0/lxc/1") 1632 c.Assert(m.Series(), gc.Equals, "quantal") 1633 c.Assert(m.ContainerType(), gc.Equals, instance.LXC) 1634 c.Assert(m.Jobs(), gc.DeepEquals, oneJob) 1635 s.assertMachineContainers(c, m0, []string{"0/lxc/0", "0/lxc/1"}) 1636 } 1637 1638 func (s *StateSuite) TestAddMachineCanOnlyAddControllerForMachine0(c *gc.C) { 1639 template := state.MachineTemplate{ 1640 Series: "quantal", 1641 Jobs: []state.MachineJob{state.JobManageModel}, 1642 } 1643 // Check that we can add the bootstrap machine. 1644 m, err := s.State.AddOneMachine(template) 1645 c.Assert(err, jc.ErrorIsNil) 1646 c.Assert(m.Id(), gc.Equals, "0") 1647 c.Assert(m.WantsVote(), jc.IsTrue) 1648 c.Assert(m.Jobs(), gc.DeepEquals, []state.MachineJob{state.JobManageModel}) 1649 1650 // Check that the controller information is correct. 1651 info, err := s.State.ControllerInfo() 1652 c.Assert(err, jc.ErrorIsNil) 1653 c.Assert(info.ModelTag, gc.Equals, s.modelTag) 1654 c.Assert(info.MachineIds, gc.DeepEquals, []string{"0"}) 1655 c.Assert(info.VotingMachineIds, gc.DeepEquals, []string{"0"}) 1656 1657 const errCannotAdd = "cannot add a new machine: controller jobs specified but not allowed" 1658 m, err = s.State.AddOneMachine(template) 1659 c.Assert(err, gc.ErrorMatches, errCannotAdd) 1660 1661 m, err = s.State.AddMachineInsideMachine(template, "0", instance.LXC) 1662 c.Assert(err, gc.ErrorMatches, errCannotAdd) 1663 1664 m, err = s.State.AddMachineInsideNewMachine(template, template, instance.LXC) 1665 c.Assert(err, gc.ErrorMatches, errCannotAdd) 1666 } 1667 1668 func (s *StateSuite) TestReadMachine(c *gc.C) { 1669 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 1670 c.Assert(err, jc.ErrorIsNil) 1671 expectedId := machine.Id() 1672 machine, err = s.State.Machine(expectedId) 1673 c.Assert(err, jc.ErrorIsNil) 1674 c.Assert(machine.Id(), gc.Equals, expectedId) 1675 } 1676 1677 func (s *StateSuite) TestReadPreModelUUIDMachine(c *gc.C) { 1678 type oldMachineDoc struct { 1679 Id string `bson:"_id"` 1680 Series string 1681 } 1682 1683 s.machines.Insert(&oldMachineDoc{"99", "quantal"}) 1684 1685 machine, err := s.State.Machine("99") 1686 c.Assert(err, jc.ErrorIsNil) 1687 c.Assert(machine.Id(), gc.Equals, "99") 1688 c.Assert(machine.Tag(), gc.Equals, names.NewMachineTag("99")) 1689 c.Assert(machine.Series(), gc.Equals, "quantal") // Sanity check. 1690 } 1691 1692 func (s *StateSuite) TestMachineNotFound(c *gc.C) { 1693 _, err := s.State.Machine("0") 1694 c.Assert(err, gc.ErrorMatches, "machine 0 not found") 1695 c.Assert(err, jc.Satisfies, errors.IsNotFound) 1696 } 1697 1698 func (s *StateSuite) TestMachineIdLessThan(c *gc.C) { 1699 c.Assert(state.MachineIdLessThan("0", "0"), jc.IsFalse) 1700 c.Assert(state.MachineIdLessThan("0", "1"), jc.IsTrue) 1701 c.Assert(state.MachineIdLessThan("1", "0"), jc.IsFalse) 1702 c.Assert(state.MachineIdLessThan("10", "2"), jc.IsFalse) 1703 c.Assert(state.MachineIdLessThan("0", "0/lxc/0"), jc.IsTrue) 1704 c.Assert(state.MachineIdLessThan("0/lxc/0", "0"), jc.IsFalse) 1705 c.Assert(state.MachineIdLessThan("1", "0/lxc/0"), jc.IsFalse) 1706 c.Assert(state.MachineIdLessThan("0/lxc/0", "1"), jc.IsTrue) 1707 c.Assert(state.MachineIdLessThan("0/lxc/0/lxc/1", "0/lxc/0"), jc.IsFalse) 1708 c.Assert(state.MachineIdLessThan("0/kvm/0", "0/lxc/0"), jc.IsTrue) 1709 } 1710 1711 func (s *StateSuite) TestAllMachines(c *gc.C) { 1712 numInserts := 42 1713 for i := 0; i < numInserts; i++ { 1714 m, err := s.State.AddMachine("quantal", state.JobHostUnits) 1715 c.Assert(err, jc.ErrorIsNil) 1716 err = m.SetProvisioned(instance.Id(fmt.Sprintf("foo-%d", i)), "fake_nonce", nil) 1717 c.Assert(err, jc.ErrorIsNil) 1718 err = m.SetAgentVersion(version.MustParseBinary("7.8.9-quantal-amd64")) 1719 c.Assert(err, jc.ErrorIsNil) 1720 err = m.Destroy() 1721 c.Assert(err, jc.ErrorIsNil) 1722 } 1723 s.AssertMachineCount(c, numInserts) 1724 ms, _ := s.State.AllMachines() 1725 for i, m := range ms { 1726 c.Assert(m.Id(), gc.Equals, strconv.Itoa(i)) 1727 instId, err := m.InstanceId() 1728 c.Assert(err, jc.ErrorIsNil) 1729 c.Assert(string(instId), gc.Equals, fmt.Sprintf("foo-%d", i)) 1730 tools, err := m.AgentTools() 1731 c.Check(err, jc.ErrorIsNil) 1732 c.Check(tools.Version, gc.DeepEquals, version.MustParseBinary("7.8.9-quantal-amd64")) 1733 c.Assert(m.Life(), gc.Equals, state.Dying) 1734 } 1735 } 1736 1737 func (s *StateSuite) TestAllRelations(c *gc.C) { 1738 const numRelations = 32 1739 _, err := s.State.AddMachine("quantal", state.JobHostUnits) 1740 c.Assert(err, jc.ErrorIsNil) 1741 mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) 1742 _, err = mysql.AddUnit() 1743 c.Assert(err, jc.ErrorIsNil) 1744 wordpressCharm := s.AddTestingCharm(c, "wordpress") 1745 for i := 0; i < numRelations; i++ { 1746 serviceName := fmt.Sprintf("wordpress%d", i) 1747 wordpress := s.AddTestingService(c, serviceName, wordpressCharm) 1748 _, err = wordpress.AddUnit() 1749 c.Assert(err, jc.ErrorIsNil) 1750 eps, err := s.State.InferEndpoints(serviceName, "mysql") 1751 c.Assert(err, jc.ErrorIsNil) 1752 _, err = s.State.AddRelation(eps...) 1753 c.Assert(err, jc.ErrorIsNil) 1754 } 1755 1756 relations, _ := s.State.AllRelations() 1757 1758 c.Assert(len(relations), gc.Equals, numRelations) 1759 for i, relation := range relations { 1760 c.Assert(relation.Id(), gc.Equals, i) 1761 c.Assert(relation, gc.Matches, fmt.Sprintf("wordpress%d:.+ mysql:.+", i)) 1762 } 1763 } 1764 1765 func (s *StateSuite) TestAddService(c *gc.C) { 1766 ch := s.AddTestingCharm(c, "dummy") 1767 _, err := s.State.AddService(state.AddServiceArgs{Name: "haha/borken", Owner: s.Owner.String(), Charm: ch}) 1768 c.Assert(err, gc.ErrorMatches, `cannot add service "haha/borken": invalid name`) 1769 _, err = s.State.Service("haha/borken") 1770 c.Assert(err, gc.ErrorMatches, `"haha/borken" is not a valid service name`) 1771 1772 // set that a nil charm is handled correctly 1773 _, err = s.State.AddService(state.AddServiceArgs{Name: "umadbro", Owner: s.Owner.String()}) 1774 c.Assert(err, gc.ErrorMatches, `cannot add service "umadbro": charm is nil`) 1775 1776 insettings := charm.Settings{"tuning": "optimized"} 1777 1778 wordpress, err := s.State.AddService(state.AddServiceArgs{Name: "wordpress", Owner: s.Owner.String(), Charm: ch, Settings: insettings}) 1779 c.Assert(err, jc.ErrorIsNil) 1780 c.Assert(wordpress.Name(), gc.Equals, "wordpress") 1781 outsettings, err := wordpress.ConfigSettings() 1782 c.Assert(err, jc.ErrorIsNil) 1783 c.Assert(outsettings, gc.DeepEquals, insettings) 1784 1785 mysql, err := s.State.AddService(state.AddServiceArgs{Name: "mysql", Owner: s.Owner.String(), Charm: ch}) 1786 c.Assert(err, jc.ErrorIsNil) 1787 c.Assert(mysql.Name(), gc.Equals, "mysql") 1788 1789 // Check that retrieving the new created services works correctly. 1790 wordpress, err = s.State.Service("wordpress") 1791 c.Assert(err, jc.ErrorIsNil) 1792 c.Assert(wordpress.Name(), gc.Equals, "wordpress") 1793 ch, _, err = wordpress.Charm() 1794 c.Assert(err, jc.ErrorIsNil) 1795 c.Assert(ch.URL(), gc.DeepEquals, ch.URL()) 1796 mysql, err = s.State.Service("mysql") 1797 c.Assert(err, jc.ErrorIsNil) 1798 c.Assert(mysql.Name(), gc.Equals, "mysql") 1799 ch, _, err = mysql.Charm() 1800 c.Assert(err, jc.ErrorIsNil) 1801 c.Assert(ch.URL(), gc.DeepEquals, ch.URL()) 1802 } 1803 1804 func (s *StateSuite) TestAddServiceEnvironmentDying(c *gc.C) { 1805 charm := s.AddTestingCharm(c, "dummy") 1806 // Check that services cannot be added if the model is initially Dying. 1807 env, err := s.State.Model() 1808 c.Assert(err, jc.ErrorIsNil) 1809 err = env.Destroy() 1810 c.Assert(err, jc.ErrorIsNil) 1811 _, err = s.State.AddService(state.AddServiceArgs{Name: "s1", Owner: s.Owner.String(), Charm: charm}) 1812 c.Assert(err, gc.ErrorMatches, `cannot add service "s1": model "testenv" is no longer alive`) 1813 } 1814 1815 func (s *StateSuite) TestAddServiceEnvironmentMigrating(c *gc.C) { 1816 charm := s.AddTestingCharm(c, "dummy") 1817 // Check that services cannot be added if the model is initially Dying. 1818 env, err := s.State.Model() 1819 c.Assert(err, jc.ErrorIsNil) 1820 err = env.SetMigrationMode(state.MigrationModeExporting) 1821 c.Assert(err, jc.ErrorIsNil) 1822 _, err = s.State.AddService(state.AddServiceArgs{Name: "s1", Owner: s.Owner.String(), Charm: charm}) 1823 c.Assert(err, gc.ErrorMatches, `cannot add service "s1": model "testenv" is being migrated`) 1824 } 1825 1826 func (s *StateSuite) TestAddServiceEnvironmentDyingAfterInitial(c *gc.C) { 1827 charm := s.AddTestingCharm(c, "dummy") 1828 s.AddTestingService(c, "s0", charm) 1829 env, err := s.State.Model() 1830 c.Assert(err, jc.ErrorIsNil) 1831 // Check that services cannot be added if the model is initially 1832 // Alive but set to Dying immediately before the transaction is run. 1833 defer state.SetBeforeHooks(c, s.State, func() { 1834 c.Assert(env.Life(), gc.Equals, state.Alive) 1835 c.Assert(env.Destroy(), gc.IsNil) 1836 }).Check() 1837 _, err = s.State.AddService(state.AddServiceArgs{Name: "s1", Owner: s.Owner.String(), Charm: charm}) 1838 c.Assert(err, gc.ErrorMatches, `cannot add service "s1": model "testenv" is no longer alive`) 1839 } 1840 1841 func (s *StateSuite) TestServiceNotFound(c *gc.C) { 1842 _, err := s.State.Service("bummer") 1843 c.Assert(err, gc.ErrorMatches, `service "bummer" not found`) 1844 c.Assert(err, jc.Satisfies, errors.IsNotFound) 1845 } 1846 1847 func (s *StateSuite) TestAddServiceNoTag(c *gc.C) { 1848 charm := s.AddTestingCharm(c, "dummy") 1849 _, err := s.State.AddService(state.AddServiceArgs{Name: "wordpress", Owner: "admin", Charm: charm}) 1850 c.Assert(err, gc.ErrorMatches, "cannot add service \"wordpress\": Invalid ownertag admin: \"admin\" is not a valid tag") 1851 } 1852 1853 func (s *StateSuite) TestAddServiceNotUserTag(c *gc.C) { 1854 charm := s.AddTestingCharm(c, "dummy") 1855 _, err := s.State.AddService(state.AddServiceArgs{Name: "wordpress", Owner: "machine-3", Charm: charm}) 1856 c.Assert(err, gc.ErrorMatches, "cannot add service \"wordpress\": Invalid ownertag machine-3: \"machine-3\" is not a valid user tag") 1857 } 1858 1859 func (s *StateSuite) TestAddServiceNonExistentUser(c *gc.C) { 1860 charm := s.AddTestingCharm(c, "dummy") 1861 _, err := s.State.AddService(state.AddServiceArgs{Name: "wordpress", Owner: "user-notAuser", Charm: charm}) 1862 c.Assert(err, gc.ErrorMatches, `cannot add service "wordpress": model user "notAuser@local" not found`) 1863 } 1864 1865 func (s *StateSuite) TestAddServiceWithDefaultBindings(c *gc.C) { 1866 ch := s.AddMetaCharm(c, "mysql", metaBase, 42) 1867 svc, err := s.State.AddService(state.AddServiceArgs{ 1868 Name: "yoursql", 1869 Owner: s.Owner.String(), 1870 Charm: ch, 1871 }) 1872 c.Assert(err, jc.ErrorIsNil) 1873 1874 // Read them back to verify defaults and given bindings got merged as 1875 // expected. 1876 bindings, err := svc.EndpointBindings() 1877 c.Assert(err, jc.ErrorIsNil) 1878 c.Assert(bindings, jc.DeepEquals, map[string]string{ 1879 "server": "", 1880 "client": "", 1881 "cluster": "", 1882 }) 1883 1884 // Removing the service also removes its bindings. 1885 err = svc.Destroy() 1886 c.Assert(err, jc.ErrorIsNil) 1887 err = svc.Refresh() 1888 c.Assert(err, jc.Satisfies, errors.IsNotFound) 1889 state.AssertEndpointBindingsNotFoundForService(c, svc) 1890 } 1891 1892 func (s *StateSuite) TestAddServiceWithSpecifiedBindings(c *gc.C) { 1893 // Add extra spaces to use in bindings. 1894 _, err := s.State.AddSpace("db", "", nil, false) 1895 c.Assert(err, jc.ErrorIsNil) 1896 _, err = s.State.AddSpace("client", "", nil, true) 1897 c.Assert(err, jc.ErrorIsNil) 1898 1899 // Specify some bindings, but not all when adding the service. 1900 ch := s.AddMetaCharm(c, "mysql", metaBase, 43) 1901 svc, err := s.State.AddService(state.AddServiceArgs{ 1902 Name: "yoursql", 1903 Owner: s.Owner.String(), 1904 Charm: ch, 1905 EndpointBindings: map[string]string{ 1906 "client": "client", 1907 "cluster": "db", 1908 }, 1909 }) 1910 c.Assert(err, jc.ErrorIsNil) 1911 1912 // Read them back to verify defaults and given bindings got merged as 1913 // expected. 1914 bindings, err := svc.EndpointBindings() 1915 c.Assert(err, jc.ErrorIsNil) 1916 c.Assert(bindings, jc.DeepEquals, map[string]string{ 1917 "server": "", // inherited from defaults. 1918 "client": "client", 1919 "cluster": "db", 1920 }) 1921 } 1922 1923 func (s *StateSuite) TestAddServiceWithInvalidBindings(c *gc.C) { 1924 charm := s.AddMetaCharm(c, "mysql", metaBase, 44) 1925 // Add extra spaces to use in bindings. 1926 _, err := s.State.AddSpace("db", "", nil, false) 1927 c.Assert(err, jc.ErrorIsNil) 1928 _, err = s.State.AddSpace("client", "", nil, true) 1929 c.Assert(err, jc.ErrorIsNil) 1930 1931 for i, test := range []struct { 1932 about string 1933 bindings map[string]string 1934 expectedError string 1935 }{{ 1936 about: "extra endpoint bound to unknown space", 1937 bindings: map[string]string{"extra": "missing"}, 1938 expectedError: `unknown endpoint "extra" not valid`, 1939 }, { 1940 about: "extra endpoint not bound to a space", 1941 bindings: map[string]string{"extra": ""}, 1942 expectedError: `unknown endpoint "extra" not valid`, 1943 }, { 1944 about: "two extra endpoints, both bound to known spaces", 1945 bindings: map[string]string{"ex1": "db", "ex2": "client"}, 1946 expectedError: `unknown endpoint "ex(1|2)" not valid`, 1947 }, { 1948 about: "empty endpoint bound to unknown space", 1949 bindings: map[string]string{"": "anything"}, 1950 expectedError: `unknown endpoint "" not valid`, 1951 }, { 1952 about: "empty endpoint not bound to a space", 1953 bindings: map[string]string{"": ""}, 1954 expectedError: `unknown endpoint "" not valid`, 1955 }, { 1956 about: "known endpoint bound to unknown space", 1957 bindings: map[string]string{"server": "invalid"}, 1958 expectedError: `unknown space "invalid" not valid`, 1959 }, { 1960 about: "known endpoint bound correctly and an extra endpoint", 1961 bindings: map[string]string{"server": "db", "foo": "public"}, 1962 expectedError: `unknown endpoint "foo" not valid`, 1963 }} { 1964 c.Logf("test #%d: %s", i, test.about) 1965 1966 _, err := s.State.AddService(state.AddServiceArgs{ 1967 Name: "yoursql", 1968 Owner: s.Owner.String(), 1969 Charm: charm, 1970 EndpointBindings: test.bindings, 1971 }) 1972 c.Check(err, gc.ErrorMatches, `cannot add service "yoursql": `+test.expectedError) 1973 c.Check(err, jc.Satisfies, errors.IsNotValid) 1974 } 1975 } 1976 1977 func (s *StateSuite) TestAddServiceMachinePlacementInvalidSeries(c *gc.C) { 1978 m, err := s.State.AddMachine("trusty", state.JobHostUnits) 1979 c.Assert(err, jc.ErrorIsNil) 1980 1981 charm := s.AddTestingCharm(c, "dummy") 1982 _, err = s.State.AddService(state.AddServiceArgs{ 1983 Name: "wordpress", Owner: s.Owner.String(), Charm: charm, 1984 Placement: []*instance.Placement{ 1985 {instance.MachineScope, m.Id()}, 1986 }, 1987 }) 1988 c.Assert(err, gc.ErrorMatches, "cannot add service \"wordpress\": cannot deploy to machine .*: series does not match") 1989 } 1990 1991 func (s *StateSuite) TestAddServiceIncompatibleOSWithSeriesInURL(c *gc.C) { 1992 charm := s.AddTestingCharm(c, "dummy") 1993 // A charm with a series in its URL is implicitly supported by that 1994 // series only. 1995 _, err := s.State.AddService(state.AddServiceArgs{ 1996 Name: "wordpress", Owner: s.Owner.String(), Charm: charm, 1997 Series: "centos7", 1998 }) 1999 c.Assert(err, gc.ErrorMatches, `cannot add service "wordpress": series "centos7" \(OS \"CentOS"\) not supported by charm, supported series are "quantal"`) 2000 } 2001 2002 func (s *StateSuite) TestAddServiceCompatibleOSWithSeriesInURL(c *gc.C) { 2003 charm := s.AddTestingCharm(c, "dummy") 2004 // A charm with a series in its URL is implicitly supported by that 2005 // series only. 2006 _, err := s.State.AddService(state.AddServiceArgs{ 2007 Name: "wordpress", Owner: s.Owner.String(), Charm: charm, 2008 Series: charm.URL().Series, 2009 }) 2010 c.Assert(err, jc.ErrorIsNil) 2011 } 2012 2013 func (s *StateSuite) TestAddServiceCompatibleOSWithNoExplicitSupportedSeries(c *gc.C) { 2014 // If a charm doesn't declare any series, we can add it with any series we choose. 2015 charm := s.AddSeriesCharm(c, "dummy", "") 2016 _, err := s.State.AddService(state.AddServiceArgs{ 2017 Name: "wordpress", Owner: s.Owner.String(), Charm: charm, 2018 Series: "quantal", 2019 }) 2020 c.Assert(err, jc.ErrorIsNil) 2021 } 2022 2023 func (s *StateSuite) TestAddServiceOSIncompatibleWithSupportedSeries(c *gc.C) { 2024 charm := state.AddTestingCharmMultiSeries(c, s.State, "multi-series") 2025 // A charm with supported series can only be force-deployed to series 2026 // of the same operating systems as the suppoted series. 2027 _, err := s.State.AddService(state.AddServiceArgs{ 2028 Name: "wordpress", Owner: s.Owner.String(), Charm: charm, 2029 Series: "centos7", 2030 }) 2031 c.Assert(err, gc.ErrorMatches, `cannot add service "wordpress": series "centos7" \(OS "CentOS"\) not supported by charm, supported series are "precise, trusty"`) 2032 } 2033 2034 func (s *StateSuite) TestAllServices(c *gc.C) { 2035 charm := s.AddTestingCharm(c, "dummy") 2036 services, err := s.State.AllServices() 2037 c.Assert(err, jc.ErrorIsNil) 2038 c.Assert(len(services), gc.Equals, 0) 2039 2040 // Check that after adding services the result is ok. 2041 _, err = s.State.AddService(state.AddServiceArgs{Name: "wordpress", Owner: s.Owner.String(), Charm: charm}) 2042 c.Assert(err, jc.ErrorIsNil) 2043 services, err = s.State.AllServices() 2044 c.Assert(err, jc.ErrorIsNil) 2045 c.Assert(len(services), gc.Equals, 1) 2046 2047 _, err = s.State.AddService(state.AddServiceArgs{Name: "mysql", Owner: s.Owner.String(), Charm: charm}) 2048 c.Assert(err, jc.ErrorIsNil) 2049 services, err = s.State.AllServices() 2050 c.Assert(err, jc.ErrorIsNil) 2051 c.Assert(services, gc.HasLen, 2) 2052 2053 // Check the returned service, order is defined by sorted keys. 2054 names := make([]string, len(services)) 2055 for i, svc := range services { 2056 names[i] = svc.Name() 2057 } 2058 sort.Strings(names) 2059 c.Assert(names[0], gc.Equals, "mysql") 2060 c.Assert(names[1], gc.Equals, "wordpress") 2061 } 2062 2063 var inferEndpointsTests = []struct { 2064 summary string 2065 inputs [][]string 2066 eps []state.Endpoint 2067 err string 2068 }{ 2069 { 2070 summary: "insane args", 2071 inputs: [][]string{nil}, 2072 err: `cannot relate 0 endpoints`, 2073 }, { 2074 summary: "insane args", 2075 inputs: [][]string{{"blah", "blur", "bleurgh"}}, 2076 err: `cannot relate 3 endpoints`, 2077 }, { 2078 summary: "invalid args", 2079 inputs: [][]string{ 2080 {"ping:"}, 2081 {":pong"}, 2082 {":"}, 2083 }, 2084 err: `invalid endpoint ".*"`, 2085 }, { 2086 summary: "unknown service", 2087 inputs: [][]string{{"wooble"}}, 2088 err: `service "wooble" not found`, 2089 }, { 2090 summary: "invalid relations", 2091 inputs: [][]string{ 2092 {"ms", "ms"}, 2093 {"wp", "wp"}, 2094 {"rk1", "rk1"}, 2095 {"rk1", "rk2"}, 2096 }, 2097 err: `no relations found`, 2098 }, { 2099 summary: "container scoped relation not possible when there's no subordinate", 2100 inputs: [][]string{ 2101 {"lg-p", "wp"}, 2102 }, 2103 err: `no relations found`, 2104 }, { 2105 summary: "container scoped relations between 2 subordinates is ok", 2106 inputs: [][]string{{"lg:logging-directory", "lg2:logging-client"}}, 2107 eps: []state.Endpoint{{ 2108 ServiceName: "lg", 2109 Relation: charm.Relation{ 2110 Name: "logging-directory", 2111 Role: "requirer", 2112 Interface: "logging", 2113 Limit: 1, 2114 Scope: charm.ScopeContainer, 2115 }}, { 2116 ServiceName: "lg2", 2117 Relation: charm.Relation{ 2118 Name: "logging-client", 2119 Role: "provider", 2120 Interface: "logging", 2121 Limit: 0, 2122 Scope: charm.ScopeGlobal, 2123 }}, 2124 }, 2125 }, 2126 { 2127 summary: "valid peer relation", 2128 inputs: [][]string{ 2129 {"rk1"}, 2130 {"rk1:ring"}, 2131 }, 2132 eps: []state.Endpoint{{ 2133 ServiceName: "rk1", 2134 Relation: charm.Relation{ 2135 Name: "ring", 2136 Interface: "riak", 2137 Limit: 1, 2138 Role: charm.RolePeer, 2139 Scope: charm.ScopeGlobal, 2140 }, 2141 }}, 2142 }, { 2143 summary: "ambiguous provider/requirer relation", 2144 inputs: [][]string{ 2145 {"ms", "wp"}, 2146 {"ms", "wp:db"}, 2147 }, 2148 err: `ambiguous relation: ".*" could refer to "wp:db ms:dev"; "wp:db ms:prod"`, 2149 }, { 2150 summary: "unambiguous provider/requirer relation", 2151 inputs: [][]string{ 2152 {"ms:dev", "wp"}, 2153 {"ms:dev", "wp:db"}, 2154 }, 2155 eps: []state.Endpoint{{ 2156 ServiceName: "ms", 2157 Relation: charm.Relation{ 2158 Interface: "mysql", 2159 Name: "dev", 2160 Role: charm.RoleProvider, 2161 Scope: charm.ScopeGlobal, 2162 Limit: 2, 2163 }, 2164 }, { 2165 ServiceName: "wp", 2166 Relation: charm.Relation{ 2167 Interface: "mysql", 2168 Name: "db", 2169 Role: charm.RoleRequirer, 2170 Scope: charm.ScopeGlobal, 2171 Limit: 1, 2172 }, 2173 }}, 2174 }, { 2175 summary: "explicit logging relation is preferred over implicit juju-info", 2176 inputs: [][]string{{"lg", "wp"}}, 2177 eps: []state.Endpoint{{ 2178 ServiceName: "lg", 2179 Relation: charm.Relation{ 2180 Interface: "logging", 2181 Name: "logging-directory", 2182 Role: charm.RoleRequirer, 2183 Scope: charm.ScopeContainer, 2184 Limit: 1, 2185 }, 2186 }, { 2187 ServiceName: "wp", 2188 Relation: charm.Relation{ 2189 Interface: "logging", 2190 Name: "logging-dir", 2191 Role: charm.RoleProvider, 2192 Scope: charm.ScopeContainer, 2193 }, 2194 }}, 2195 }, { 2196 summary: "implict relations can be chosen explicitly", 2197 inputs: [][]string{ 2198 {"lg:info", "wp"}, 2199 {"lg", "wp:juju-info"}, 2200 {"lg:info", "wp:juju-info"}, 2201 }, 2202 eps: []state.Endpoint{{ 2203 ServiceName: "lg", 2204 Relation: charm.Relation{ 2205 Interface: "juju-info", 2206 Name: "info", 2207 Role: charm.RoleRequirer, 2208 Scope: charm.ScopeContainer, 2209 Limit: 1, 2210 }, 2211 }, { 2212 ServiceName: "wp", 2213 Relation: charm.Relation{ 2214 Interface: "juju-info", 2215 Name: "juju-info", 2216 Role: charm.RoleProvider, 2217 Scope: charm.ScopeGlobal, 2218 }, 2219 }}, 2220 }, { 2221 summary: "implicit relations will be chosen if there are no other options", 2222 inputs: [][]string{{"lg", "ms"}}, 2223 eps: []state.Endpoint{{ 2224 ServiceName: "lg", 2225 Relation: charm.Relation{ 2226 Interface: "juju-info", 2227 Name: "info", 2228 Role: charm.RoleRequirer, 2229 Scope: charm.ScopeContainer, 2230 Limit: 1, 2231 }, 2232 }, { 2233 ServiceName: "ms", 2234 Relation: charm.Relation{ 2235 Interface: "juju-info", 2236 Name: "juju-info", 2237 Role: charm.RoleProvider, 2238 Scope: charm.ScopeGlobal, 2239 }, 2240 }}, 2241 }, 2242 } 2243 2244 func (s *StateSuite) TestInferEndpoints(c *gc.C) { 2245 s.AddTestingService(c, "ms", s.AddTestingCharm(c, "mysql-alternative")) 2246 s.AddTestingService(c, "wp", s.AddTestingCharm(c, "wordpress")) 2247 loggingCh := s.AddTestingCharm(c, "logging") 2248 s.AddTestingService(c, "lg", loggingCh) 2249 s.AddTestingService(c, "lg2", loggingCh) 2250 riak := s.AddTestingCharm(c, "riak") 2251 s.AddTestingService(c, "rk1", riak) 2252 s.AddTestingService(c, "rk2", riak) 2253 s.AddTestingService(c, "lg-p", s.AddTestingCharm(c, "logging-principal")) 2254 2255 for i, t := range inferEndpointsTests { 2256 c.Logf("test %d: %s", i, t.summary) 2257 for j, input := range t.inputs { 2258 c.Logf(" input %d: %+v", j, input) 2259 eps, err := s.State.InferEndpoints(input...) 2260 if t.err == "" { 2261 c.Assert(err, jc.ErrorIsNil) 2262 c.Assert(eps, gc.DeepEquals, t.eps) 2263 } else { 2264 c.Assert(err, gc.ErrorMatches, t.err) 2265 } 2266 } 2267 } 2268 } 2269 2270 func (s *StateSuite) TestModelConfig(c *gc.C) { 2271 attrs := map[string]interface{}{ 2272 "authorized-keys": "different-keys", 2273 "arbitrary-key": "shazam!", 2274 } 2275 cfg, err := s.State.ModelConfig() 2276 c.Assert(err, jc.ErrorIsNil) 2277 err = s.State.UpdateModelConfig(attrs, nil, nil) 2278 c.Assert(err, jc.ErrorIsNil) 2279 cfg, err = cfg.Apply(attrs) 2280 c.Assert(err, jc.ErrorIsNil) 2281 oldCfg, err := s.State.ModelConfig() 2282 c.Assert(err, jc.ErrorIsNil) 2283 2284 c.Assert(oldCfg, gc.DeepEquals, cfg) 2285 } 2286 2287 func (s *StateSuite) TestModelConstraints(c *gc.C) { 2288 // Environ constraints start out empty (for now). 2289 cons, err := s.State.ModelConstraints() 2290 c.Assert(err, jc.ErrorIsNil) 2291 c.Assert(&cons, jc.Satisfies, constraints.IsEmpty) 2292 2293 // Environ constraints can be set. 2294 cons2 := constraints.Value{Mem: uint64p(1024)} 2295 err = s.State.SetModelConstraints(cons2) 2296 c.Assert(err, jc.ErrorIsNil) 2297 cons3, err := s.State.ModelConstraints() 2298 c.Assert(err, jc.ErrorIsNil) 2299 c.Assert(cons3, gc.DeepEquals, cons2) 2300 2301 // Environ constraints are completely overwritten when re-set. 2302 cons4 := constraints.Value{CpuPower: uint64p(250)} 2303 err = s.State.SetModelConstraints(cons4) 2304 c.Assert(err, jc.ErrorIsNil) 2305 cons5, err := s.State.ModelConstraints() 2306 c.Assert(err, jc.ErrorIsNil) 2307 c.Assert(cons5, gc.DeepEquals, cons4) 2308 } 2309 2310 func (s *StateSuite) TestSetInvalidConstraints(c *gc.C) { 2311 cons := constraints.MustParse("mem=4G instance-type=foo") 2312 err := s.State.SetModelConstraints(cons) 2313 c.Assert(err, gc.ErrorMatches, `ambiguous constraints: "instance-type" overlaps with "mem"`) 2314 } 2315 2316 func (s *StateSuite) TestSetUnsupportedConstraintsWarning(c *gc.C) { 2317 defer loggo.ResetWriters() 2318 logger := loggo.GetLogger("test") 2319 logger.SetLogLevel(loggo.DEBUG) 2320 tw := &loggo.TestWriter{} 2321 c.Assert(loggo.RegisterWriter("constraints-tester", tw, loggo.DEBUG), gc.IsNil) 2322 2323 cons := constraints.MustParse("mem=4G cpu-power=10") 2324 err := s.State.SetModelConstraints(cons) 2325 c.Assert(err, jc.ErrorIsNil) 2326 c.Assert(tw.Log(), jc.LogMatches, jc.SimpleMessages{{ 2327 loggo.WARNING, 2328 `setting model constraints: unsupported constraints: cpu-power`}, 2329 }) 2330 econs, err := s.State.ModelConstraints() 2331 c.Assert(err, jc.ErrorIsNil) 2332 c.Assert(econs, gc.DeepEquals, cons) 2333 } 2334 2335 func (s *StateSuite) TestWatchIPAddresses(c *gc.C) { 2336 w := s.State.WatchIPAddresses() 2337 defer statetesting.AssertStop(c, w) 2338 wc := statetesting.NewStringsWatcherC(c, s.State, w) 2339 wc.AssertChangeInSingleEvent() 2340 2341 // add an IP address 2342 addr, err := s.State.AddIPAddress(network.NewAddress("0.1.2.3"), "foo") 2343 c.Assert(err, jc.ErrorIsNil) 2344 wc.AssertChangeInSingleEvent(addr.Value()) 2345 2346 // Make it Dead: reported. 2347 err = addr.EnsureDead() 2348 c.Assert(err, jc.ErrorIsNil) 2349 wc.AssertChangeInSingleEvent(addr.Value()) 2350 } 2351 2352 func (s *StateSuite) TestWatchModelsBulkEvents(c *gc.C) { 2353 // Alive model... 2354 alive, err := s.State.Model() 2355 c.Assert(err, jc.ErrorIsNil) 2356 2357 // Dying model... 2358 st1 := s.Factory.MakeModel(c, nil) 2359 defer st1.Close() 2360 // Add a service so Destroy doesn't advance to Dead. 2361 svc := factory.NewFactory(st1).MakeService(c, nil) 2362 dying, err := st1.Model() 2363 c.Assert(err, jc.ErrorIsNil) 2364 err = dying.Destroy() 2365 c.Assert(err, jc.ErrorIsNil) 2366 2367 // Add an empty model, destroy and remove it; we should 2368 // never see it reported. 2369 st2 := s.Factory.MakeModel(c, nil) 2370 defer st2.Close() 2371 env2, err := st2.Model() 2372 c.Assert(err, jc.ErrorIsNil) 2373 c.Assert(env2.Destroy(), jc.ErrorIsNil) 2374 err = st2.RemoveAllModelDocs() 2375 c.Assert(err, jc.ErrorIsNil) 2376 2377 // All except the removed env are reported in initial event. 2378 w := s.State.WatchModels() 2379 defer statetesting.AssertStop(c, w) 2380 wc := statetesting.NewStringsWatcherC(c, s.State, w) 2381 wc.AssertChangeInSingleEvent(alive.UUID(), dying.UUID()) 2382 2383 // Progress dying to dead, alive to dying; and see changes reported. 2384 err = svc.Destroy() 2385 c.Assert(err, jc.ErrorIsNil) 2386 err = st1.ProcessDyingModel() 2387 c.Assert(err, jc.ErrorIsNil) 2388 err = alive.Destroy() 2389 c.Assert(err, jc.ErrorIsNil) 2390 wc.AssertChangeInSingleEvent(alive.UUID(), dying.UUID()) 2391 } 2392 2393 func (s *StateSuite) TestWatchModelsLifecycle(c *gc.C) { 2394 // Initial event reports the controller model. 2395 w := s.State.WatchModels() 2396 defer statetesting.AssertStop(c, w) 2397 wc := statetesting.NewStringsWatcherC(c, s.State, w) 2398 wc.AssertChange(s.State.ModelUUID()) 2399 wc.AssertNoChange() 2400 2401 // Add a non-empty model: reported. 2402 st1 := s.Factory.MakeModel(c, nil) 2403 defer st1.Close() 2404 svc := factory.NewFactory(st1).MakeService(c, nil) 2405 env, err := st1.Model() 2406 c.Assert(err, jc.ErrorIsNil) 2407 wc.AssertChange(env.UUID()) 2408 wc.AssertNoChange() 2409 2410 // Make it Dying: reported. 2411 err = env.Destroy() 2412 c.Assert(err, jc.ErrorIsNil) 2413 wc.AssertChange(env.UUID()) 2414 wc.AssertNoChange() 2415 2416 // Remove the model: reported. 2417 err = svc.Destroy() 2418 c.Assert(err, jc.ErrorIsNil) 2419 err = st1.ProcessDyingModel() 2420 c.Assert(err, jc.ErrorIsNil) 2421 err = st1.RemoveAllModelDocs() 2422 c.Assert(err, jc.ErrorIsNil) 2423 wc.AssertChange(env.UUID()) 2424 wc.AssertNoChange() 2425 } 2426 2427 func (s *StateSuite) TestWatchServicesBulkEvents(c *gc.C) { 2428 // Alive service... 2429 dummyCharm := s.AddTestingCharm(c, "dummy") 2430 alive := s.AddTestingService(c, "service0", dummyCharm) 2431 2432 // Dying service... 2433 dying := s.AddTestingService(c, "service1", dummyCharm) 2434 keepDying, err := dying.AddUnit() 2435 c.Assert(err, jc.ErrorIsNil) 2436 err = dying.Destroy() 2437 c.Assert(err, jc.ErrorIsNil) 2438 2439 // Dead service (actually, gone, Dead == removed in this case). 2440 gone := s.AddTestingService(c, "service2", dummyCharm) 2441 err = gone.Destroy() 2442 c.Assert(err, jc.ErrorIsNil) 2443 2444 // All except gone are reported in initial event. 2445 w := s.State.WatchServices() 2446 defer statetesting.AssertStop(c, w) 2447 wc := statetesting.NewStringsWatcherC(c, s.State, w) 2448 wc.AssertChange(alive.Name(), dying.Name()) 2449 wc.AssertNoChange() 2450 2451 // Remove them all; alive/dying changes reported. 2452 err = alive.Destroy() 2453 c.Assert(err, jc.ErrorIsNil) 2454 err = keepDying.Destroy() 2455 c.Assert(err, jc.ErrorIsNil) 2456 wc.AssertChange(alive.Name(), dying.Name()) 2457 wc.AssertNoChange() 2458 } 2459 2460 func (s *StateSuite) TestWatchServicesLifecycle(c *gc.C) { 2461 // Initial event is empty when no services. 2462 w := s.State.WatchServices() 2463 defer statetesting.AssertStop(c, w) 2464 wc := statetesting.NewStringsWatcherC(c, s.State, w) 2465 wc.AssertChange() 2466 wc.AssertNoChange() 2467 2468 // Add a service: reported. 2469 service := s.AddTestingService(c, "service", s.AddTestingCharm(c, "dummy")) 2470 wc.AssertChange("service") 2471 wc.AssertNoChange() 2472 2473 // Change the service: not reported. 2474 keepDying, err := service.AddUnit() 2475 c.Assert(err, jc.ErrorIsNil) 2476 wc.AssertNoChange() 2477 2478 // Make it Dying: reported. 2479 err = service.Destroy() 2480 c.Assert(err, jc.ErrorIsNil) 2481 wc.AssertChange("service") 2482 wc.AssertNoChange() 2483 2484 // Make it Dead(/removed): reported. 2485 err = keepDying.Destroy() 2486 c.Assert(err, jc.ErrorIsNil) 2487 wc.AssertChange("service") 2488 wc.AssertNoChange() 2489 } 2490 2491 func (s *StateSuite) TestWatchServicesDiesOnStateClose(c *gc.C) { 2492 // This test is testing logic in watcher.lifecycleWatcher, 2493 // which is also used by: 2494 // State.WatchModels 2495 // Service.WatchUnits 2496 // Service.WatchRelations 2497 // State.WatchEnviron 2498 // Machine.WatchContainers 2499 testWatcherDiesWhenStateCloses(c, s.modelTag, func(c *gc.C, st *state.State) waiter { 2500 w := st.WatchServices() 2501 <-w.Changes() 2502 return w 2503 }) 2504 } 2505 2506 func (s *StateSuite) TestWatchMachinesBulkEvents(c *gc.C) { 2507 // Alive machine... 2508 alive, err := s.State.AddMachine("quantal", state.JobHostUnits) 2509 c.Assert(err, jc.ErrorIsNil) 2510 2511 // Dying machine... 2512 dying, err := s.State.AddMachine("quantal", state.JobHostUnits) 2513 c.Assert(err, jc.ErrorIsNil) 2514 err = dying.SetProvisioned(instance.Id("i-blah"), "fake-nonce", nil) 2515 c.Assert(err, jc.ErrorIsNil) 2516 err = dying.Destroy() 2517 c.Assert(err, jc.ErrorIsNil) 2518 2519 // Dead machine... 2520 dead, err := s.State.AddMachine("quantal", state.JobHostUnits) 2521 c.Assert(err, jc.ErrorIsNil) 2522 err = dead.EnsureDead() 2523 c.Assert(err, jc.ErrorIsNil) 2524 2525 // Gone machine. 2526 gone, err := s.State.AddMachine("quantal", state.JobHostUnits) 2527 c.Assert(err, jc.ErrorIsNil) 2528 err = gone.EnsureDead() 2529 c.Assert(err, jc.ErrorIsNil) 2530 err = gone.Remove() 2531 c.Assert(err, jc.ErrorIsNil) 2532 2533 // All except gone machine are reported in initial event. 2534 w := s.State.WatchModelMachines() 2535 defer statetesting.AssertStop(c, w) 2536 wc := statetesting.NewStringsWatcherC(c, s.State, w) 2537 wc.AssertChange(alive.Id(), dying.Id(), dead.Id()) 2538 wc.AssertNoChange() 2539 2540 // Remove them all; alive/dying changes reported; dead never mentioned again. 2541 err = alive.Destroy() 2542 c.Assert(err, jc.ErrorIsNil) 2543 err = dying.EnsureDead() 2544 c.Assert(err, jc.ErrorIsNil) 2545 err = dying.Remove() 2546 c.Assert(err, jc.ErrorIsNil) 2547 err = dead.Remove() 2548 c.Assert(err, jc.ErrorIsNil) 2549 wc.AssertChange(alive.Id(), dying.Id()) 2550 wc.AssertNoChange() 2551 } 2552 2553 func (s *StateSuite) TestWatchMachinesLifecycle(c *gc.C) { 2554 // Initial event is empty when no machines. 2555 w := s.State.WatchModelMachines() 2556 defer statetesting.AssertStop(c, w) 2557 wc := statetesting.NewStringsWatcherC(c, s.State, w) 2558 wc.AssertChange() 2559 wc.AssertNoChange() 2560 2561 // Add a machine: reported. 2562 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 2563 c.Assert(err, jc.ErrorIsNil) 2564 wc.AssertChange("0") 2565 wc.AssertNoChange() 2566 2567 // Change the machine: not reported. 2568 err = machine.SetProvisioned(instance.Id("i-blah"), "fake-nonce", nil) 2569 c.Assert(err, jc.ErrorIsNil) 2570 wc.AssertNoChange() 2571 2572 // Make it Dying: reported. 2573 err = machine.Destroy() 2574 c.Assert(err, jc.ErrorIsNil) 2575 wc.AssertChange("0") 2576 wc.AssertNoChange() 2577 2578 // Make it Dead: reported. 2579 err = machine.EnsureDead() 2580 c.Assert(err, jc.ErrorIsNil) 2581 wc.AssertChange("0") 2582 wc.AssertNoChange() 2583 2584 // Remove it: not reported. 2585 err = machine.Remove() 2586 c.Assert(err, jc.ErrorIsNil) 2587 wc.AssertNoChange() 2588 } 2589 2590 func (s *StateSuite) TestWatchMachinesIncludesOldMachines(c *gc.C) { 2591 // Older versions of juju do not write the "containertype" field. 2592 // This has caused machines to not be detected in the initial event. 2593 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 2594 c.Assert(err, jc.ErrorIsNil) 2595 err = s.machines.Update( 2596 bson.D{{"_id", state.DocID(s.State, machine.Id())}}, 2597 bson.D{{"$unset", bson.D{{"containertype", 1}}}}, 2598 ) 2599 c.Assert(err, jc.ErrorIsNil) 2600 2601 w := s.State.WatchModelMachines() 2602 defer statetesting.AssertStop(c, w) 2603 wc := statetesting.NewStringsWatcherC(c, s.State, w) 2604 wc.AssertChange(machine.Id()) 2605 wc.AssertNoChange() 2606 } 2607 2608 func (s *StateSuite) TestWatchMachinesIgnoresContainers(c *gc.C) { 2609 // Initial event is empty when no machines. 2610 w := s.State.WatchModelMachines() 2611 defer statetesting.AssertStop(c, w) 2612 wc := statetesting.NewStringsWatcherC(c, s.State, w) 2613 wc.AssertChange() 2614 wc.AssertNoChange() 2615 2616 // Add a machine: reported. 2617 template := state.MachineTemplate{ 2618 Series: "quantal", 2619 Jobs: []state.MachineJob{state.JobHostUnits}, 2620 } 2621 machines, err := s.State.AddMachines(template) 2622 c.Assert(err, jc.ErrorIsNil) 2623 c.Assert(machines, gc.HasLen, 1) 2624 machine := machines[0] 2625 wc.AssertChange("0") 2626 wc.AssertNoChange() 2627 2628 // Add a container: not reported. 2629 m, err := s.State.AddMachineInsideMachine(template, machine.Id(), instance.LXC) 2630 c.Assert(err, jc.ErrorIsNil) 2631 wc.AssertNoChange() 2632 2633 // Make the container Dying: not reported. 2634 err = m.Destroy() 2635 c.Assert(err, jc.ErrorIsNil) 2636 wc.AssertNoChange() 2637 2638 // Make the container Dead: not reported. 2639 err = m.EnsureDead() 2640 c.Assert(err, jc.ErrorIsNil) 2641 wc.AssertNoChange() 2642 2643 // Remove the container: not reported. 2644 err = m.Remove() 2645 c.Assert(err, jc.ErrorIsNil) 2646 wc.AssertNoChange() 2647 } 2648 2649 func (s *StateSuite) TestWatchContainerLifecycle(c *gc.C) { 2650 // Add a host machine. 2651 template := state.MachineTemplate{ 2652 Series: "quantal", 2653 Jobs: []state.MachineJob{state.JobHostUnits}, 2654 } 2655 machine, err := s.State.AddOneMachine(template) 2656 c.Assert(err, jc.ErrorIsNil) 2657 2658 otherMachine, err := s.State.AddOneMachine(template) 2659 c.Assert(err, jc.ErrorIsNil) 2660 2661 // Initial event is empty when no containers. 2662 w := machine.WatchContainers(instance.LXC) 2663 defer statetesting.AssertStop(c, w) 2664 wAll := machine.WatchAllContainers() 2665 defer statetesting.AssertStop(c, wAll) 2666 2667 wc := statetesting.NewStringsWatcherC(c, s.State, w) 2668 wc.AssertChange() 2669 wc.AssertNoChange() 2670 2671 wcAll := statetesting.NewStringsWatcherC(c, s.State, wAll) 2672 wcAll.AssertChange() 2673 wcAll.AssertNoChange() 2674 2675 // Add a container of the required type: reported. 2676 m, err := s.State.AddMachineInsideMachine(template, machine.Id(), instance.LXC) 2677 c.Assert(err, jc.ErrorIsNil) 2678 wc.AssertChange("0/lxc/0") 2679 wc.AssertNoChange() 2680 wcAll.AssertChange("0/lxc/0") 2681 wcAll.AssertNoChange() 2682 2683 // Add a container of a different type: not reported. 2684 m1, err := s.State.AddMachineInsideMachine(template, machine.Id(), instance.KVM) 2685 c.Assert(err, jc.ErrorIsNil) 2686 wc.AssertNoChange() 2687 // But reported by the all watcher. 2688 wcAll.AssertChange("0/kvm/0") 2689 wcAll.AssertNoChange() 2690 2691 // Add a nested container of the right type: not reported. 2692 mchild, err := s.State.AddMachineInsideMachine(template, m.Id(), instance.LXC) 2693 c.Assert(err, jc.ErrorIsNil) 2694 wc.AssertNoChange() 2695 wcAll.AssertNoChange() 2696 2697 // Add a container of a different machine: not reported. 2698 m2, err := s.State.AddMachineInsideMachine(template, otherMachine.Id(), instance.LXC) 2699 c.Assert(err, jc.ErrorIsNil) 2700 wc.AssertNoChange() 2701 statetesting.AssertStop(c, w) 2702 wcAll.AssertNoChange() 2703 statetesting.AssertStop(c, wAll) 2704 2705 w = machine.WatchContainers(instance.LXC) 2706 defer statetesting.AssertStop(c, w) 2707 wc = statetesting.NewStringsWatcherC(c, s.State, w) 2708 wAll = machine.WatchAllContainers() 2709 defer statetesting.AssertStop(c, wAll) 2710 wcAll = statetesting.NewStringsWatcherC(c, s.State, wAll) 2711 wc.AssertChange("0/lxc/0") 2712 wc.AssertNoChange() 2713 wcAll.AssertChange("0/kvm/0", "0/lxc/0") 2714 wcAll.AssertNoChange() 2715 2716 // Make the container Dying: cannot because of nested container. 2717 err = m.Destroy() 2718 c.Assert(err, gc.ErrorMatches, `machine .* is hosting containers ".*"`) 2719 2720 err = mchild.EnsureDead() 2721 c.Assert(err, jc.ErrorIsNil) 2722 err = mchild.Remove() 2723 c.Assert(err, jc.ErrorIsNil) 2724 2725 // Make the container Dying: reported. 2726 err = m.Destroy() 2727 c.Assert(err, jc.ErrorIsNil) 2728 wc.AssertChange("0/lxc/0") 2729 wc.AssertNoChange() 2730 wcAll.AssertChange("0/lxc/0") 2731 wcAll.AssertNoChange() 2732 2733 // Make the other containers Dying: not reported. 2734 err = m1.Destroy() 2735 c.Assert(err, jc.ErrorIsNil) 2736 err = m2.Destroy() 2737 c.Assert(err, jc.ErrorIsNil) 2738 wc.AssertNoChange() 2739 // But reported by the all watcher. 2740 wcAll.AssertChange("0/kvm/0") 2741 wcAll.AssertNoChange() 2742 2743 // Make the container Dead: reported. 2744 err = m.EnsureDead() 2745 c.Assert(err, jc.ErrorIsNil) 2746 wc.AssertChange("0/lxc/0") 2747 wc.AssertNoChange() 2748 wcAll.AssertChange("0/lxc/0") 2749 wcAll.AssertNoChange() 2750 2751 // Make the other containers Dead: not reported. 2752 err = m1.EnsureDead() 2753 c.Assert(err, jc.ErrorIsNil) 2754 err = m2.EnsureDead() 2755 c.Assert(err, jc.ErrorIsNil) 2756 wc.AssertNoChange() 2757 // But reported by the all watcher. 2758 wcAll.AssertChange("0/kvm/0") 2759 wcAll.AssertNoChange() 2760 2761 // Remove the container: not reported. 2762 err = m.Remove() 2763 c.Assert(err, jc.ErrorIsNil) 2764 wc.AssertNoChange() 2765 wcAll.AssertNoChange() 2766 } 2767 2768 func (s *StateSuite) TestWatchMachineHardwareCharacteristics(c *gc.C) { 2769 // Add a machine: reported. 2770 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 2771 c.Assert(err, jc.ErrorIsNil) 2772 w := machine.WatchHardwareCharacteristics() 2773 defer statetesting.AssertStop(c, w) 2774 2775 // Initial event. 2776 wc := statetesting.NewNotifyWatcherC(c, s.State, w) 2777 wc.AssertOneChange() 2778 2779 // Provision a machine: reported. 2780 err = machine.SetProvisioned(instance.Id("i-blah"), "fake-nonce", nil) 2781 c.Assert(err, jc.ErrorIsNil) 2782 wc.AssertOneChange() 2783 2784 // Alter the machine: not reported. 2785 vers := version.MustParseBinary("1.2.3-quantal-ppc") 2786 err = machine.SetAgentVersion(vers) 2787 c.Assert(err, jc.ErrorIsNil) 2788 wc.AssertNoChange() 2789 } 2790 2791 func (s *StateSuite) TestWatchControllerInfo(c *gc.C) { 2792 _, err := s.State.AddMachine("quantal", state.JobManageModel) 2793 c.Assert(err, jc.ErrorIsNil) 2794 2795 w := s.State.WatchControllerInfo() 2796 defer statetesting.AssertStop(c, w) 2797 2798 // Initial event. 2799 wc := statetesting.NewNotifyWatcherC(c, s.State, w) 2800 wc.AssertOneChange() 2801 2802 info, err := s.State.ControllerInfo() 2803 c.Assert(err, jc.ErrorIsNil) 2804 c.Assert(info, jc.DeepEquals, &state.ControllerInfo{ 2805 ModelTag: s.modelTag, 2806 MachineIds: []string{"0"}, 2807 VotingMachineIds: []string{"0"}, 2808 }) 2809 2810 s.PatchValue(state.ControllerAvailable, func(m *state.Machine) (bool, error) { 2811 return true, nil 2812 }) 2813 2814 changes, err := s.State.EnableHA(3, constraints.Value{}, "quantal", nil) 2815 c.Assert(err, jc.ErrorIsNil) 2816 c.Assert(changes.Added, gc.HasLen, 2) 2817 2818 wc.AssertOneChange() 2819 2820 info, err = s.State.ControllerInfo() 2821 c.Assert(err, jc.ErrorIsNil) 2822 c.Assert(info, jc.DeepEquals, &state.ControllerInfo{ 2823 ModelTag: s.modelTag, 2824 MachineIds: []string{"0", "1", "2"}, 2825 VotingMachineIds: []string{"0", "1", "2"}, 2826 }) 2827 } 2828 2829 func (s *StateSuite) TestAdditionalValidation(c *gc.C) { 2830 updateAttrs := map[string]interface{}{"logging-config": "juju=ERROR"} 2831 configValidator1 := func(updateAttrs map[string]interface{}, removeAttrs []string, oldConfig *config.Config) error { 2832 c.Assert(updateAttrs, gc.DeepEquals, map[string]interface{}{"logging-config": "juju=ERROR"}) 2833 if _, found := updateAttrs["logging-config"]; found { 2834 return fmt.Errorf("cannot change logging-config") 2835 } 2836 return nil 2837 } 2838 removeAttrs := []string{"logging-config"} 2839 configValidator2 := func(updateAttrs map[string]interface{}, removeAttrs []string, oldConfig *config.Config) error { 2840 c.Assert(removeAttrs, gc.DeepEquals, []string{"logging-config"}) 2841 for _, i := range removeAttrs { 2842 if i == "logging-config" { 2843 return fmt.Errorf("cannot remove logging-config") 2844 } 2845 } 2846 return nil 2847 } 2848 configValidator3 := func(updateAttrs map[string]interface{}, removeAttrs []string, oldConfig *config.Config) error { 2849 return nil 2850 } 2851 2852 err := s.State.UpdateModelConfig(updateAttrs, nil, configValidator1) 2853 c.Assert(err, gc.ErrorMatches, "cannot change logging-config") 2854 err = s.State.UpdateModelConfig(nil, removeAttrs, configValidator2) 2855 c.Assert(err, gc.ErrorMatches, "cannot remove logging-config") 2856 err = s.State.UpdateModelConfig(updateAttrs, nil, configValidator3) 2857 c.Assert(err, jc.ErrorIsNil) 2858 } 2859 2860 func (s *StateSuite) insertFakeModelDocs(c *gc.C, st *state.State) string { 2861 // insert one doc for each multiEnvCollection 2862 var ops []mgotxn.Op 2863 for _, collName := range state.MultiEnvCollections() { 2864 // skip adding constraints, modelUser and settings as they were added when the 2865 // model was created 2866 if collName == "constraints" || collName == "modelusers" || collName == "settings" { 2867 continue 2868 } 2869 if state.HasRawAccess(collName) { 2870 coll, closer := state.GetRawCollection(st, collName) 2871 defer closer() 2872 2873 err := coll.Insert(bson.M{ 2874 "_id": state.DocID(st, "arbitraryid"), 2875 "model-uuid": st.ModelUUID(), 2876 }) 2877 c.Assert(err, jc.ErrorIsNil) 2878 } else { 2879 ops = append(ops, mgotxn.Op{ 2880 C: collName, 2881 Id: state.DocID(st, "arbitraryid"), 2882 Insert: bson.M{"model-uuid": st.ModelUUID()}}) 2883 } 2884 } 2885 err := state.RunTransaction(st, ops) 2886 c.Assert(err, jc.ErrorIsNil) 2887 2888 // test that we can find each doc in state 2889 for _, collName := range state.MultiEnvCollections() { 2890 coll, closer := state.GetRawCollection(st, collName) 2891 defer closer() 2892 n, err := coll.Find(bson.D{{"model-uuid", st.ModelUUID()}}).Count() 2893 c.Assert(err, jc.ErrorIsNil) 2894 c.Assert(n, gc.Not(gc.Equals), 0) 2895 } 2896 2897 model, err := st.Model() 2898 c.Assert(err, jc.ErrorIsNil) 2899 return state.UserModelNameIndex(model.Owner().Canonical(), model.Name()) 2900 } 2901 2902 type checkUserModelNameArgs struct { 2903 st *state.State 2904 id string 2905 exists bool 2906 } 2907 2908 func (s *StateSuite) checkUserModelNameExists(c *gc.C, args checkUserModelNameArgs) { 2909 indexColl, closer := state.GetCollection(args.st, "usermodelname") 2910 defer closer() 2911 n, err := indexColl.FindId(args.id).Count() 2912 c.Assert(err, jc.ErrorIsNil) 2913 if args.exists { 2914 c.Assert(n, gc.Equals, 1) 2915 } else { 2916 c.Assert(n, gc.Equals, 0) 2917 } 2918 } 2919 2920 func (s *StateSuite) AssertModelDeleted(c *gc.C, st *state.State) { 2921 // check to see if the model itself is gone 2922 _, err := st.Model() 2923 c.Assert(err, gc.ErrorMatches, `model not found`) 2924 2925 // ensure all docs for all multiEnvCollections are removed 2926 for _, collName := range state.MultiEnvCollections() { 2927 coll, closer := state.GetRawCollection(st, collName) 2928 defer closer() 2929 n, err := coll.Find(bson.D{{"model-uuid", st.ModelUUID()}}).Count() 2930 c.Assert(err, jc.ErrorIsNil) 2931 c.Assert(n, gc.Equals, 0) 2932 } 2933 } 2934 2935 func (s *StateSuite) TestRemoveAllModelDocs(c *gc.C) { 2936 st := s.Factory.MakeModel(c, nil) 2937 defer st.Close() 2938 2939 userModelKey := s.insertFakeModelDocs(c, st) 2940 s.checkUserModelNameExists(c, checkUserModelNameArgs{st: st, id: userModelKey, exists: true}) 2941 2942 err := state.SetModelLifeDead(st, st.ModelUUID()) 2943 c.Assert(err, jc.ErrorIsNil) 2944 2945 err = st.RemoveAllModelDocs() 2946 c.Assert(err, jc.ErrorIsNil) 2947 2948 // test that we can not find the user:envName unique index 2949 s.checkUserModelNameExists(c, checkUserModelNameArgs{st: st, id: userModelKey, exists: false}) 2950 s.AssertModelDeleted(c, st) 2951 } 2952 2953 func (s *StateSuite) TestRemoveAllModelDocsAliveEnvFails(c *gc.C) { 2954 st := s.Factory.MakeModel(c, nil) 2955 defer st.Close() 2956 2957 err := st.RemoveAllModelDocs() 2958 c.Assert(err, gc.ErrorMatches, "transaction aborted") 2959 } 2960 2961 func (s *StateSuite) TestRemoveImportingModelDocsFailsActive(c *gc.C) { 2962 st := s.Factory.MakeModel(c, nil) 2963 defer st.Close() 2964 2965 err := st.RemoveImportingModelDocs() 2966 c.Assert(err, gc.ErrorMatches, "transaction aborted") 2967 } 2968 2969 func (s *StateSuite) TestRemoveImportingModelDocsFailsExporting(c *gc.C) { 2970 st := s.Factory.MakeModel(c, nil) 2971 defer st.Close() 2972 model, err := st.Model() 2973 c.Assert(err, jc.ErrorIsNil) 2974 err = model.SetMigrationMode(state.MigrationModeExporting) 2975 c.Assert(err, jc.ErrorIsNil) 2976 2977 err = st.RemoveImportingModelDocs() 2978 c.Assert(err, gc.ErrorMatches, "transaction aborted") 2979 } 2980 2981 func (s *StateSuite) TestRemoveImportingModelDocsImporting(c *gc.C) { 2982 st := s.Factory.MakeModel(c, nil) 2983 defer st.Close() 2984 userModelKey := s.insertFakeModelDocs(c, st) 2985 c.Assert(state.HostedModelCount(c, s.State), gc.Equals, 1) 2986 2987 model, err := st.Model() 2988 c.Assert(err, jc.ErrorIsNil) 2989 err = model.SetMigrationMode(state.MigrationModeImporting) 2990 c.Assert(err, jc.ErrorIsNil) 2991 2992 err = st.RemoveImportingModelDocs() 2993 c.Assert(err, jc.ErrorIsNil) 2994 2995 // test that we can not find the user:envName unique index 2996 s.checkUserModelNameExists(c, checkUserModelNameArgs{st: st, id: userModelKey, exists: false}) 2997 s.AssertModelDeleted(c, st) 2998 c.Assert(state.HostedModelCount(c, s.State), gc.Equals, 0) 2999 } 3000 3001 type attrs map[string]interface{} 3002 3003 func (s *StateSuite) TestWatchModelConfig(c *gc.C) { 3004 w := s.State.WatchModelConfig() 3005 defer statetesting.AssertStop(c, w) 3006 3007 // TODO(fwereade) just use a NotifyWatcher and NotifyWatcherC to test it. 3008 assertNoChange := func() { 3009 s.State.StartSync() 3010 select { 3011 case got := <-w.Changes(): 3012 c.Fatalf("got unexpected change: %#v", got) 3013 case <-time.After(testing.ShortWait): 3014 } 3015 } 3016 assertChange := func(change attrs) { 3017 cfg, err := s.State.ModelConfig() 3018 c.Assert(err, jc.ErrorIsNil) 3019 cfg, err = cfg.Apply(change) 3020 c.Assert(err, jc.ErrorIsNil) 3021 if change != nil { 3022 err = s.State.UpdateModelConfig(change, nil, nil) 3023 c.Assert(err, jc.ErrorIsNil) 3024 } 3025 s.State.StartSync() 3026 select { 3027 case got, ok := <-w.Changes(): 3028 c.Assert(ok, jc.IsTrue) 3029 c.Assert(got.AllAttrs(), gc.DeepEquals, cfg.AllAttrs()) 3030 case <-time.After(testing.LongWait): 3031 c.Fatalf("did not get change: %#v", change) 3032 } 3033 assertNoChange() 3034 } 3035 assertChange(nil) 3036 assertChange(attrs{"default-series": "another-series"}) 3037 assertChange(attrs{"fancy-new-key": "arbitrary-value"}) 3038 } 3039 3040 func (s *StateSuite) TestWatchModelConfigDiesOnStateClose(c *gc.C) { 3041 testWatcherDiesWhenStateCloses(c, s.modelTag, func(c *gc.C, st *state.State) waiter { 3042 w := st.WatchModelConfig() 3043 <-w.Changes() 3044 return w 3045 }) 3046 } 3047 3048 func (s *StateSuite) TestWatchForModelConfigChanges(c *gc.C) { 3049 cur := jujuversion.Current 3050 err := statetesting.SetAgentVersion(s.State, cur) 3051 c.Assert(err, jc.ErrorIsNil) 3052 w := s.State.WatchForModelConfigChanges() 3053 defer statetesting.AssertStop(c, w) 3054 3055 wc := statetesting.NewNotifyWatcherC(c, s.State, w) 3056 // Initially we get one change notification 3057 wc.AssertOneChange() 3058 3059 // Multiple changes will only result in a single change notification 3060 newVersion := cur 3061 newVersion.Minor++ 3062 err = statetesting.SetAgentVersion(s.State, newVersion) 3063 c.Assert(err, jc.ErrorIsNil) 3064 3065 newerVersion := newVersion 3066 newerVersion.Minor++ 3067 err = statetesting.SetAgentVersion(s.State, newerVersion) 3068 c.Assert(err, jc.ErrorIsNil) 3069 wc.AssertOneChange() 3070 3071 // Setting it to the same value does not trigger a change notification 3072 err = statetesting.SetAgentVersion(s.State, newerVersion) 3073 c.Assert(err, jc.ErrorIsNil) 3074 wc.AssertNoChange() 3075 } 3076 3077 func (s *StateSuite) TestWatchModelConfigCorruptConfig(c *gc.C) { 3078 cfg, err := s.State.ModelConfig() 3079 c.Assert(err, jc.ErrorIsNil) 3080 3081 // Corrupt the model configuration. 3082 settings := s.Session.DB("juju").C("settings") 3083 err = settings.UpdateId(state.DocID(s.State, "e"), bson.D{{"$unset", bson.D{{"settings.name", 1}}}}) 3084 c.Assert(err, jc.ErrorIsNil) 3085 3086 s.State.StartSync() 3087 3088 // Start watching the configuration. 3089 watcher := s.State.WatchModelConfig() 3090 defer watcher.Stop() 3091 done := make(chan *config.Config) 3092 go func() { 3093 select { 3094 case cfg, ok := <-watcher.Changes(): 3095 if !ok { 3096 c.Errorf("watcher channel closed") 3097 } else { 3098 done <- cfg 3099 } 3100 case <-time.After(5 * time.Second): 3101 c.Fatalf("no model configuration observed") 3102 } 3103 }() 3104 3105 s.State.StartSync() 3106 3107 // The invalid configuration must not have been generated. 3108 select { 3109 case <-done: 3110 c.Fatalf("configuration returned too soon") 3111 case <-time.After(testing.ShortWait): 3112 } 3113 3114 // Fix the configuration. 3115 err = settings.UpdateId(state.DocID(s.State, "e"), bson.D{{"$set", bson.D{{"settings.name", "foo"}}}}) 3116 c.Assert(err, jc.ErrorIsNil) 3117 fixed := cfg.AllAttrs() 3118 err = s.State.UpdateModelConfig(fixed, nil, nil) 3119 c.Assert(err, jc.ErrorIsNil) 3120 3121 s.State.StartSync() 3122 select { 3123 case got := <-done: 3124 c.Assert(got.AllAttrs(), gc.DeepEquals, fixed) 3125 case <-time.After(5 * time.Second): 3126 c.Fatalf("no model configuration observed") 3127 } 3128 } 3129 3130 func (s *StateSuite) TestAddAndGetEquivalence(c *gc.C) { 3131 // The equivalence tested here isn't necessarily correct, and 3132 // comparing private details is discouraged in the project. 3133 // The implementation might choose to cache information, or 3134 // to have different logic when adding or removing, and the 3135 // comparison might fail despite it being correct. 3136 // That said, we've had bugs with txn-revno being incorrect 3137 // before, so this testing at least ensures we're conscious 3138 // about such changes. 3139 3140 m1, err := s.State.AddMachine("quantal", state.JobHostUnits) 3141 c.Assert(err, jc.ErrorIsNil) 3142 m2, err := s.State.Machine(m1.Id()) 3143 c.Assert(m1, jc.DeepEquals, m2) 3144 3145 charm1 := s.AddTestingCharm(c, "wordpress") 3146 charm2, err := s.State.Charm(charm1.URL()) 3147 c.Assert(err, jc.ErrorIsNil) 3148 c.Assert(charm1, jc.DeepEquals, charm2) 3149 3150 wordpress1 := s.AddTestingService(c, "wordpress", charm1) 3151 wordpress2, err := s.State.Service("wordpress") 3152 c.Assert(err, jc.ErrorIsNil) 3153 c.Assert(wordpress1, jc.DeepEquals, wordpress2) 3154 3155 unit1, err := wordpress1.AddUnit() 3156 c.Assert(err, jc.ErrorIsNil) 3157 unit2, err := s.State.Unit("wordpress/0") 3158 c.Assert(err, jc.ErrorIsNil) 3159 c.Assert(unit1, jc.DeepEquals, unit2) 3160 3161 s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) 3162 c.Assert(err, jc.ErrorIsNil) 3163 eps, err := s.State.InferEndpoints("wordpress", "mysql") 3164 c.Assert(err, jc.ErrorIsNil) 3165 relation1, err := s.State.AddRelation(eps...) 3166 c.Assert(err, jc.ErrorIsNil) 3167 relation2, err := s.State.EndpointsRelation(eps...) 3168 c.Assert(relation1, jc.DeepEquals, relation2) 3169 relation3, err := s.State.Relation(relation1.Id()) 3170 c.Assert(relation1, jc.DeepEquals, relation3) 3171 } 3172 3173 func tryOpenState(modelTag names.ModelTag, info *mongo.MongoInfo) error { 3174 st, err := state.Open(modelTag, info, mongotest.DialOpts(), state.Policy(nil)) 3175 if err == nil { 3176 err = st.Close() 3177 } 3178 return err 3179 } 3180 3181 func (s *StateSuite) TestOpenWithoutSetMongoPassword(c *gc.C) { 3182 info := statetesting.NewMongoInfo() 3183 info.Tag, info.Password = names.NewUserTag("arble"), "bar" 3184 err := tryOpenState(s.modelTag, info) 3185 c.Check(errors.Cause(err), jc.Satisfies, errors.IsUnauthorized) 3186 c.Check(err, gc.ErrorMatches, `cannot log in to admin database as "user-arble": unauthorized mongo access: .*`) 3187 3188 info.Tag, info.Password = names.NewUserTag("arble"), "" 3189 err = tryOpenState(s.modelTag, info) 3190 c.Check(errors.Cause(err), jc.Satisfies, errors.IsUnauthorized) 3191 c.Check(err, gc.ErrorMatches, `cannot log in to admin database as "user-arble": unauthorized mongo access: .*`) 3192 3193 info.Tag, info.Password = nil, "" 3194 err = tryOpenState(s.modelTag, info) 3195 c.Check(err, jc.ErrorIsNil) 3196 } 3197 3198 func (s *StateSuite) TestOpenBadAddress(c *gc.C) { 3199 info := statetesting.NewMongoInfo() 3200 info.Addrs = []string{"0.1.2.3:1234"} 3201 st, err := state.Open(testing.ModelTag, info, mongo.DialOpts{ 3202 Timeout: 1 * time.Millisecond, 3203 }, state.Policy(nil)) 3204 if err == nil { 3205 st.Close() 3206 } 3207 c.Assert(err, gc.ErrorMatches, "cannot connect to mongodb: no reachable servers") 3208 } 3209 3210 func (s *StateSuite) TestOpenDelaysRetryBadAddress(c *gc.C) { 3211 // Default mgo retry delay 3212 retryDelay := 500 * time.Millisecond 3213 info := statetesting.NewMongoInfo() 3214 info.Addrs = []string{"0.1.2.3:1234"} 3215 3216 t0 := time.Now() 3217 st, err := state.Open(testing.ModelTag, info, mongo.DialOpts{ 3218 Timeout: 1 * time.Millisecond, 3219 }, state.Policy(nil)) 3220 if err == nil { 3221 st.Close() 3222 } 3223 c.Assert(err, gc.ErrorMatches, "cannot connect to mongodb: no reachable servers") 3224 // tryOpenState should have delayed for at least retryDelay 3225 if t1 := time.Since(t0); t1 < retryDelay { 3226 c.Errorf("mgo.Dial only paused for %v, expected at least %v", t1, retryDelay) 3227 } 3228 } 3229 3230 func testSetPassword(c *gc.C, getEntity func() (state.Authenticator, error)) { 3231 e, err := getEntity() 3232 c.Assert(err, jc.ErrorIsNil) 3233 3234 c.Assert(e.PasswordValid(goodPassword), jc.IsFalse) 3235 err = e.SetPassword(goodPassword) 3236 c.Assert(err, jc.ErrorIsNil) 3237 c.Assert(e.PasswordValid(goodPassword), jc.IsTrue) 3238 3239 // Check a newly-fetched entity has the same password. 3240 e2, err := getEntity() 3241 c.Assert(err, jc.ErrorIsNil) 3242 c.Assert(e2.PasswordValid(goodPassword), jc.IsTrue) 3243 3244 err = e.SetPassword(alternatePassword) 3245 c.Assert(err, jc.ErrorIsNil) 3246 c.Assert(e.PasswordValid(goodPassword), jc.IsFalse) 3247 c.Assert(e.PasswordValid(alternatePassword), jc.IsTrue) 3248 3249 // Check that refreshing fetches the new password 3250 err = e2.Refresh() 3251 c.Assert(err, jc.ErrorIsNil) 3252 c.Assert(e2.PasswordValid(alternatePassword), jc.IsTrue) 3253 3254 if le, ok := e.(lifer); ok { 3255 testWhenDying(c, le, noErr, deadErr, func() error { 3256 return e.SetPassword("arble-farble-dying-yarble") 3257 }) 3258 } 3259 } 3260 3261 type entity interface { 3262 state.Entity 3263 state.Lifer 3264 state.Authenticator 3265 } 3266 3267 type findEntityTest struct { 3268 tag names.Tag 3269 err string 3270 } 3271 3272 var findEntityTests = []findEntityTest{{ 3273 tag: names.NewRelationTag("svc1:rel1 svc2:rel2"), 3274 err: `relation "svc1:rel1 svc2:rel2" not found`, 3275 }, { 3276 tag: names.NewModelTag("9f484882-2f18-4fd2-967d-db9663db7bea"), 3277 err: `model "9f484882-2f18-4fd2-967d-db9663db7bea" not found`, 3278 }, { 3279 tag: names.NewMachineTag("0"), 3280 }, { 3281 tag: names.NewServiceTag("ser-vice2"), 3282 }, { 3283 tag: names.NewRelationTag("wordpress:db ser-vice2:server"), 3284 }, { 3285 tag: names.NewUnitTag("ser-vice2/0"), 3286 }, { 3287 tag: names.NewUserTag("arble"), 3288 }, { 3289 tag: names.NewActionTag("fedcba98-7654-4321-ba98-76543210beef"), 3290 err: `action "fedcba98-7654-4321-ba98-76543210beef" not found`, 3291 }, { 3292 tag: names.NewUserTag("eric"), 3293 }, { 3294 tag: names.NewUserTag("eric@local"), 3295 }, { 3296 tag: names.NewUserTag("eric@remote"), 3297 err: `user "eric@remote" not found`, 3298 }} 3299 3300 var entityTypes = map[string]interface{}{ 3301 names.UserTagKind: (*state.User)(nil), 3302 names.ModelTagKind: (*state.Model)(nil), 3303 names.ServiceTagKind: (*state.Service)(nil), 3304 names.UnitTagKind: (*state.Unit)(nil), 3305 names.MachineTagKind: (*state.Machine)(nil), 3306 names.RelationTagKind: (*state.Relation)(nil), 3307 names.ActionTagKind: (state.Action)(nil), 3308 } 3309 3310 func (s *StateSuite) TestFindEntity(c *gc.C) { 3311 s.Factory.MakeUser(c, &factory.UserParams{Name: "eric"}) 3312 _, err := s.State.AddMachine("quantal", state.JobHostUnits) 3313 c.Assert(err, jc.ErrorIsNil) 3314 svc := s.AddTestingService(c, "ser-vice2", s.AddTestingCharm(c, "mysql")) 3315 unit, err := svc.AddUnit() 3316 c.Assert(err, jc.ErrorIsNil) 3317 _, err = unit.AddAction("fakeaction", nil) 3318 c.Assert(err, jc.ErrorIsNil) 3319 s.Factory.MakeUser(c, &factory.UserParams{Name: "arble"}) 3320 c.Assert(err, jc.ErrorIsNil) 3321 s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 3322 eps, err := s.State.InferEndpoints("wordpress", "ser-vice2") 3323 c.Assert(err, jc.ErrorIsNil) 3324 rel, err := s.State.AddRelation(eps...) 3325 c.Assert(err, jc.ErrorIsNil) 3326 c.Assert(rel.String(), gc.Equals, "wordpress:db ser-vice2:server") 3327 3328 // model tag is dynamically generated 3329 env, err := s.State.Model() 3330 c.Assert(err, jc.ErrorIsNil) 3331 findEntityTests = append([]findEntityTest{}, findEntityTests...) 3332 findEntityTests = append(findEntityTests, findEntityTest{ 3333 tag: names.NewModelTag(env.UUID()), 3334 }) 3335 3336 for i, test := range findEntityTests { 3337 c.Logf("test %d: %q", i, test.tag) 3338 e, err := s.State.FindEntity(test.tag) 3339 if test.err != "" { 3340 c.Assert(err, gc.ErrorMatches, test.err) 3341 } else { 3342 c.Assert(err, jc.ErrorIsNil) 3343 kind := test.tag.Kind() 3344 c.Assert(e, gc.FitsTypeOf, entityTypes[kind]) 3345 if kind == names.ModelTagKind { 3346 // TODO(axw) 2013-12-04 #1257587 3347 // We *should* only be able to get the entity with its tag, but 3348 // for backwards-compatibility we accept any non-UUID tag. 3349 c.Assert(e.Tag(), gc.Equals, env.Tag()) 3350 } else if kind == names.UserTagKind { 3351 // Test the fully qualified username rather than the tag structure itself. 3352 expected := test.tag.(names.UserTag).Canonical() 3353 c.Assert(e.Tag().(names.UserTag).Canonical(), gc.Equals, expected) 3354 } else { 3355 c.Assert(e.Tag(), gc.Equals, test.tag) 3356 } 3357 } 3358 } 3359 } 3360 3361 func (s *StateSuite) TestParseNilTagReturnsAnError(c *gc.C) { 3362 coll, id, err := state.ConvertTagToCollectionNameAndId(s.State, nil) 3363 c.Assert(err, gc.ErrorMatches, "tag is nil") 3364 c.Assert(coll, gc.Equals, "") 3365 c.Assert(id, gc.IsNil) 3366 } 3367 3368 func (s *StateSuite) TestParseMachineTag(c *gc.C) { 3369 m, err := s.State.AddMachine("quantal", state.JobHostUnits) 3370 c.Assert(err, jc.ErrorIsNil) 3371 coll, id, err := state.ConvertTagToCollectionNameAndId(s.State, m.Tag()) 3372 c.Assert(err, jc.ErrorIsNil) 3373 c.Assert(coll, gc.Equals, "machines") 3374 c.Assert(id, gc.Equals, state.DocID(s.State, m.Id())) 3375 } 3376 3377 func (s *StateSuite) TestParseServiceTag(c *gc.C) { 3378 svc := s.AddTestingService(c, "ser-vice2", s.AddTestingCharm(c, "dummy")) 3379 coll, id, err := state.ConvertTagToCollectionNameAndId(s.State, svc.Tag()) 3380 c.Assert(err, jc.ErrorIsNil) 3381 c.Assert(coll, gc.Equals, "services") 3382 c.Assert(id, gc.Equals, state.DocID(s.State, svc.Name())) 3383 } 3384 3385 func (s *StateSuite) TestParseUnitTag(c *gc.C) { 3386 svc := s.AddTestingService(c, "service2", s.AddTestingCharm(c, "dummy")) 3387 u, err := svc.AddUnit() 3388 c.Assert(err, jc.ErrorIsNil) 3389 coll, id, err := state.ConvertTagToCollectionNameAndId(s.State, u.Tag()) 3390 c.Assert(err, jc.ErrorIsNil) 3391 c.Assert(coll, gc.Equals, "units") 3392 c.Assert(id, gc.Equals, state.DocID(s.State, u.Name())) 3393 } 3394 3395 func (s *StateSuite) TestParseActionTag(c *gc.C) { 3396 svc := s.AddTestingService(c, "service2", s.AddTestingCharm(c, "dummy")) 3397 u, err := svc.AddUnit() 3398 c.Assert(err, jc.ErrorIsNil) 3399 f, err := u.AddAction("snapshot", nil) 3400 c.Assert(err, jc.ErrorIsNil) 3401 action, err := s.State.Action(f.Id()) 3402 c.Assert(action.Tag(), gc.Equals, names.NewActionTag(action.Id())) 3403 coll, id, err := state.ConvertTagToCollectionNameAndId(s.State, action.Tag()) 3404 c.Assert(err, jc.ErrorIsNil) 3405 c.Assert(coll, gc.Equals, "actions") 3406 c.Assert(id, gc.Equals, action.Id()) 3407 } 3408 3409 func (s *StateSuite) TestParseUserTag(c *gc.C) { 3410 user := s.Factory.MakeUser(c, nil) 3411 coll, id, err := state.ConvertTagToCollectionNameAndId(s.State, user.Tag()) 3412 c.Assert(err, jc.ErrorIsNil) 3413 c.Assert(coll, gc.Equals, "users") 3414 c.Assert(id, gc.Equals, user.Name()) 3415 } 3416 3417 func (s *StateSuite) TestParseModelTag(c *gc.C) { 3418 env, err := s.State.Model() 3419 c.Assert(err, jc.ErrorIsNil) 3420 coll, id, err := state.ConvertTagToCollectionNameAndId(s.State, env.Tag()) 3421 c.Assert(err, jc.ErrorIsNil) 3422 c.Assert(coll, gc.Equals, "models") 3423 c.Assert(id, gc.Equals, env.UUID()) 3424 } 3425 3426 func (s *StateSuite) TestWatchCleanups(c *gc.C) { 3427 // Check initial event. 3428 w := s.State.WatchCleanups() 3429 defer statetesting.AssertStop(c, w) 3430 wc := statetesting.NewNotifyWatcherC(c, s.State, w) 3431 wc.AssertOneChange() 3432 3433 // Set up two relations for later use, check no events. 3434 s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 3435 s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) 3436 eps, err := s.State.InferEndpoints("wordpress", "mysql") 3437 c.Assert(err, jc.ErrorIsNil) 3438 relM, err := s.State.AddRelation(eps...) 3439 c.Assert(err, jc.ErrorIsNil) 3440 s.AddTestingService(c, "varnish", s.AddTestingCharm(c, "varnish")) 3441 c.Assert(err, jc.ErrorIsNil) 3442 eps, err = s.State.InferEndpoints("wordpress", "varnish") 3443 c.Assert(err, jc.ErrorIsNil) 3444 relV, err := s.State.AddRelation(eps...) 3445 c.Assert(err, jc.ErrorIsNil) 3446 wc.AssertNoChange() 3447 3448 // Destroy one relation, check one change. 3449 err = relM.Destroy() 3450 c.Assert(err, jc.ErrorIsNil) 3451 wc.AssertOneChange() 3452 3453 // Handle that cleanup doc and create another, check one change. 3454 err = s.State.Cleanup() 3455 c.Assert(err, jc.ErrorIsNil) 3456 err = relV.Destroy() 3457 c.Assert(err, jc.ErrorIsNil) 3458 wc.AssertOneChange() 3459 3460 // Clean up final doc, check change. 3461 err = s.State.Cleanup() 3462 c.Assert(err, jc.ErrorIsNil) 3463 wc.AssertOneChange() 3464 3465 // Stop watcher, check closed. 3466 statetesting.AssertStop(c, w) 3467 wc.AssertClosed() 3468 } 3469 3470 func (s *StateSuite) TestWatchCleanupsDiesOnStateClose(c *gc.C) { 3471 testWatcherDiesWhenStateCloses(c, s.modelTag, func(c *gc.C, st *state.State) waiter { 3472 w := st.WatchCleanups() 3473 <-w.Changes() 3474 return w 3475 }) 3476 } 3477 3478 func (s *StateSuite) TestWatchCleanupsBulk(c *gc.C) { 3479 // Check initial event. 3480 w := s.State.WatchCleanups() 3481 defer statetesting.AssertStop(c, w) 3482 wc := statetesting.NewNotifyWatcherC(c, s.State, w) 3483 wc.AssertOneChange() 3484 3485 // Create two peer relations by creating their services. 3486 riak := s.AddTestingService(c, "riak", s.AddTestingCharm(c, "riak")) 3487 _, err := riak.Endpoint("ring") 3488 c.Assert(err, jc.ErrorIsNil) 3489 allHooks := s.AddTestingService(c, "all-hooks", s.AddTestingCharm(c, "all-hooks")) 3490 _, err = allHooks.Endpoint("self") 3491 c.Assert(err, jc.ErrorIsNil) 3492 wc.AssertNoChange() 3493 3494 // Destroy them both, check one change. 3495 err = riak.Destroy() 3496 c.Assert(err, jc.ErrorIsNil) 3497 err = allHooks.Destroy() 3498 c.Assert(err, jc.ErrorIsNil) 3499 wc.AssertOneChange() 3500 3501 // Clean them both up, check one change. 3502 err = s.State.Cleanup() 3503 c.Assert(err, jc.ErrorIsNil) 3504 wc.AssertOneChange() 3505 } 3506 3507 func (s *StateSuite) TestWatchMinUnits(c *gc.C) { 3508 // Check initial event. 3509 w := s.State.WatchMinUnits() 3510 defer statetesting.AssertStop(c, w) 3511 wc := statetesting.NewStringsWatcherC(c, s.State, w) 3512 wc.AssertChange() 3513 wc.AssertNoChange() 3514 3515 // Set up services for later use. 3516 wordpress := s.AddTestingService(c, 3517 "wordpress", s.AddTestingCharm(c, "wordpress")) 3518 mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) 3519 wordpressName := wordpress.Name() 3520 3521 // Add service units for later use. 3522 wordpress0, err := wordpress.AddUnit() 3523 c.Assert(err, jc.ErrorIsNil) 3524 wordpress1, err := wordpress.AddUnit() 3525 c.Assert(err, jc.ErrorIsNil) 3526 mysql0, err := mysql.AddUnit() 3527 c.Assert(err, jc.ErrorIsNil) 3528 // No events should occur. 3529 wc.AssertNoChange() 3530 3531 // Add minimum units to a service; a single change should occur. 3532 err = wordpress.SetMinUnits(2) 3533 c.Assert(err, jc.ErrorIsNil) 3534 wc.AssertChange(wordpressName) 3535 wc.AssertNoChange() 3536 3537 // Decrease minimum units for a service; expect no changes. 3538 err = wordpress.SetMinUnits(1) 3539 c.Assert(err, jc.ErrorIsNil) 3540 wc.AssertNoChange() 3541 3542 // Increase minimum units for two services; a single change should occur. 3543 err = mysql.SetMinUnits(1) 3544 c.Assert(err, jc.ErrorIsNil) 3545 err = wordpress.SetMinUnits(3) 3546 c.Assert(err, jc.ErrorIsNil) 3547 wc.AssertChange(mysql.Name(), wordpressName) 3548 wc.AssertNoChange() 3549 3550 // Remove minimum units for a service; expect no changes. 3551 err = mysql.SetMinUnits(0) 3552 c.Assert(err, jc.ErrorIsNil) 3553 wc.AssertNoChange() 3554 3555 // Destroy a unit of a service with required minimum units. 3556 // Also avoid the unit removal. A single change should occur. 3557 preventUnitDestroyRemove(c, wordpress0) 3558 err = wordpress0.Destroy() 3559 c.Assert(err, jc.ErrorIsNil) 3560 wc.AssertChange(wordpressName) 3561 wc.AssertNoChange() 3562 3563 // Two actions: destroy a unit and increase minimum units for a service. 3564 // A single change should occur, and the service name should appear only 3565 // one time in the change. 3566 err = wordpress.SetMinUnits(5) 3567 c.Assert(err, jc.ErrorIsNil) 3568 err = wordpress1.Destroy() 3569 c.Assert(err, jc.ErrorIsNil) 3570 wc.AssertChange(wordpressName) 3571 wc.AssertNoChange() 3572 3573 // Destroy a unit of a service not requiring minimum units; expect no changes. 3574 err = mysql0.Destroy() 3575 c.Assert(err, jc.ErrorIsNil) 3576 wc.AssertNoChange() 3577 3578 // Destroy a service with required minimum units; expect no changes. 3579 err = wordpress.Destroy() 3580 c.Assert(err, jc.ErrorIsNil) 3581 wc.AssertNoChange() 3582 3583 // Destroy a service not requiring minimum units; expect no changes. 3584 err = mysql.Destroy() 3585 c.Assert(err, jc.ErrorIsNil) 3586 wc.AssertNoChange() 3587 3588 // Stop watcher, check closed. 3589 statetesting.AssertStop(c, w) 3590 wc.AssertClosed() 3591 } 3592 3593 func (s *StateSuite) TestWatchMinUnitsDiesOnStateClose(c *gc.C) { 3594 testWatcherDiesWhenStateCloses(c, s.modelTag, func(c *gc.C, st *state.State) waiter { 3595 w := st.WatchMinUnits() 3596 <-w.Changes() 3597 return w 3598 }) 3599 } 3600 3601 func (s *StateSuite) TestNestingLevel(c *gc.C) { 3602 c.Assert(state.NestingLevel("0"), gc.Equals, 0) 3603 c.Assert(state.NestingLevel("0/lxc/1"), gc.Equals, 1) 3604 c.Assert(state.NestingLevel("0/lxc/1/kvm/0"), gc.Equals, 2) 3605 } 3606 3607 func (s *StateSuite) TestTopParentId(c *gc.C) { 3608 c.Assert(state.TopParentId("0"), gc.Equals, "0") 3609 c.Assert(state.TopParentId("0/lxc/1"), gc.Equals, "0") 3610 c.Assert(state.TopParentId("0/lxc/1/kvm/2"), gc.Equals, "0") 3611 } 3612 3613 func (s *StateSuite) TestParentId(c *gc.C) { 3614 c.Assert(state.ParentId("0"), gc.Equals, "") 3615 c.Assert(state.ParentId("0/lxc/1"), gc.Equals, "0") 3616 c.Assert(state.ParentId("0/lxc/1/kvm/0"), gc.Equals, "0/lxc/1") 3617 } 3618 3619 func (s *StateSuite) TestContainerTypeFromId(c *gc.C) { 3620 c.Assert(state.ContainerTypeFromId("0"), gc.Equals, instance.ContainerType("")) 3621 c.Assert(state.ContainerTypeFromId("0/lxc/1"), gc.Equals, instance.LXC) 3622 c.Assert(state.ContainerTypeFromId("0/lxc/1/kvm/0"), gc.Equals, instance.KVM) 3623 } 3624 3625 func (s *StateSuite) TestIsUpgradeInProgressError(c *gc.C) { 3626 c.Assert(state.IsUpgradeInProgressError(errors.New("foo")), jc.IsFalse) 3627 c.Assert(state.IsUpgradeInProgressError(state.UpgradeInProgressError), jc.IsTrue) 3628 c.Assert(state.IsUpgradeInProgressError(errors.Trace(state.UpgradeInProgressError)), jc.IsTrue) 3629 } 3630 3631 func (s *StateSuite) TestSetEnvironAgentVersionErrors(c *gc.C) { 3632 // Get the agent-version set in the model. 3633 envConfig, err := s.State.ModelConfig() 3634 c.Assert(err, jc.ErrorIsNil) 3635 agentVersion, ok := envConfig.AgentVersion() 3636 c.Assert(ok, jc.IsTrue) 3637 stringVersion := agentVersion.String() 3638 3639 // Add 4 machines: one with a different version, one with an 3640 // empty version, one with the current version, and one with 3641 // the new version. 3642 machine0, err := s.State.AddMachine("series", state.JobHostUnits) 3643 c.Assert(err, jc.ErrorIsNil) 3644 err = machine0.SetAgentVersion(version.MustParseBinary("9.9.9-quantal-amd64")) 3645 c.Assert(err, jc.ErrorIsNil) 3646 machine1, err := s.State.AddMachine("series", state.JobHostUnits) 3647 c.Assert(err, jc.ErrorIsNil) 3648 machine2, err := s.State.AddMachine("series", state.JobHostUnits) 3649 c.Assert(err, jc.ErrorIsNil) 3650 err = machine2.SetAgentVersion(version.MustParseBinary(stringVersion + "-quantal-amd64")) 3651 c.Assert(err, jc.ErrorIsNil) 3652 machine3, err := s.State.AddMachine("series", state.JobHostUnits) 3653 c.Assert(err, jc.ErrorIsNil) 3654 err = machine3.SetAgentVersion(version.MustParseBinary("4.5.6-quantal-amd64")) 3655 c.Assert(err, jc.ErrorIsNil) 3656 3657 // Verify machine0 and machine1 are reported as error. 3658 err = s.State.SetModelAgentVersion(version.MustParse("4.5.6")) 3659 expectErr := fmt.Sprintf("some agents have not upgraded to the current model version %s: machine-0, machine-1", stringVersion) 3660 c.Assert(err, gc.ErrorMatches, expectErr) 3661 c.Assert(err, jc.Satisfies, state.IsVersionInconsistentError) 3662 3663 // Add a service and 4 units: one with a different version, one 3664 // with an empty version, one with the current version, and one 3665 // with the new version. 3666 service, err := s.State.AddService(state.AddServiceArgs{Name: "wordpress", Owner: s.Owner.String(), Charm: s.AddTestingCharm(c, "wordpress")}) 3667 c.Assert(err, jc.ErrorIsNil) 3668 unit0, err := service.AddUnit() 3669 c.Assert(err, jc.ErrorIsNil) 3670 err = unit0.SetAgentVersion(version.MustParseBinary("6.6.6-quantal-amd64")) 3671 c.Assert(err, jc.ErrorIsNil) 3672 _, err = service.AddUnit() 3673 c.Assert(err, jc.ErrorIsNil) 3674 unit2, err := service.AddUnit() 3675 c.Assert(err, jc.ErrorIsNil) 3676 err = unit2.SetAgentVersion(version.MustParseBinary(stringVersion + "-quantal-amd64")) 3677 c.Assert(err, jc.ErrorIsNil) 3678 unit3, err := service.AddUnit() 3679 c.Assert(err, jc.ErrorIsNil) 3680 err = unit3.SetAgentVersion(version.MustParseBinary("4.5.6-quantal-amd64")) 3681 c.Assert(err, jc.ErrorIsNil) 3682 3683 // Verify unit0 and unit1 are reported as error, along with the 3684 // machines from before. 3685 err = s.State.SetModelAgentVersion(version.MustParse("4.5.6")) 3686 expectErr = fmt.Sprintf("some agents have not upgraded to the current model version %s: machine-0, machine-1, unit-wordpress-0, unit-wordpress-1", stringVersion) 3687 c.Assert(err, gc.ErrorMatches, expectErr) 3688 c.Assert(err, jc.Satisfies, state.IsVersionInconsistentError) 3689 3690 // Now remove the machines. 3691 for _, machine := range []*state.Machine{machine0, machine1, machine2} { 3692 err = machine.EnsureDead() 3693 c.Assert(err, jc.ErrorIsNil) 3694 err = machine.Remove() 3695 c.Assert(err, jc.ErrorIsNil) 3696 } 3697 3698 // Verify only the units are reported as error. 3699 err = s.State.SetModelAgentVersion(version.MustParse("4.5.6")) 3700 expectErr = fmt.Sprintf("some agents have not upgraded to the current model version %s: unit-wordpress-0, unit-wordpress-1", stringVersion) 3701 c.Assert(err, gc.ErrorMatches, expectErr) 3702 c.Assert(err, jc.Satisfies, state.IsVersionInconsistentError) 3703 } 3704 3705 func (s *StateSuite) prepareAgentVersionTests(c *gc.C, st *state.State) (*config.Config, string) { 3706 // Get the agent-version set in the model. 3707 envConfig, err := st.ModelConfig() 3708 c.Assert(err, jc.ErrorIsNil) 3709 agentVersion, ok := envConfig.AgentVersion() 3710 c.Assert(ok, jc.IsTrue) 3711 currentVersion := agentVersion.String() 3712 3713 // Add a machine and a unit with the current version. 3714 machine, err := st.AddMachine("series", state.JobHostUnits) 3715 c.Assert(err, jc.ErrorIsNil) 3716 service, err := st.AddService(state.AddServiceArgs{Name: "wordpress", Owner: s.Owner.String(), Charm: s.AddTestingCharm(c, "wordpress")}) 3717 c.Assert(err, jc.ErrorIsNil) 3718 unit, err := service.AddUnit() 3719 c.Assert(err, jc.ErrorIsNil) 3720 3721 err = machine.SetAgentVersion(version.MustParseBinary(currentVersion + "-quantal-amd64")) 3722 c.Assert(err, jc.ErrorIsNil) 3723 err = unit.SetAgentVersion(version.MustParseBinary(currentVersion + "-quantal-amd64")) 3724 c.Assert(err, jc.ErrorIsNil) 3725 3726 return envConfig, currentVersion 3727 } 3728 3729 func (s *StateSuite) changeEnviron(c *gc.C, envConfig *config.Config, name string, value interface{}) { 3730 attrs := envConfig.AllAttrs() 3731 attrs[name] = value 3732 c.Assert(s.State.UpdateModelConfig(attrs, nil, nil), gc.IsNil) 3733 } 3734 3735 func assertAgentVersion(c *gc.C, st *state.State, vers string) { 3736 envConfig, err := st.ModelConfig() 3737 c.Assert(err, jc.ErrorIsNil) 3738 agentVersion, ok := envConfig.AgentVersion() 3739 c.Assert(ok, jc.IsTrue) 3740 c.Assert(agentVersion.String(), gc.Equals, vers) 3741 } 3742 3743 func (s *StateSuite) TestSetEnvironAgentVersionRetriesOnConfigChange(c *gc.C) { 3744 envConfig, _ := s.prepareAgentVersionTests(c, s.State) 3745 3746 // Set up a transaction hook to change something 3747 // other than the version, and make sure it retries 3748 // and passes. 3749 defer state.SetBeforeHooks(c, s.State, func() { 3750 s.changeEnviron(c, envConfig, "default-series", "foo") 3751 }).Check() 3752 3753 // Change the agent-version and ensure it has changed. 3754 err := s.State.SetModelAgentVersion(version.MustParse("4.5.6")) 3755 c.Assert(err, jc.ErrorIsNil) 3756 assertAgentVersion(c, s.State, "4.5.6") 3757 } 3758 3759 func (s *StateSuite) TestSetEnvironAgentVersionSucceedsWithSameVersion(c *gc.C) { 3760 envConfig, _ := s.prepareAgentVersionTests(c, s.State) 3761 3762 // Set up a transaction hook to change the version 3763 // to the new one, and make sure it retries 3764 // and passes. 3765 defer state.SetBeforeHooks(c, s.State, func() { 3766 s.changeEnviron(c, envConfig, "agent-version", "4.5.6") 3767 }).Check() 3768 3769 // Change the agent-version and verify. 3770 err := s.State.SetModelAgentVersion(version.MustParse("4.5.6")) 3771 c.Assert(err, jc.ErrorIsNil) 3772 assertAgentVersion(c, s.State, "4.5.6") 3773 } 3774 3775 func (s *StateSuite) TestSetEnvironAgentVersionOnOtherEnviron(c *gc.C) { 3776 current := version.MustParseBinary("1.24.7-trusty-amd64") 3777 s.PatchValue(&jujuversion.Current, current.Number) 3778 s.PatchValue(&arch.HostArch, func() string { return current.Arch }) 3779 s.PatchValue(&series.HostSeries, func() string { return current.Series }) 3780 3781 otherSt := s.Factory.MakeModel(c, nil) 3782 defer otherSt.Close() 3783 3784 higher := version.MustParseBinary("1.25.0-trusty-amd64") 3785 lower := version.MustParseBinary("1.24.6-trusty-amd64") 3786 3787 // Set other environ version to < server environ version 3788 err := otherSt.SetModelAgentVersion(lower.Number) 3789 c.Assert(err, jc.ErrorIsNil) 3790 assertAgentVersion(c, otherSt, lower.Number.String()) 3791 3792 // Set other environ version == server environ version 3793 err = otherSt.SetModelAgentVersion(jujuversion.Current) 3794 c.Assert(err, jc.ErrorIsNil) 3795 assertAgentVersion(c, otherSt, jujuversion.Current.String()) 3796 3797 // Set other environ version to > server environ version 3798 err = otherSt.SetModelAgentVersion(higher.Number) 3799 expected := fmt.Sprintf("a hosted model cannot have a higher version than the server model: %s > %s", 3800 higher.Number, 3801 jujuversion.Current, 3802 ) 3803 c.Assert(err, gc.ErrorMatches, expected) 3804 } 3805 3806 func (s *StateSuite) TestSetEnvironAgentVersionExcessiveContention(c *gc.C) { 3807 envConfig, currentVersion := s.prepareAgentVersionTests(c, s.State) 3808 3809 // Set a hook to change the config 3 times 3810 // to test we return ErrExcessiveContention. 3811 changeFuncs := []func(){ 3812 func() { s.changeEnviron(c, envConfig, "default-series", "1") }, 3813 func() { s.changeEnviron(c, envConfig, "default-series", "2") }, 3814 func() { s.changeEnviron(c, envConfig, "default-series", "3") }, 3815 } 3816 defer state.SetBeforeHooks(c, s.State, changeFuncs...).Check() 3817 err := s.State.SetModelAgentVersion(version.MustParse("4.5.6")) 3818 c.Assert(errors.Cause(err), gc.Equals, txn.ErrExcessiveContention) 3819 // Make sure the version remained the same. 3820 assertAgentVersion(c, s.State, currentVersion) 3821 } 3822 3823 func (s *StateSuite) TestSetEnvironAgentFailsIfUpgrading(c *gc.C) { 3824 // Get the agent-version set in the model. 3825 envConfig, err := s.State.ModelConfig() 3826 c.Assert(err, jc.ErrorIsNil) 3827 agentVersion, ok := envConfig.AgentVersion() 3828 c.Assert(ok, jc.IsTrue) 3829 3830 machine, err := s.State.AddMachine("series", state.JobManageModel) 3831 c.Assert(err, jc.ErrorIsNil) 3832 err = machine.SetAgentVersion(version.MustParseBinary(agentVersion.String() + "-quantal-amd64")) 3833 c.Assert(err, jc.ErrorIsNil) 3834 err = machine.SetProvisioned(instance.Id("i-blah"), "fake-nonce", nil) 3835 c.Assert(err, jc.ErrorIsNil) 3836 3837 nextVersion := agentVersion 3838 nextVersion.Minor++ 3839 3840 // Create an unfinished UpgradeInfo instance. 3841 _, err = s.State.EnsureUpgradeInfo(machine.Tag().Id(), agentVersion, nextVersion) 3842 c.Assert(err, jc.ErrorIsNil) 3843 3844 err = s.State.SetModelAgentVersion(nextVersion) 3845 c.Assert(err, jc.Satisfies, state.IsUpgradeInProgressError) 3846 } 3847 3848 func (s *StateSuite) TestSetEnvironAgentFailsReportsCorrectError(c *gc.C) { 3849 // Ensure that the correct error is reported if an upgrade is 3850 // progress but that isn't the reason for the 3851 // SetModelAgentVersion call failing. 3852 3853 // Get the agent-version set in the model. 3854 envConfig, err := s.State.ModelConfig() 3855 c.Assert(err, jc.ErrorIsNil) 3856 agentVersion, ok := envConfig.AgentVersion() 3857 c.Assert(ok, jc.IsTrue) 3858 3859 machine, err := s.State.AddMachine("series", state.JobManageModel) 3860 c.Assert(err, jc.ErrorIsNil) 3861 err = machine.SetAgentVersion(version.MustParseBinary("9.9.9-quantal-amd64")) 3862 c.Assert(err, jc.ErrorIsNil) 3863 err = machine.SetProvisioned(instance.Id("i-blah"), "fake-nonce", nil) 3864 c.Assert(err, jc.ErrorIsNil) 3865 3866 nextVersion := agentVersion 3867 nextVersion.Minor++ 3868 3869 // Create an unfinished UpgradeInfo instance. 3870 _, err = s.State.EnsureUpgradeInfo(machine.Tag().Id(), agentVersion, nextVersion) 3871 c.Assert(err, jc.ErrorIsNil) 3872 3873 err = s.State.SetModelAgentVersion(nextVersion) 3874 c.Assert(err, gc.ErrorMatches, "some agents have not upgraded to the current model version.+") 3875 } 3876 3877 type waiter interface { 3878 Wait() error 3879 } 3880 3881 // testWatcherDiesWhenStateCloses calls the given function to start a watcher, 3882 // closes the state and checks that the watcher dies with the expected error. 3883 // The watcher should already have consumed the first 3884 // event, otherwise the watcher's initialisation logic may 3885 // interact with the closed state, causing it to return an 3886 // unexpected error (often "Closed explictly"). 3887 func testWatcherDiesWhenStateCloses(c *gc.C, modelTag names.ModelTag, startWatcher func(c *gc.C, st *state.State) waiter) { 3888 st, err := state.Open(modelTag, statetesting.NewMongoInfo(), mongotest.DialOpts(), state.Policy(nil)) 3889 c.Assert(err, jc.ErrorIsNil) 3890 watcher := startWatcher(c, st) 3891 err = st.Close() 3892 c.Assert(err, jc.ErrorIsNil) 3893 done := make(chan error) 3894 go func() { 3895 done <- watcher.Wait() 3896 }() 3897 select { 3898 case err := <-done: 3899 c.Assert(err, gc.ErrorMatches, state.ErrStateClosed.Error()) 3900 case <-time.After(testing.LongWait): 3901 c.Fatalf("watcher %T did not exit when state closed", watcher) 3902 } 3903 } 3904 3905 func (s *StateSuite) TestControllerInfo(c *gc.C) { 3906 ids, err := s.State.ControllerInfo() 3907 c.Assert(err, jc.ErrorIsNil) 3908 c.Assert(ids.ModelTag, gc.Equals, s.modelTag) 3909 c.Assert(ids.MachineIds, gc.HasLen, 0) 3910 c.Assert(ids.VotingMachineIds, gc.HasLen, 0) 3911 3912 // TODO(rog) more testing here when we can actually add 3913 // controllers. 3914 } 3915 3916 func (s *StateSuite) TestControllerInfoWithPreMigrationDoc(c *gc.C) { 3917 err := s.controllers.Update( 3918 nil, 3919 bson.D{{"$unset", bson.D{{"model-uuid", 1}}}}, 3920 ) 3921 c.Assert(err, jc.ErrorIsNil) 3922 3923 ids, err := s.State.ControllerInfo() 3924 c.Assert(err, jc.ErrorIsNil) 3925 c.Assert(ids.ModelTag, gc.Equals, s.modelTag) 3926 } 3927 3928 func (s *StateSuite) TestReopenWithNoMachines(c *gc.C) { 3929 expected := &state.ControllerInfo{ 3930 ModelTag: s.modelTag, 3931 } 3932 info, err := s.State.ControllerInfo() 3933 c.Assert(err, jc.ErrorIsNil) 3934 c.Assert(info, jc.DeepEquals, expected) 3935 3936 st, err := state.Open(s.modelTag, statetesting.NewMongoInfo(), mongotest.DialOpts(), state.Policy(nil)) 3937 c.Assert(err, jc.ErrorIsNil) 3938 defer st.Close() 3939 3940 info, err = s.State.ControllerInfo() 3941 c.Assert(err, jc.ErrorIsNil) 3942 c.Assert(info, jc.DeepEquals, expected) 3943 } 3944 3945 func (s *StateSuite) TestEnableHAFailsWithBadCount(c *gc.C) { 3946 for _, n := range []int{-1, 2, 6} { 3947 changes, err := s.State.EnableHA(n, constraints.Value{}, "", nil) 3948 c.Assert(err, gc.ErrorMatches, "number of controllers must be odd and non-negative") 3949 c.Assert(changes.Added, gc.HasLen, 0) 3950 } 3951 _, err := s.State.EnableHA(replicaset.MaxPeers+2, constraints.Value{}, "", nil) 3952 c.Assert(err, gc.ErrorMatches, `controller count is too large \(allowed \d+\)`) 3953 } 3954 3955 func (s *StateSuite) TestEnableHAAddsNewMachines(c *gc.C) { 3956 // Don't use agent presence to decide on machine availability. 3957 s.PatchValue(state.ControllerAvailable, func(m *state.Machine) (bool, error) { 3958 return true, nil 3959 }) 3960 3961 ids := make([]string, 3) 3962 m0, err := s.State.AddMachine("quantal", state.JobHostUnits, state.JobManageModel) 3963 c.Assert(err, jc.ErrorIsNil) 3964 ids[0] = m0.Id() 3965 3966 // Add a non-controller machine just to make sure. 3967 _, err = s.State.AddMachine("quantal", state.JobHostUnits) 3968 c.Assert(err, jc.ErrorIsNil) 3969 3970 s.assertControllerInfo(c, []string{"0"}, []string{"0"}, nil) 3971 3972 cons := constraints.Value{ 3973 Mem: newUint64(100), 3974 } 3975 changes, err := s.State.EnableHA(3, cons, "quantal", nil) 3976 c.Assert(err, jc.ErrorIsNil) 3977 c.Assert(changes.Added, gc.HasLen, 2) 3978 3979 for i := 1; i < 3; i++ { 3980 m, err := s.State.Machine(fmt.Sprint(i + 1)) 3981 c.Assert(err, jc.ErrorIsNil) 3982 c.Assert(m.Jobs(), gc.DeepEquals, []state.MachineJob{ 3983 state.JobHostUnits, 3984 state.JobManageModel, 3985 }) 3986 gotCons, err := m.Constraints() 3987 c.Assert(err, jc.ErrorIsNil) 3988 c.Assert(gotCons, gc.DeepEquals, cons) 3989 c.Assert(m.WantsVote(), jc.IsTrue) 3990 ids[i] = m.Id() 3991 } 3992 s.assertControllerInfo(c, ids, ids, nil) 3993 } 3994 3995 func (s *StateSuite) TestEnableHATo(c *gc.C) { 3996 // Don't use agent presence to decide on machine availability. 3997 s.PatchValue(state.ControllerAvailable, func(m *state.Machine) (bool, error) { 3998 return true, nil 3999 }) 4000 4001 ids := make([]string, 3) 4002 m0, err := s.State.AddMachine("quantal", state.JobHostUnits, state.JobManageModel) 4003 c.Assert(err, jc.ErrorIsNil) 4004 ids[0] = m0.Id() 4005 4006 // Add two non-controller machines. 4007 _, err = s.State.AddMachine("quantal", state.JobHostUnits) 4008 c.Assert(err, jc.ErrorIsNil) 4009 4010 _, err = s.State.AddMachine("quantal", state.JobHostUnits) 4011 c.Assert(err, jc.ErrorIsNil) 4012 4013 s.assertControllerInfo(c, []string{"0"}, []string{"0"}, nil) 4014 4015 changes, err := s.State.EnableHA(3, constraints.Value{}, "quantal", []string{"1", "2"}) 4016 c.Assert(err, jc.ErrorIsNil) 4017 c.Assert(changes.Added, gc.HasLen, 0) 4018 c.Assert(changes.Converted, gc.HasLen, 2) 4019 4020 for i := 1; i < 3; i++ { 4021 m, err := s.State.Machine(fmt.Sprint(i)) 4022 c.Assert(err, jc.ErrorIsNil) 4023 c.Assert(m.Jobs(), gc.DeepEquals, []state.MachineJob{ 4024 state.JobHostUnits, 4025 state.JobManageModel, 4026 }) 4027 gotCons, err := m.Constraints() 4028 c.Assert(err, jc.ErrorIsNil) 4029 c.Assert(gotCons, gc.DeepEquals, constraints.Value{}) 4030 c.Assert(m.WantsVote(), jc.IsTrue) 4031 ids[i] = m.Id() 4032 } 4033 s.assertControllerInfo(c, ids, ids, nil) 4034 } 4035 4036 func newUint64(i uint64) *uint64 { 4037 return &i 4038 } 4039 4040 func (s *StateSuite) assertControllerInfo(c *gc.C, machineIds []string, votingMachineIds []string, placement []string) { 4041 info, err := s.State.ControllerInfo() 4042 c.Assert(err, jc.ErrorIsNil) 4043 c.Assert(info.ModelTag, gc.Equals, s.modelTag) 4044 c.Assert(info.MachineIds, jc.SameContents, machineIds) 4045 c.Assert(info.VotingMachineIds, jc.SameContents, votingMachineIds) 4046 for i, id := range machineIds { 4047 m, err := s.State.Machine(id) 4048 c.Assert(err, jc.ErrorIsNil) 4049 if len(placement) == 0 || i >= len(placement) { 4050 c.Check(m.Placement(), gc.Equals, "") 4051 } else { 4052 c.Check(m.Placement(), gc.Equals, placement[i]) 4053 } 4054 } 4055 } 4056 4057 func (s *StateSuite) TestEnableHASamePlacementAsNewCount(c *gc.C) { 4058 placement := []string{"p1", "p2", "p3"} 4059 changes, err := s.State.EnableHA(3, constraints.Value{}, "quantal", placement) 4060 c.Assert(err, jc.ErrorIsNil) 4061 c.Assert(changes.Added, gc.HasLen, 3) 4062 s.assertControllerInfo(c, []string{"0", "1", "2"}, []string{"0", "1", "2"}, []string{"p1", "p2", "p3"}) 4063 } 4064 4065 func (s *StateSuite) TestEnableHAMorePlacementThanNewCount(c *gc.C) { 4066 placement := []string{"p1", "p2", "p3", "p4"} 4067 changes, err := s.State.EnableHA(3, constraints.Value{}, "quantal", placement) 4068 c.Assert(err, jc.ErrorIsNil) 4069 c.Assert(changes.Added, gc.HasLen, 3) 4070 s.assertControllerInfo(c, []string{"0", "1", "2"}, []string{"0", "1", "2"}, []string{"p1", "p2", "p3"}) 4071 } 4072 4073 func (s *StateSuite) TestEnableHALessPlacementThanNewCount(c *gc.C) { 4074 placement := []string{"p1", "p2"} 4075 changes, err := s.State.EnableHA(3, constraints.Value{}, "quantal", placement) 4076 c.Assert(err, jc.ErrorIsNil) 4077 c.Assert(changes.Added, gc.HasLen, 3) 4078 s.assertControllerInfo(c, []string{"0", "1", "2"}, []string{"0", "1", "2"}, []string{"p1", "p2"}) 4079 } 4080 4081 func (s *StateSuite) TestEnableHADemotesUnavailableMachines(c *gc.C) { 4082 changes, err := s.State.EnableHA(3, constraints.Value{}, "quantal", nil) 4083 c.Assert(err, jc.ErrorIsNil) 4084 c.Assert(changes.Added, gc.HasLen, 3) 4085 s.assertControllerInfo(c, []string{"0", "1", "2"}, []string{"0", "1", "2"}, nil) 4086 s.PatchValue(state.ControllerAvailable, func(m *state.Machine) (bool, error) { 4087 return m.Id() != "0", nil 4088 }) 4089 changes, err = s.State.EnableHA(3, constraints.Value{}, "quantal", nil) 4090 c.Assert(err, jc.ErrorIsNil) 4091 c.Assert(changes.Added, gc.HasLen, 1) 4092 c.Assert(changes.Maintained, gc.HasLen, 2) 4093 4094 // New controller machine "3" is created; "0" still exists in MachineIds, 4095 // but no longer in VotingMachineIds. 4096 s.assertControllerInfo(c, []string{"0", "1", "2", "3"}, []string{"1", "2", "3"}, nil) 4097 m0, err := s.State.Machine("0") 4098 c.Assert(err, jc.ErrorIsNil) 4099 c.Assert(m0.WantsVote(), jc.IsFalse) 4100 c.Assert(m0.IsManager(), jc.IsTrue) // job still intact for now 4101 m3, err := s.State.Machine("3") 4102 c.Assert(err, jc.ErrorIsNil) 4103 c.Assert(m3.WantsVote(), jc.IsTrue) 4104 c.Assert(m3.IsManager(), jc.IsTrue) 4105 } 4106 4107 func (s *StateSuite) TestEnableHAPromotesAvailableMachines(c *gc.C) { 4108 changes, err := s.State.EnableHA(3, constraints.Value{}, "quantal", nil) 4109 c.Assert(err, jc.ErrorIsNil) 4110 c.Assert(changes.Added, gc.HasLen, 3) 4111 s.assertControllerInfo(c, []string{"0", "1", "2"}, []string{"0", "1", "2"}, nil) 4112 s.PatchValue(state.ControllerAvailable, func(m *state.Machine) (bool, error) { 4113 return m.Id() != "0", nil 4114 }) 4115 changes, err = s.State.EnableHA(3, constraints.Value{}, "quantal", nil) 4116 c.Assert(err, jc.ErrorIsNil) 4117 c.Assert(changes.Added, gc.HasLen, 1) 4118 c.Assert(changes.Demoted, gc.DeepEquals, []string{"0"}) 4119 c.Assert(changes.Maintained, gc.HasLen, 2) 4120 4121 // New controller machine "3" is created; "0" still exists in MachineIds, 4122 // but no longer in VotingMachineIds. 4123 s.assertControllerInfo(c, []string{"0", "1", "2", "3"}, []string{"1", "2", "3"}, nil) 4124 m0, err := s.State.Machine("0") 4125 c.Assert(err, jc.ErrorIsNil) 4126 c.Assert(m0.WantsVote(), jc.IsFalse) 4127 4128 // Mark machine 0 as having a vote, so it doesn't get removed, and make it 4129 // available once more. 4130 err = m0.SetHasVote(true) 4131 c.Assert(err, jc.ErrorIsNil) 4132 s.PatchValue(state.ControllerAvailable, func(m *state.Machine) (bool, error) { 4133 return true, nil 4134 }) 4135 changes, err = s.State.EnableHA(3, constraints.Value{}, "quantal", nil) 4136 c.Assert(err, jc.ErrorIsNil) 4137 c.Assert(changes.Added, gc.HasLen, 0) 4138 4139 // No change; we've got as many voting machines as we need. 4140 s.assertControllerInfo(c, []string{"0", "1", "2", "3"}, []string{"1", "2", "3"}, nil) 4141 4142 // Make machine 3 unavailable; machine 0 should be promoted, and two new 4143 // machines created. 4144 s.PatchValue(state.ControllerAvailable, func(m *state.Machine) (bool, error) { 4145 return m.Id() != "3", nil 4146 }) 4147 changes, err = s.State.EnableHA(5, constraints.Value{}, "quantal", nil) 4148 c.Assert(err, jc.ErrorIsNil) 4149 c.Assert(changes.Added, gc.HasLen, 2) 4150 c.Assert(changes.Demoted, gc.DeepEquals, []string{"3"}) 4151 s.assertControllerInfo(c, []string{"0", "1", "2", "3", "4", "5"}, []string{"0", "1", "2", "4", "5"}, nil) 4152 err = m0.Refresh() 4153 c.Assert(err, jc.ErrorIsNil) 4154 c.Assert(m0.WantsVote(), jc.IsTrue) 4155 } 4156 4157 func (s *StateSuite) TestEnableHARemovesUnavailableMachines(c *gc.C) { 4158 changes, err := s.State.EnableHA(3, constraints.Value{}, "quantal", nil) 4159 c.Assert(err, jc.ErrorIsNil) 4160 c.Assert(changes.Added, gc.HasLen, 3) 4161 4162 s.assertControllerInfo(c, []string{"0", "1", "2"}, []string{"0", "1", "2"}, nil) 4163 s.PatchValue(state.ControllerAvailable, func(m *state.Machine) (bool, error) { 4164 return m.Id() != "0", nil 4165 }) 4166 changes, err = s.State.EnableHA(3, constraints.Value{}, "quantal", nil) 4167 c.Assert(err, jc.ErrorIsNil) 4168 c.Assert(changes.Added, gc.HasLen, 1) 4169 s.assertControllerInfo(c, []string{"0", "1", "2", "3"}, []string{"1", "2", "3"}, nil) 4170 // machine 0 does not have a vote, so another call to EnableHA 4171 // will remove machine 0's JobEnvironManager job. 4172 changes, err = s.State.EnableHA(3, constraints.Value{}, "quantal", nil) 4173 c.Assert(changes.Removed, gc.HasLen, 1) 4174 c.Assert(changes.Maintained, gc.HasLen, 3) 4175 c.Assert(err, jc.ErrorIsNil) 4176 s.assertControllerInfo(c, []string{"1", "2", "3"}, []string{"1", "2", "3"}, nil) 4177 m0, err := s.State.Machine("0") 4178 c.Assert(err, jc.ErrorIsNil) 4179 c.Assert(m0.IsManager(), jc.IsFalse) 4180 } 4181 4182 func (s *StateSuite) TestEnableHAMaintainsVoteList(c *gc.C) { 4183 changes, err := s.State.EnableHA(5, constraints.Value{}, "quantal", nil) 4184 c.Assert(err, jc.ErrorIsNil) 4185 c.Assert(changes.Added, gc.HasLen, 5) 4186 4187 s.assertControllerInfo(c, 4188 []string{"0", "1", "2", "3", "4"}, 4189 []string{"0", "1", "2", "3", "4"}, nil) 4190 // Mark machine-0 as dead, so we'll want to create another one again 4191 s.PatchValue(state.ControllerAvailable, func(m *state.Machine) (bool, error) { 4192 return m.Id() != "0", nil 4193 }) 4194 changes, err = s.State.EnableHA(0, constraints.Value{}, "quantal", nil) 4195 c.Assert(err, jc.ErrorIsNil) 4196 c.Assert(changes.Added, gc.HasLen, 1) 4197 4198 // New controller machine "5" is created; "0" still exists in MachineIds, 4199 // but no longer in VotingMachineIds. 4200 s.assertControllerInfo(c, 4201 []string{"0", "1", "2", "3", "4", "5"}, 4202 []string{"1", "2", "3", "4", "5"}, nil) 4203 m0, err := s.State.Machine("0") 4204 c.Assert(err, jc.ErrorIsNil) 4205 c.Assert(m0.WantsVote(), jc.IsFalse) 4206 c.Assert(m0.IsManager(), jc.IsTrue) // job still intact for now 4207 m3, err := s.State.Machine("5") 4208 c.Assert(err, jc.ErrorIsNil) 4209 c.Assert(m3.WantsVote(), jc.IsTrue) 4210 c.Assert(m3.IsManager(), jc.IsTrue) 4211 } 4212 4213 func (s *StateSuite) TestEnableHADefaultsTo3(c *gc.C) { 4214 changes, err := s.State.EnableHA(0, constraints.Value{}, "quantal", nil) 4215 c.Assert(err, jc.ErrorIsNil) 4216 c.Assert(changes.Added, gc.HasLen, 3) 4217 s.assertControllerInfo(c, []string{"0", "1", "2"}, []string{"0", "1", "2"}, nil) 4218 // Mark machine-0 as dead, so we'll want to create it again 4219 s.PatchValue(state.ControllerAvailable, func(m *state.Machine) (bool, error) { 4220 return m.Id() != "0", nil 4221 }) 4222 changes, err = s.State.EnableHA(0, constraints.Value{}, "quantal", nil) 4223 c.Assert(err, jc.ErrorIsNil) 4224 c.Assert(changes.Added, gc.HasLen, 1) 4225 4226 // New controller machine "3" is created; "0" still exists in MachineIds, 4227 // but no longer in VotingMachineIds. 4228 s.assertControllerInfo(c, 4229 []string{"0", "1", "2", "3"}, 4230 []string{"1", "2", "3"}, nil) 4231 m0, err := s.State.Machine("0") 4232 c.Assert(err, jc.ErrorIsNil) 4233 c.Assert(m0.WantsVote(), jc.IsFalse) 4234 c.Assert(m0.IsManager(), jc.IsTrue) // job still intact for now 4235 m3, err := s.State.Machine("3") 4236 c.Assert(err, jc.ErrorIsNil) 4237 c.Assert(m3.WantsVote(), jc.IsTrue) 4238 c.Assert(m3.IsManager(), jc.IsTrue) 4239 } 4240 4241 func (s *StateSuite) TestEnableHAConcurrentSame(c *gc.C) { 4242 s.PatchValue(state.ControllerAvailable, func(m *state.Machine) (bool, error) { 4243 return true, nil 4244 }) 4245 4246 defer state.SetBeforeHooks(c, s.State, func() { 4247 changes, err := s.State.EnableHA(3, constraints.Value{}, "quantal", nil) 4248 c.Assert(err, jc.ErrorIsNil) 4249 // The outer EnableHA call will allocate IDs 0..2, 4250 // and the inner one 3..5. 4251 c.Assert(changes.Added, gc.HasLen, 3) 4252 expected := []string{"3", "4", "5"} 4253 s.assertControllerInfo(c, expected, expected, nil) 4254 }).Check() 4255 4256 changes, err := s.State.EnableHA(3, constraints.Value{}, "quantal", nil) 4257 c.Assert(err, jc.ErrorIsNil) 4258 c.Assert(changes.Added, gc.DeepEquals, []string{"0", "1", "2"}) 4259 s.assertControllerInfo(c, []string{"3", "4", "5"}, []string{"3", "4", "5"}, nil) 4260 4261 // Machine 0 should never have been created. 4262 _, err = s.State.Machine("0") 4263 c.Assert(err, jc.Satisfies, errors.IsNotFound) 4264 } 4265 4266 func (s *StateSuite) TestEnableHAConcurrentLess(c *gc.C) { 4267 s.PatchValue(state.ControllerAvailable, func(m *state.Machine) (bool, error) { 4268 return true, nil 4269 }) 4270 4271 defer state.SetBeforeHooks(c, s.State, func() { 4272 changes, err := s.State.EnableHA(3, constraints.Value{}, "quantal", nil) 4273 c.Assert(err, jc.ErrorIsNil) 4274 c.Assert(changes.Added, gc.HasLen, 3) 4275 // The outer EnableHA call will initially allocate IDs 0..4, 4276 // and the inner one 5..7. 4277 expected := []string{"5", "6", "7"} 4278 s.assertControllerInfo(c, expected, expected, nil) 4279 }).Check() 4280 4281 // This call to EnableHA will initially attempt to allocate 4282 // machines 0..4, and fail due to the concurrent change. It will then 4283 // allocate machines 8..9 to make up the difference from the concurrent 4284 // EnableHA call. 4285 changes, err := s.State.EnableHA(5, constraints.Value{}, "quantal", nil) 4286 c.Assert(err, jc.ErrorIsNil) 4287 c.Assert(changes.Added, gc.HasLen, 2) 4288 expected := []string{"5", "6", "7", "8", "9"} 4289 s.assertControllerInfo(c, expected, expected, nil) 4290 4291 // Machine 0 should never have been created. 4292 _, err = s.State.Machine("0") 4293 c.Assert(err, jc.Satisfies, errors.IsNotFound) 4294 } 4295 4296 func (s *StateSuite) TestEnableHAConcurrentMore(c *gc.C) { 4297 s.PatchValue(state.ControllerAvailable, func(m *state.Machine) (bool, error) { 4298 return true, nil 4299 }) 4300 4301 defer state.SetBeforeHooks(c, s.State, func() { 4302 changes, err := s.State.EnableHA(5, constraints.Value{}, "quantal", nil) 4303 c.Assert(err, jc.ErrorIsNil) 4304 c.Assert(changes.Added, gc.HasLen, 5) 4305 // The outer EnableHA call will allocate IDs 0..2, 4306 // and the inner one 3..7. 4307 expected := []string{"3", "4", "5", "6", "7"} 4308 s.assertControllerInfo(c, expected, expected, nil) 4309 }).Check() 4310 4311 // This call to EnableHA will initially attempt to allocate 4312 // machines 0..2, and fail due to the concurrent change. It will then 4313 // find that the number of voting machines in state is greater than 4314 // what we're attempting to ensure, and fail. 4315 changes, err := s.State.EnableHA(3, constraints.Value{}, "quantal", nil) 4316 c.Assert(err, gc.ErrorMatches, "failed to create new controller machines: cannot reduce controller count") 4317 c.Assert(changes.Added, gc.HasLen, 0) 4318 4319 // Machine 0 should never have been created. 4320 _, err = s.State.Machine("0") 4321 c.Assert(err, jc.Satisfies, errors.IsNotFound) 4322 } 4323 4324 func (s *StateSuite) TestStateServingInfo(c *gc.C) { 4325 info, err := s.State.StateServingInfo() 4326 c.Assert(err, gc.ErrorMatches, "state serving info not found") 4327 c.Assert(err, jc.Satisfies, errors.IsNotFound) 4328 4329 data := state.StateServingInfo{ 4330 APIPort: 69, 4331 StatePort: 80, 4332 Cert: "Some cert", 4333 PrivateKey: "Some key", 4334 SharedSecret: "Some Keyfile", 4335 } 4336 err = s.State.SetStateServingInfo(data) 4337 c.Assert(err, jc.ErrorIsNil) 4338 4339 info, err = s.State.StateServingInfo() 4340 c.Assert(err, jc.ErrorIsNil) 4341 c.Assert(info, jc.DeepEquals, data) 4342 } 4343 4344 var setStateServingInfoWithInvalidInfoTests = []func(info *state.StateServingInfo){ 4345 func(info *state.StateServingInfo) { info.APIPort = 0 }, 4346 func(info *state.StateServingInfo) { info.StatePort = 0 }, 4347 func(info *state.StateServingInfo) { info.Cert = "" }, 4348 func(info *state.StateServingInfo) { info.PrivateKey = "" }, 4349 } 4350 4351 func (s *StateSuite) TestSetStateServingInfoWithInvalidInfo(c *gc.C) { 4352 origData := state.StateServingInfo{ 4353 APIPort: 69, 4354 StatePort: 80, 4355 Cert: "Some cert", 4356 PrivateKey: "Some key", 4357 SharedSecret: "Some Keyfile", 4358 } 4359 for i, test := range setStateServingInfoWithInvalidInfoTests { 4360 c.Logf("test %d", i) 4361 data := origData 4362 test(&data) 4363 err := s.State.SetStateServingInfo(data) 4364 c.Assert(err, gc.ErrorMatches, "incomplete state serving info set in state") 4365 } 4366 } 4367 4368 func (s *StateSuite) TestSetAPIHostPorts(c *gc.C) { 4369 addrs, err := s.State.APIHostPorts() 4370 c.Assert(err, jc.ErrorIsNil) 4371 c.Assert(addrs, gc.HasLen, 0) 4372 4373 newHostPorts := [][]network.HostPort{{{ 4374 Address: network.Address{ 4375 Value: "0.2.4.6", 4376 Type: network.IPv4Address, 4377 Scope: network.ScopeCloudLocal, 4378 }, 4379 Port: 1, 4380 }, { 4381 Address: network.Address{ 4382 Value: "0.4.8.16", 4383 Type: network.IPv4Address, 4384 Scope: network.ScopePublic, 4385 }, 4386 Port: 2, 4387 }}, {{ 4388 Address: network.Address{ 4389 Value: "0.6.1.2", 4390 Type: network.IPv4Address, 4391 Scope: network.ScopeCloudLocal, 4392 }, 4393 Port: 5, 4394 }}} 4395 err = s.State.SetAPIHostPorts(newHostPorts) 4396 c.Assert(err, jc.ErrorIsNil) 4397 4398 gotHostPorts, err := s.State.APIHostPorts() 4399 c.Assert(err, jc.ErrorIsNil) 4400 c.Assert(gotHostPorts, jc.DeepEquals, newHostPorts) 4401 4402 newHostPorts = [][]network.HostPort{{{ 4403 Address: network.Address{ 4404 Value: "0.2.4.6", 4405 Type: network.IPv6Address, 4406 Scope: network.ScopeCloudLocal, 4407 }, 4408 Port: 13, 4409 }}} 4410 err = s.State.SetAPIHostPorts(newHostPorts) 4411 c.Assert(err, jc.ErrorIsNil) 4412 4413 gotHostPorts, err = s.State.APIHostPorts() 4414 c.Assert(err, jc.ErrorIsNil) 4415 c.Assert(gotHostPorts, jc.DeepEquals, newHostPorts) 4416 } 4417 4418 func (s *StateSuite) TestSetAPIHostPortsConcurrentSame(c *gc.C) { 4419 hostPorts := [][]network.HostPort{{{ 4420 Address: network.Address{ 4421 Value: "0.4.8.16", 4422 Type: network.IPv4Address, 4423 Scope: network.ScopePublic, 4424 }, 4425 Port: 2, 4426 }}, {{ 4427 Address: network.Address{ 4428 Value: "0.2.4.6", 4429 Type: network.IPv4Address, 4430 Scope: network.ScopeCloudLocal, 4431 }, 4432 Port: 1, 4433 }}} 4434 4435 // API host ports are concurrently changed to the same 4436 // desired value; second arrival will fail its assertion, 4437 // refresh finding nothing to do, and then issue a 4438 // read-only assertion that suceeds. 4439 4440 var prevRevno int64 4441 defer state.SetBeforeHooks(c, s.State, func() { 4442 err := s.State.SetAPIHostPorts(hostPorts) 4443 c.Assert(err, jc.ErrorIsNil) 4444 revno, err := state.TxnRevno(s.State, "controllers", "apiHostPorts") 4445 c.Assert(err, jc.ErrorIsNil) 4446 prevRevno = revno 4447 }).Check() 4448 4449 err := s.State.SetAPIHostPorts(hostPorts) 4450 c.Assert(err, jc.ErrorIsNil) 4451 c.Assert(prevRevno, gc.Not(gc.Equals), 0) 4452 revno, err := state.TxnRevno(s.State, "controllers", "apiHostPorts") 4453 c.Assert(err, jc.ErrorIsNil) 4454 c.Assert(revno, gc.Equals, prevRevno) 4455 } 4456 4457 func (s *StateSuite) TestSetAPIHostPortsConcurrentDifferent(c *gc.C) { 4458 hostPorts0 := []network.HostPort{{ 4459 Address: network.Address{ 4460 Value: "0.4.8.16", 4461 Type: network.IPv4Address, 4462 Scope: network.ScopePublic, 4463 }, 4464 Port: 2, 4465 }} 4466 hostPorts1 := []network.HostPort{{ 4467 Address: network.Address{ 4468 Value: "0.2.4.6", 4469 Type: network.IPv4Address, 4470 Scope: network.ScopeCloudLocal, 4471 }, 4472 Port: 1, 4473 }} 4474 4475 // API host ports are concurrently changed to different 4476 // values; second arrival will fail its assertion, refresh 4477 // finding and reattempt. 4478 4479 var prevRevno int64 4480 defer state.SetBeforeHooks(c, s.State, func() { 4481 err := s.State.SetAPIHostPorts([][]network.HostPort{hostPorts0}) 4482 c.Assert(err, jc.ErrorIsNil) 4483 revno, err := state.TxnRevno(s.State, "controllers", "apiHostPorts") 4484 c.Assert(err, jc.ErrorIsNil) 4485 prevRevno = revno 4486 }).Check() 4487 4488 err := s.State.SetAPIHostPorts([][]network.HostPort{hostPorts1}) 4489 c.Assert(err, jc.ErrorIsNil) 4490 c.Assert(prevRevno, gc.Not(gc.Equals), 0) 4491 revno, err := state.TxnRevno(s.State, "controllers", "apiHostPorts") 4492 c.Assert(err, jc.ErrorIsNil) 4493 c.Assert(revno, gc.Not(gc.Equals), prevRevno) 4494 4495 hostPorts, err := s.State.APIHostPorts() 4496 c.Assert(err, jc.ErrorIsNil) 4497 c.Assert(hostPorts, gc.DeepEquals, [][]network.HostPort{hostPorts1}) 4498 } 4499 4500 func (s *StateSuite) TestWatchAPIHostPorts(c *gc.C) { 4501 w := s.State.WatchAPIHostPorts() 4502 defer statetesting.AssertStop(c, w) 4503 4504 // Initial event. 4505 wc := statetesting.NewNotifyWatcherC(c, s.State, w) 4506 wc.AssertOneChange() 4507 4508 err := s.State.SetAPIHostPorts([][]network.HostPort{ 4509 network.NewHostPorts(99, "0.1.2.3"), 4510 }) 4511 c.Assert(err, jc.ErrorIsNil) 4512 4513 wc.AssertOneChange() 4514 4515 // Stop, check closed. 4516 statetesting.AssertStop(c, w) 4517 wc.AssertClosed() 4518 } 4519 4520 func (s *StateSuite) TestWatchMachineAddresses(c *gc.C) { 4521 // Add a machine: reported. 4522 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 4523 c.Assert(err, jc.ErrorIsNil) 4524 4525 w := machine.WatchAddresses() 4526 defer w.Stop() 4527 wc := statetesting.NewNotifyWatcherC(c, s.State, w) 4528 wc.AssertOneChange() 4529 4530 // Change the machine: not reported. 4531 err = machine.SetProvisioned(instance.Id("i-blah"), "fake-nonce", nil) 4532 c.Assert(err, jc.ErrorIsNil) 4533 wc.AssertNoChange() 4534 4535 // Set machine addresses: reported. 4536 err = machine.SetMachineAddresses(network.NewAddress("abc")) 4537 c.Assert(err, jc.ErrorIsNil) 4538 wc.AssertOneChange() 4539 4540 // Set provider addresses eclipsing machine addresses: reported. 4541 err = machine.SetProviderAddresses(network.NewScopedAddress("abc", network.ScopePublic)) 4542 c.Assert(err, jc.ErrorIsNil) 4543 wc.AssertOneChange() 4544 4545 // Set same machine eclipsed by provider addresses: not reported. 4546 err = machine.SetMachineAddresses(network.NewScopedAddress("abc", network.ScopeCloudLocal)) 4547 c.Assert(err, jc.ErrorIsNil) 4548 wc.AssertNoChange() 4549 4550 // Set different machine addresses: reported. 4551 err = machine.SetMachineAddresses(network.NewAddress("def")) 4552 c.Assert(err, jc.ErrorIsNil) 4553 wc.AssertOneChange() 4554 4555 // Set different provider addresses: reported. 4556 err = machine.SetMachineAddresses(network.NewScopedAddress("def", network.ScopePublic)) 4557 c.Assert(err, jc.ErrorIsNil) 4558 wc.AssertOneChange() 4559 4560 // Make it Dying: not reported. 4561 err = machine.Destroy() 4562 c.Assert(err, jc.ErrorIsNil) 4563 wc.AssertNoChange() 4564 4565 // Make it Dead: not reported. 4566 err = machine.EnsureDead() 4567 c.Assert(err, jc.ErrorIsNil) 4568 wc.AssertNoChange() 4569 4570 // Remove it: watcher eventually closed and Err 4571 // returns an IsNotFound error. 4572 err = machine.Remove() 4573 c.Assert(err, jc.ErrorIsNil) 4574 s.State.StartSync() 4575 select { 4576 case _, ok := <-w.Changes(): 4577 c.Assert(ok, jc.IsFalse) 4578 case <-time.After(testing.LongWait): 4579 c.Fatalf("watcher not closed") 4580 } 4581 c.Assert(w.Err(), jc.Satisfies, errors.IsNotFound) 4582 } 4583 4584 func (s *StateSuite) TestNowToTheSecond(c *gc.C) { 4585 t := state.NowToTheSecond() 4586 rounded := t.Round(time.Second) 4587 c.Assert(t, gc.DeepEquals, rounded) 4588 } 4589 4590 func (s *StateSuite) TestUnitsForInvalidId(c *gc.C) { 4591 // Check that an error is returned if an invalid machine id is provided. 4592 // Success cases are tested as part of TestMachinePrincipalUnits in the 4593 // MachineSuite. 4594 units, err := s.State.UnitsFor("invalid-id") 4595 c.Assert(units, gc.IsNil) 4596 c.Assert(err, gc.ErrorMatches, `"invalid-id" is not a valid machine id`) 4597 } 4598 4599 func (s *StateSuite) TestSetOrGetMongoSpaceNameSets(c *gc.C) { 4600 info, err := s.State.ControllerInfo() 4601 c.Assert(err, jc.ErrorIsNil) 4602 c.Assert(info.MongoSpaceName, gc.Equals, "") 4603 c.Assert(info.MongoSpaceState, gc.Equals, state.MongoSpaceUnknown) 4604 4605 spaceName := network.SpaceName("foo") 4606 4607 name, err := s.State.SetOrGetMongoSpaceName(spaceName) 4608 c.Assert(err, jc.ErrorIsNil) 4609 c.Assert(name, gc.Equals, spaceName) 4610 4611 info, err = s.State.ControllerInfo() 4612 c.Assert(err, jc.ErrorIsNil) 4613 c.Assert(info.MongoSpaceName, gc.Equals, string(spaceName)) 4614 c.Assert(info.MongoSpaceState, gc.Equals, state.MongoSpaceValid) 4615 } 4616 4617 func (s *StateSuite) TestSetOrGetMongoSpaceNameDoesNotReplaceValidSpace(c *gc.C) { 4618 spaceName := network.SpaceName("foo") 4619 name, err := s.State.SetOrGetMongoSpaceName(spaceName) 4620 c.Assert(err, jc.ErrorIsNil) 4621 c.Assert(name, gc.Equals, spaceName) 4622 4623 name, err = s.State.SetOrGetMongoSpaceName(network.SpaceName("bar")) 4624 c.Assert(err, jc.ErrorIsNil) 4625 c.Assert(name, gc.Equals, spaceName) 4626 4627 info, err := s.State.ControllerInfo() 4628 c.Assert(err, jc.ErrorIsNil) 4629 c.Assert(info.MongoSpaceName, gc.Equals, string(spaceName)) 4630 c.Assert(info.MongoSpaceState, gc.Equals, state.MongoSpaceValid) 4631 } 4632 4633 func (s *StateSuite) TestSetMongoSpaceStateSetsValidStates(c *gc.C) { 4634 mongoStates := []state.MongoSpaceStates{ 4635 state.MongoSpaceUnknown, 4636 state.MongoSpaceValid, 4637 state.MongoSpaceInvalid, 4638 state.MongoSpaceUnsupported, 4639 } 4640 for _, st := range mongoStates { 4641 err := s.State.SetMongoSpaceState(st) 4642 c.Assert(err, jc.ErrorIsNil) 4643 info, err := s.State.ControllerInfo() 4644 c.Assert(err, jc.ErrorIsNil) 4645 c.Assert(info.MongoSpaceState, gc.Equals, st) 4646 } 4647 } 4648 4649 func (s *StateSuite) TestSetMongoSpaceStateErrorOnInvalidStates(c *gc.C) { 4650 err := s.State.SetMongoSpaceState(state.MongoSpaceStates("bad")) 4651 c.Assert(err, gc.ErrorMatches, "mongoSpaceState: bad not valid") 4652 info, err := s.State.ControllerInfo() 4653 c.Assert(err, jc.ErrorIsNil) 4654 c.Assert(info.MongoSpaceState, gc.Equals, state.MongoSpaceUnknown) 4655 } 4656 4657 type SetAdminMongoPasswordSuite struct { 4658 testing.BaseSuite 4659 } 4660 4661 var _ = gc.Suite(&SetAdminMongoPasswordSuite{}) 4662 4663 func (s *SetAdminMongoPasswordSuite) TestSetAdminMongoPassword(c *gc.C) { 4664 inst := &gitjujutesting.MgoInstance{EnableAuth: true} 4665 err := inst.Start(testing.Certs) 4666 c.Assert(err, jc.ErrorIsNil) 4667 defer inst.DestroyWithLog() 4668 4669 owner := names.NewLocalUserTag("initialize-admin") 4670 mongoInfo := &mongo.MongoInfo{ 4671 Info: mongo.Info{ 4672 Addrs: []string{inst.Addr()}, 4673 CACert: testing.CACert, 4674 }, 4675 } 4676 cfg := testing.ModelConfig(c) 4677 st, err := state.Initialize(owner, mongoInfo, cfg, mongotest.DialOpts(), nil) 4678 c.Assert(err, jc.ErrorIsNil) 4679 defer st.Close() 4680 4681 // Check that we can SetAdminMongoPassword to nothing when there's 4682 // no password currently set. 4683 err = st.SetAdminMongoPassword("") 4684 c.Assert(err, jc.ErrorIsNil) 4685 4686 err = st.SetAdminMongoPassword("foo") 4687 c.Assert(err, jc.ErrorIsNil) 4688 err = st.MongoSession().DB("admin").Login("admin", "foo") 4689 c.Assert(err, jc.ErrorIsNil) 4690 4691 err = tryOpenState(st.ModelTag(), mongoInfo) 4692 c.Check(errors.Cause(err), jc.Satisfies, errors.IsUnauthorized) 4693 // note: collections are set up in arbitrary order, proximate cause of 4694 // failure may differ. 4695 c.Check(err, gc.ErrorMatches, `[^:]+: unauthorized mongo access: .*`) 4696 4697 mongoInfo.Password = "foo" 4698 err = tryOpenState(st.ModelTag(), mongoInfo) 4699 c.Assert(err, jc.ErrorIsNil) 4700 4701 err = st.SetAdminMongoPassword("") 4702 c.Assert(err, jc.ErrorIsNil) 4703 4704 mongoInfo.Password = "" 4705 err = tryOpenState(st.ModelTag(), mongoInfo) 4706 c.Assert(err, jc.ErrorIsNil) 4707 }