github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/apiserver/client/client_test.go (about) 1 // Copyright 2012-2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package client_test 5 6 import ( 7 "fmt" 8 "sort" 9 "strconv" 10 "strings" 11 "time" 12 13 "github.com/juju/errors" 14 "github.com/juju/loggo" 15 jc "github.com/juju/testing/checkers" 16 "github.com/juju/utils/series" 17 "github.com/juju/version" 18 gc "gopkg.in/check.v1" 19 "gopkg.in/juju/charm.v6-unstable" 20 "gopkg.in/juju/names.v2" 21 22 "github.com/juju/juju/agent" 23 "github.com/juju/juju/apiserver/client" 24 "github.com/juju/juju/apiserver/common" 25 "github.com/juju/juju/apiserver/modelconfig" 26 "github.com/juju/juju/apiserver/params" 27 "github.com/juju/juju/apiserver/testing" 28 "github.com/juju/juju/constraints" 29 "github.com/juju/juju/environs" 30 "github.com/juju/juju/environs/config" 31 "github.com/juju/juju/environs/manual" 32 toolstesting "github.com/juju/juju/environs/tools/testing" 33 "github.com/juju/juju/instance" 34 "github.com/juju/juju/network" 35 "github.com/juju/juju/permission" 36 "github.com/juju/juju/rpc" 37 "github.com/juju/juju/state" 38 "github.com/juju/juju/state/multiwatcher" 39 "github.com/juju/juju/state/presence" 40 "github.com/juju/juju/state/stateenvirons" 41 "github.com/juju/juju/status" 42 coretesting "github.com/juju/juju/testing" 43 "github.com/juju/juju/testing/factory" 44 jujuversion "github.com/juju/juju/version" 45 "github.com/juju/juju/worker" 46 ) 47 48 type serverSuite struct { 49 baseSuite 50 client *client.Client 51 newEnviron func() (environs.Environ, error) 52 } 53 54 var _ = gc.Suite(&serverSuite{}) 55 56 func (s *serverSuite) SetUpTest(c *gc.C) { 57 s.ConfigAttrs = map[string]interface{}{ 58 "authorized-keys": coretesting.FakeAuthKeys, 59 } 60 s.baseSuite.SetUpTest(c) 61 62 var err error 63 auth := testing.FakeAuthorizer{ 64 Tag: s.AdminUserTag(c), 65 EnvironManager: true, 66 } 67 urlGetter := common.NewToolsURLGetter(s.State.ModelUUID(), s.State) 68 configGetter := stateenvirons.EnvironConfigGetter{s.State} 69 statusSetter := common.NewStatusSetter(s.State, common.AuthAlways()) 70 toolsFinder := common.NewToolsFinder(configGetter, s.State, urlGetter) 71 s.newEnviron = func() (environs.Environ, error) { 72 return environs.GetEnviron(configGetter, environs.New) 73 } 74 newEnviron := func() (environs.Environ, error) { 75 return s.newEnviron() 76 } 77 blockChecker := common.NewBlockChecker(s.State) 78 modelConfigAPI, err := modelconfig.NewModelConfigAPI(s.State, auth) 79 c.Assert(err, jc.ErrorIsNil) 80 s.client, err = client.NewClient( 81 client.NewStateBackend(s.State), 82 modelConfigAPI, 83 common.NewResources(), 84 auth, 85 statusSetter, 86 toolsFinder, 87 newEnviron, 88 blockChecker, 89 ) 90 c.Assert(err, jc.ErrorIsNil) 91 } 92 93 func (s *serverSuite) setAgentPresence(c *gc.C, machineId string) *presence.Pinger { 94 m, err := s.State.Machine(machineId) 95 c.Assert(err, jc.ErrorIsNil) 96 pinger, err := m.SetAgentPresence() 97 c.Assert(err, jc.ErrorIsNil) 98 s.AddCleanup(func(c *gc.C) { 99 c.Assert(worker.Stop(pinger), jc.ErrorIsNil) 100 }) 101 s.State.StartSync() 102 err = m.WaitAgentPresence(coretesting.LongWait) 103 c.Assert(err, jc.ErrorIsNil) 104 return pinger 105 } 106 107 func (s *serverSuite) TestModelInfo(c *gc.C) { 108 model, err := s.State.Model() 109 c.Assert(err, jc.ErrorIsNil) 110 conf, _ := s.State.ModelConfig() 111 info, err := s.client.ModelInfo() 112 c.Assert(err, jc.ErrorIsNil) 113 c.Assert(info.DefaultSeries, gc.Equals, config.PreferredSeries(conf)) 114 c.Assert(info.CloudRegion, gc.Equals, model.CloudRegion()) 115 c.Assert(info.ProviderType, gc.Equals, conf.Type()) 116 c.Assert(info.Name, gc.Equals, conf.Name()) 117 c.Assert(info.UUID, gc.Equals, model.UUID()) 118 c.Assert(info.OwnerTag, gc.Equals, model.Owner().String()) 119 c.Assert(info.Life, gc.Equals, params.Alive) 120 // The controller UUID is not returned by the ModelInfo endpoint on the 121 // Client facade. 122 c.Assert(info.ControllerUUID, gc.Equals, "") 123 } 124 125 func (s *serverSuite) TestModelUsersInfo(c *gc.C) { 126 testAdmin := s.AdminUserTag(c) 127 owner, err := s.State.UserAccess(testAdmin, s.State.ModelTag()) 128 c.Assert(err, jc.ErrorIsNil) 129 130 localUser1 := s.makeLocalModelUser(c, "ralphdoe", "Ralph Doe") 131 localUser2 := s.makeLocalModelUser(c, "samsmith", "Sam Smith") 132 remoteUser1 := s.Factory.MakeModelUser(c, &factory.ModelUserParams{User: "bobjohns@ubuntuone", DisplayName: "Bob Johns", Access: permission.WriteAccess}) 133 remoteUser2 := s.Factory.MakeModelUser(c, &factory.ModelUserParams{User: "nicshaw@idprovider", DisplayName: "Nic Shaw", Access: permission.WriteAccess}) 134 135 results, err := s.client.ModelUserInfo() 136 c.Assert(err, jc.ErrorIsNil) 137 var expected params.ModelUserInfoResults 138 for _, r := range []struct { 139 user permission.UserAccess 140 info *params.ModelUserInfo 141 }{ 142 { 143 owner, 144 ¶ms.ModelUserInfo{ 145 UserName: owner.UserName, 146 DisplayName: owner.DisplayName, 147 Access: "admin", 148 }, 149 }, { 150 localUser1, 151 ¶ms.ModelUserInfo{ 152 UserName: "ralphdoe@local", 153 DisplayName: "Ralph Doe", 154 Access: "admin", 155 }, 156 }, { 157 localUser2, 158 ¶ms.ModelUserInfo{ 159 UserName: "samsmith@local", 160 DisplayName: "Sam Smith", 161 Access: "admin", 162 }, 163 }, { 164 remoteUser1, 165 ¶ms.ModelUserInfo{ 166 UserName: "bobjohns@ubuntuone", 167 DisplayName: "Bob Johns", 168 Access: "write", 169 }, 170 }, { 171 remoteUser2, 172 ¶ms.ModelUserInfo{ 173 UserName: "nicshaw@idprovider", 174 DisplayName: "Nic Shaw", 175 Access: "write", 176 }, 177 }, 178 } { 179 r.info.LastConnection = lastConnPointer(c, r.user, s.State) 180 expected.Results = append(expected.Results, params.ModelUserInfoResult{Result: r.info}) 181 } 182 183 sort.Sort(ByUserName(expected.Results)) 184 sort.Sort(ByUserName(results.Results)) 185 c.Assert(results, jc.DeepEquals, expected) 186 } 187 188 func lastConnPointer(c *gc.C, modelUser permission.UserAccess, st *state.State) *time.Time { 189 lastConn, err := st.LastModelConnection(modelUser.UserTag) 190 if err != nil { 191 if state.IsNeverConnectedError(err) { 192 return nil 193 } 194 c.Fatal(err) 195 } 196 return &lastConn 197 } 198 199 // ByUserName implements sort.Interface for []params.ModelUserInfoResult based on 200 // the UserName field. 201 type ByUserName []params.ModelUserInfoResult 202 203 func (a ByUserName) Len() int { return len(a) } 204 func (a ByUserName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 205 func (a ByUserName) Less(i, j int) bool { return a[i].Result.UserName < a[j].Result.UserName } 206 207 func (s *serverSuite) makeLocalModelUser(c *gc.C, username, displayname string) permission.UserAccess { 208 // factory.MakeUser will create an ModelUser for a local user by defalut 209 user := s.Factory.MakeUser(c, &factory.UserParams{Name: username, DisplayName: displayname}) 210 modelUser, err := s.State.UserAccess(user.UserTag(), s.State.ModelTag()) 211 c.Assert(err, jc.ErrorIsNil) 212 return modelUser 213 } 214 215 func (s *serverSuite) TestSetEnvironAgentVersion(c *gc.C) { 216 args := params.SetModelAgentVersion{ 217 Version: version.MustParse("9.8.7"), 218 } 219 err := s.client.SetModelAgentVersion(args) 220 c.Assert(err, jc.ErrorIsNil) 221 222 modelConfig, err := s.State.ModelConfig() 223 c.Assert(err, jc.ErrorIsNil) 224 agentVersion, found := modelConfig.AllAttrs()["agent-version"] 225 c.Assert(found, jc.IsTrue) 226 c.Assert(agentVersion, gc.Equals, "9.8.7") 227 } 228 229 type mockEnviron struct { 230 environs.Environ 231 allInstancesCalled bool 232 err error 233 } 234 235 func (m *mockEnviron) AllInstances() ([]instance.Instance, error) { 236 m.allInstancesCalled = true 237 return nil, m.err 238 } 239 240 func (s *serverSuite) assertCheckProviderAPI(c *gc.C, envError error, expectErr string) { 241 env := &mockEnviron{err: envError} 242 s.newEnviron = func() (environs.Environ, error) { 243 return env, nil 244 } 245 args := params.SetModelAgentVersion{ 246 Version: version.MustParse("9.8.7"), 247 } 248 err := s.client.SetModelAgentVersion(args) 249 c.Assert(env.allInstancesCalled, jc.IsTrue) 250 if expectErr != "" { 251 c.Assert(err, gc.ErrorMatches, expectErr) 252 } else { 253 c.Assert(err, jc.ErrorIsNil) 254 } 255 } 256 257 func (s *serverSuite) TestCheckProviderAPISuccess(c *gc.C) { 258 s.assertCheckProviderAPI(c, nil, "") 259 s.assertCheckProviderAPI(c, environs.ErrPartialInstances, "") 260 s.assertCheckProviderAPI(c, environs.ErrNoInstances, "") 261 } 262 263 func (s *serverSuite) TestCheckProviderAPIFail(c *gc.C) { 264 s.assertCheckProviderAPI(c, errors.New("instances error"), "cannot make API call to provider: instances error") 265 } 266 267 func (s *serverSuite) assertSetEnvironAgentVersion(c *gc.C) { 268 args := params.SetModelAgentVersion{ 269 Version: version.MustParse("9.8.7"), 270 } 271 err := s.client.SetModelAgentVersion(args) 272 c.Assert(err, jc.ErrorIsNil) 273 modelConfig, err := s.State.ModelConfig() 274 c.Assert(err, jc.ErrorIsNil) 275 agentVersion, found := modelConfig.AllAttrs()["agent-version"] 276 c.Assert(found, jc.IsTrue) 277 c.Assert(agentVersion, gc.Equals, "9.8.7") 278 } 279 280 func (s *serverSuite) assertSetEnvironAgentVersionBlocked(c *gc.C, msg string) { 281 args := params.SetModelAgentVersion{ 282 Version: version.MustParse("9.8.7"), 283 } 284 err := s.client.SetModelAgentVersion(args) 285 s.AssertBlocked(c, err, msg) 286 } 287 288 func (s *serverSuite) TestBlockDestroySetEnvironAgentVersion(c *gc.C) { 289 s.BlockDestroyModel(c, "TestBlockDestroySetEnvironAgentVersion") 290 s.assertSetEnvironAgentVersion(c) 291 } 292 293 func (s *serverSuite) TestBlockRemoveSetEnvironAgentVersion(c *gc.C) { 294 s.BlockRemoveObject(c, "TestBlockRemoveSetEnvironAgentVersion") 295 s.assertSetEnvironAgentVersion(c) 296 } 297 298 func (s *serverSuite) TestBlockChangesSetEnvironAgentVersion(c *gc.C) { 299 s.BlockAllChanges(c, "TestBlockChangesSetEnvironAgentVersion") 300 s.assertSetEnvironAgentVersionBlocked(c, "TestBlockChangesSetEnvironAgentVersion") 301 } 302 303 func (s *serverSuite) TestAbortCurrentUpgrade(c *gc.C) { 304 // Create a provisioned controller. 305 machine, err := s.State.AddMachine("series", state.JobManageModel) 306 c.Assert(err, jc.ErrorIsNil) 307 err = machine.SetProvisioned(instance.Id("i-blah"), "fake-nonce", nil) 308 c.Assert(err, jc.ErrorIsNil) 309 310 // Start an upgrade. 311 _, err = s.State.EnsureUpgradeInfo( 312 machine.Id(), 313 version.MustParse("1.2.3"), 314 version.MustParse("9.8.7"), 315 ) 316 c.Assert(err, jc.ErrorIsNil) 317 isUpgrading, err := s.State.IsUpgrading() 318 c.Assert(err, jc.ErrorIsNil) 319 c.Assert(isUpgrading, jc.IsTrue) 320 321 // Abort it. 322 err = s.client.AbortCurrentUpgrade() 323 c.Assert(err, jc.ErrorIsNil) 324 325 isUpgrading, err = s.State.IsUpgrading() 326 c.Assert(err, jc.ErrorIsNil) 327 c.Assert(isUpgrading, jc.IsFalse) 328 } 329 330 func (s *serverSuite) assertAbortCurrentUpgradeBlocked(c *gc.C, msg string) { 331 err := s.client.AbortCurrentUpgrade() 332 s.AssertBlocked(c, err, msg) 333 } 334 335 func (s *serverSuite) assertAbortCurrentUpgrade(c *gc.C) { 336 err := s.client.AbortCurrentUpgrade() 337 c.Assert(err, jc.ErrorIsNil) 338 isUpgrading, err := s.State.IsUpgrading() 339 c.Assert(err, jc.ErrorIsNil) 340 c.Assert(isUpgrading, jc.IsFalse) 341 } 342 343 func (s *serverSuite) setupAbortCurrentUpgradeBlocked(c *gc.C) { 344 // Create a provisioned controller. 345 machine, err := s.State.AddMachine("series", state.JobManageModel) 346 c.Assert(err, jc.ErrorIsNil) 347 err = machine.SetProvisioned(instance.Id("i-blah"), "fake-nonce", nil) 348 c.Assert(err, jc.ErrorIsNil) 349 350 // Start an upgrade. 351 _, err = s.State.EnsureUpgradeInfo( 352 machine.Id(), 353 version.MustParse("1.2.3"), 354 version.MustParse("9.8.7"), 355 ) 356 c.Assert(err, jc.ErrorIsNil) 357 isUpgrading, err := s.State.IsUpgrading() 358 c.Assert(err, jc.ErrorIsNil) 359 c.Assert(isUpgrading, jc.IsTrue) 360 } 361 362 func (s *serverSuite) TestBlockDestroyAbortCurrentUpgrade(c *gc.C) { 363 s.setupAbortCurrentUpgradeBlocked(c) 364 s.BlockDestroyModel(c, "TestBlockDestroyAbortCurrentUpgrade") 365 s.assertAbortCurrentUpgrade(c) 366 } 367 368 func (s *serverSuite) TestBlockRemoveAbortCurrentUpgrade(c *gc.C) { 369 s.setupAbortCurrentUpgradeBlocked(c) 370 s.BlockRemoveObject(c, "TestBlockRemoveAbortCurrentUpgrade") 371 s.assertAbortCurrentUpgrade(c) 372 } 373 374 func (s *serverSuite) TestBlockChangesAbortCurrentUpgrade(c *gc.C) { 375 s.setupAbortCurrentUpgradeBlocked(c) 376 s.BlockAllChanges(c, "TestBlockChangesAbortCurrentUpgrade") 377 s.assertAbortCurrentUpgradeBlocked(c, "TestBlockChangesAbortCurrentUpgrade") 378 } 379 380 type clientSuite struct { 381 baseSuite 382 } 383 384 var _ = gc.Suite(&clientSuite{}) 385 386 // clearSinceTimes zeros out the updated timestamps inside status 387 // so we can easily check the results. 388 func clearSinceTimes(status *params.FullStatus) { 389 for applicationId, service := range status.Applications { 390 for unitId, unit := range service.Units { 391 unit.WorkloadStatus.Since = nil 392 unit.AgentStatus.Since = nil 393 for id, subord := range unit.Subordinates { 394 subord.WorkloadStatus.Since = nil 395 subord.AgentStatus.Since = nil 396 unit.Subordinates[id] = subord 397 } 398 service.Units[unitId] = unit 399 } 400 service.Status.Since = nil 401 status.Applications[applicationId] = service 402 } 403 for id, machine := range status.Machines { 404 machine.AgentStatus.Since = nil 405 machine.InstanceStatus.Since = nil 406 status.Machines[id] = machine 407 } 408 } 409 410 func (s *clientSuite) TestClientStatus(c *gc.C) { 411 s.setUpScenario(c) 412 status, err := s.APIState.Client().Status(nil) 413 clearSinceTimes(status) 414 c.Assert(err, jc.ErrorIsNil) 415 c.Assert(status, jc.DeepEquals, scenarioStatus) 416 } 417 418 func assertLife(c *gc.C, entity state.Living, life state.Life) { 419 err := entity.Refresh() 420 c.Assert(err, jc.ErrorIsNil) 421 c.Assert(entity.Life(), gc.Equals, life) 422 } 423 424 func assertRemoved(c *gc.C, entity state.Living) { 425 err := entity.Refresh() 426 c.Assert(err, jc.Satisfies, errors.IsNotFound) 427 } 428 429 func (s *clientSuite) setupDestroyMachinesTest(c *gc.C) (*state.Machine, *state.Machine, *state.Machine, *state.Unit) { 430 m0, err := s.State.AddMachine("quantal", state.JobManageModel) 431 c.Assert(err, jc.ErrorIsNil) 432 m1, err := s.State.AddMachine("quantal", state.JobHostUnits) 433 c.Assert(err, jc.ErrorIsNil) 434 m2, err := s.State.AddMachine("quantal", state.JobHostUnits) 435 c.Assert(err, jc.ErrorIsNil) 436 437 sch := s.AddTestingCharm(c, "wordpress") 438 wordpress := s.AddTestingService(c, "wordpress", sch) 439 u, err := wordpress.AddUnit() 440 c.Assert(err, jc.ErrorIsNil) 441 err = u.AssignToMachine(m1) 442 c.Assert(err, jc.ErrorIsNil) 443 444 return m0, m1, m2, u 445 } 446 447 func (s *clientSuite) TestDestroyMachines(c *gc.C) { 448 m0, m1, m2, u := s.setupDestroyMachinesTest(c) 449 s.assertDestroyMachineSuccess(c, u, m0, m1, m2) 450 } 451 452 func (s *clientSuite) TestForceDestroyMachines(c *gc.C) { 453 s.assertForceDestroyMachines(c) 454 } 455 456 func (s *clientSuite) testClientUnitResolved(c *gc.C, noretry bool, expectedResolvedMode state.ResolvedMode) { 457 // Setup: 458 s.setUpScenario(c) 459 u, err := s.State.Unit("wordpress/0") 460 c.Assert(err, jc.ErrorIsNil) 461 now := time.Now() 462 sInfo := status.StatusInfo{ 463 Status: status.Error, 464 Message: "gaaah", 465 Since: &now, 466 } 467 err = u.SetAgentStatus(sInfo) 468 c.Assert(err, jc.ErrorIsNil) 469 // Code under test: 470 err = s.APIState.Client().Resolved("wordpress/0", noretry) 471 c.Assert(err, jc.ErrorIsNil) 472 // Freshen the unit's state. 473 err = u.Refresh() 474 c.Assert(err, jc.ErrorIsNil) 475 // And now the actual test assertions: we set the unit as resolved via 476 // the API so it should have a resolved mode set. 477 mode := u.Resolved() 478 c.Assert(mode, gc.Equals, expectedResolvedMode) 479 } 480 481 func (s *clientSuite) TestClientUnitResolved(c *gc.C) { 482 s.testClientUnitResolved(c, true, state.ResolvedNoHooks) 483 } 484 485 func (s *clientSuite) TestClientUnitResolvedRetry(c *gc.C) { 486 s.testClientUnitResolved(c, false, state.ResolvedRetryHooks) 487 } 488 489 func (s *clientSuite) setupResolved(c *gc.C) *state.Unit { 490 s.setUpScenario(c) 491 u, err := s.State.Unit("wordpress/0") 492 c.Assert(err, jc.ErrorIsNil) 493 now := time.Now() 494 sInfo := status.StatusInfo{ 495 Status: status.Error, 496 Message: "gaaah", 497 Since: &now, 498 } 499 err = u.SetAgentStatus(sInfo) 500 c.Assert(err, jc.ErrorIsNil) 501 return u 502 } 503 504 func (s *clientSuite) assertResolved(c *gc.C, u *state.Unit) { 505 err := s.APIState.Client().Resolved("wordpress/0", false) 506 c.Assert(err, jc.ErrorIsNil) 507 // Freshen the unit's state. 508 err = u.Refresh() 509 c.Assert(err, jc.ErrorIsNil) 510 // And now the actual test assertions: we set the unit as resolved via 511 // the API so it should have a resolved mode set. 512 mode := u.Resolved() 513 c.Assert(mode, gc.Equals, state.ResolvedRetryHooks) 514 } 515 516 func (s *clientSuite) assertResolvedBlocked(c *gc.C, u *state.Unit, msg string) { 517 err := s.APIState.Client().Resolved("wordpress/0", false) 518 s.AssertBlocked(c, err, msg) 519 } 520 521 func (s *clientSuite) TestBlockDestroyUnitResolved(c *gc.C) { 522 u := s.setupResolved(c) 523 s.BlockDestroyModel(c, "TestBlockDestroyUnitResolved") 524 s.assertResolved(c, u) 525 } 526 527 func (s *clientSuite) TestBlockRemoveUnitResolved(c *gc.C) { 528 u := s.setupResolved(c) 529 s.BlockRemoveObject(c, "TestBlockRemoveUnitResolved") 530 s.assertResolved(c, u) 531 } 532 533 func (s *clientSuite) TestBlockChangeUnitResolved(c *gc.C) { 534 u := s.setupResolved(c) 535 s.BlockAllChanges(c, "TestBlockChangeUnitResolved") 536 s.assertResolvedBlocked(c, u, "TestBlockChangeUnitResolved") 537 } 538 539 type clientRepoSuite struct { 540 baseSuite 541 testing.CharmStoreSuite 542 } 543 544 var _ = gc.Suite(&clientRepoSuite{}) 545 546 func (s *clientRepoSuite) SetUpSuite(c *gc.C) { 547 s.CharmStoreSuite.SetUpSuite(c) 548 s.baseSuite.SetUpSuite(c) 549 550 } 551 552 func (s *clientRepoSuite) TearDownSuite(c *gc.C) { 553 s.CharmStoreSuite.TearDownSuite(c) 554 s.baseSuite.TearDownSuite(c) 555 } 556 557 func (s *clientRepoSuite) SetUpTest(c *gc.C) { 558 s.baseSuite.SetUpTest(c) 559 s.CharmStoreSuite.Session = s.baseSuite.Session 560 s.CharmStoreSuite.SetUpTest(c) 561 562 c.Assert(s.APIState, gc.NotNil) 563 } 564 565 func (s *clientRepoSuite) TearDownTest(c *gc.C) { 566 s.CharmStoreSuite.TearDownTest(c) 567 s.baseSuite.TearDownTest(c) 568 } 569 570 func (s *clientSuite) TestClientWatchAll(c *gc.C) { 571 loggo.GetLogger("juju.apiserver").SetLogLevel(loggo.TRACE) 572 // A very simple end-to-end test, because 573 // all the logic is tested elsewhere. 574 m, err := s.State.AddMachine("quantal", state.JobManageModel) 575 c.Assert(err, jc.ErrorIsNil) 576 err = m.SetProvisioned("i-0", agent.BootstrapNonce, nil) 577 c.Assert(err, jc.ErrorIsNil) 578 watcher, err := s.APIState.Client().WatchAll() 579 c.Assert(err, jc.ErrorIsNil) 580 defer func() { 581 err := watcher.Stop() 582 c.Assert(err, jc.ErrorIsNil) 583 }() 584 deltas, err := watcher.Next() 585 c.Assert(err, jc.ErrorIsNil) 586 c.Assert(len(deltas), gc.Equals, 1) 587 d0, ok := deltas[0].Entity.(*multiwatcher.MachineInfo) 588 c.Assert(ok, jc.IsTrue) 589 d0.AgentStatus.Since = nil 590 d0.InstanceStatus.Since = nil 591 if !c.Check(deltas, jc.DeepEquals, []multiwatcher.Delta{{ 592 Entity: &multiwatcher.MachineInfo{ 593 ModelUUID: s.State.ModelUUID(), 594 Id: m.Id(), 595 InstanceId: "i-0", 596 AgentStatus: multiwatcher.StatusInfo{ 597 Current: status.Pending, 598 }, 599 InstanceStatus: multiwatcher.StatusInfo{ 600 Current: status.Pending, 601 }, 602 Life: multiwatcher.Life("alive"), 603 Series: "quantal", 604 Jobs: []multiwatcher.MachineJob{state.JobManageModel.ToParams()}, 605 Addresses: []multiwatcher.Address{}, 606 HardwareCharacteristics: &instance.HardwareCharacteristics{}, 607 HasVote: false, 608 WantsVote: true, 609 }, 610 }}) { 611 c.Logf("got:") 612 for _, d := range deltas { 613 c.Logf("%#v\n", d.Entity) 614 } 615 } 616 } 617 618 func (s *clientSuite) TestClientSetModelConstraints(c *gc.C) { 619 // Set constraints for the model. 620 cons, err := constraints.Parse("mem=4096", "cores=2") 621 c.Assert(err, jc.ErrorIsNil) 622 err = s.APIState.Client().SetModelConstraints(cons) 623 c.Assert(err, jc.ErrorIsNil) 624 625 // Ensure the constraints have been correctly updated. 626 obtained, err := s.State.ModelConstraints() 627 c.Assert(err, jc.ErrorIsNil) 628 c.Assert(obtained, gc.DeepEquals, cons) 629 } 630 631 func (s *clientSuite) assertSetModelConstraints(c *gc.C) { 632 // Set constraints for the model. 633 cons, err := constraints.Parse("mem=4096", "cores=2") 634 c.Assert(err, jc.ErrorIsNil) 635 err = s.APIState.Client().SetModelConstraints(cons) 636 c.Assert(err, jc.ErrorIsNil) 637 // Ensure the constraints have been correctly updated. 638 obtained, err := s.State.ModelConstraints() 639 c.Assert(err, jc.ErrorIsNil) 640 c.Assert(obtained, gc.DeepEquals, cons) 641 } 642 643 func (s *clientSuite) assertSetModelConstraintsBlocked(c *gc.C, msg string) { 644 // Set constraints for the model. 645 cons, err := constraints.Parse("mem=4096", "cores=2") 646 c.Assert(err, jc.ErrorIsNil) 647 err = s.APIState.Client().SetModelConstraints(cons) 648 s.AssertBlocked(c, err, msg) 649 } 650 651 func (s *clientSuite) TestBlockDestroyClientSetModelConstraints(c *gc.C) { 652 s.BlockDestroyModel(c, "TestBlockDestroyClientSetModelConstraints") 653 s.assertSetModelConstraints(c) 654 } 655 656 func (s *clientSuite) TestBlockRemoveClientSetModelConstraints(c *gc.C) { 657 s.BlockRemoveObject(c, "TestBlockRemoveClientSetModelConstraints") 658 s.assertSetModelConstraints(c) 659 } 660 661 func (s *clientSuite) TestBlockChangesClientSetModelConstraints(c *gc.C) { 662 s.BlockAllChanges(c, "TestBlockChangesClientSetModelConstraints") 663 s.assertSetModelConstraintsBlocked(c, "TestBlockChangesClientSetModelConstraints") 664 } 665 666 func (s *clientSuite) TestClientGetModelConstraints(c *gc.C) { 667 // Set constraints for the model. 668 cons, err := constraints.Parse("mem=4096", "cores=2") 669 c.Assert(err, jc.ErrorIsNil) 670 err = s.State.SetModelConstraints(cons) 671 c.Assert(err, jc.ErrorIsNil) 672 673 // Check we can get the constraints. 674 obtained, err := s.APIState.Client().GetModelConstraints() 675 c.Assert(err, jc.ErrorIsNil) 676 c.Assert(obtained, gc.DeepEquals, cons) 677 } 678 679 func (s *clientSuite) TestClientPublicAddressErrors(c *gc.C) { 680 s.setUpScenario(c) 681 _, err := s.APIState.Client().PublicAddress("wordpress") 682 c.Assert(err, gc.ErrorMatches, `unknown unit or machine "wordpress"`) 683 _, err = s.APIState.Client().PublicAddress("0") 684 c.Assert(err, gc.ErrorMatches, `error fetching address for machine "0": no public address`) 685 _, err = s.APIState.Client().PublicAddress("wordpress/0") 686 c.Assert(err, gc.ErrorMatches, `error fetching address for unit "wordpress/0": no public address`) 687 } 688 689 func (s *clientSuite) TestClientPublicAddressMachine(c *gc.C) { 690 s.setUpScenario(c) 691 692 // Internally, network.SelectPublicAddress is used; the "most public" 693 // address is returned. 694 m1, err := s.State.Machine("1") 695 c.Assert(err, jc.ErrorIsNil) 696 cloudLocalAddress := network.NewScopedAddress("cloudlocal", network.ScopeCloudLocal) 697 publicAddress := network.NewScopedAddress("public", network.ScopePublic) 698 err = m1.SetProviderAddresses(cloudLocalAddress) 699 c.Assert(err, jc.ErrorIsNil) 700 addr, err := s.APIState.Client().PublicAddress("1") 701 c.Assert(err, jc.ErrorIsNil) 702 c.Assert(addr, gc.Equals, "cloudlocal") 703 err = m1.SetProviderAddresses(cloudLocalAddress, publicAddress) 704 addr, err = s.APIState.Client().PublicAddress("1") 705 c.Assert(err, jc.ErrorIsNil) 706 c.Assert(addr, gc.Equals, "public") 707 } 708 709 func (s *clientSuite) TestClientPublicAddressUnit(c *gc.C) { 710 s.setUpScenario(c) 711 712 m1, err := s.State.Machine("1") 713 publicAddress := network.NewScopedAddress("public", network.ScopePublic) 714 err = m1.SetProviderAddresses(publicAddress) 715 c.Assert(err, jc.ErrorIsNil) 716 addr, err := s.APIState.Client().PublicAddress("wordpress/0") 717 c.Assert(err, jc.ErrorIsNil) 718 c.Assert(addr, gc.Equals, "public") 719 } 720 721 func (s *clientSuite) TestClientPrivateAddressErrors(c *gc.C) { 722 s.setUpScenario(c) 723 _, err := s.APIState.Client().PrivateAddress("wordpress") 724 c.Assert(err, gc.ErrorMatches, `unknown unit or machine "wordpress"`) 725 _, err = s.APIState.Client().PrivateAddress("0") 726 c.Assert(err, gc.ErrorMatches, `error fetching address for machine "0": no private address`) 727 _, err = s.APIState.Client().PrivateAddress("wordpress/0") 728 c.Assert(err, gc.ErrorMatches, `error fetching address for unit "wordpress/0": no private address`) 729 } 730 731 func (s *clientSuite) TestClientPrivateAddress(c *gc.C) { 732 s.setUpScenario(c) 733 734 // Internally, network.SelectInternalAddress is used; the public 735 // address if no cloud-local one is available. 736 m1, err := s.State.Machine("1") 737 c.Assert(err, jc.ErrorIsNil) 738 cloudLocalAddress := network.NewScopedAddress("cloudlocal", network.ScopeCloudLocal) 739 publicAddress := network.NewScopedAddress("public", network.ScopePublic) 740 err = m1.SetProviderAddresses(publicAddress) 741 c.Assert(err, jc.ErrorIsNil) 742 addr, err := s.APIState.Client().PrivateAddress("1") 743 c.Assert(err, jc.ErrorIsNil) 744 c.Assert(addr, gc.Equals, "public") 745 err = m1.SetProviderAddresses(cloudLocalAddress, publicAddress) 746 addr, err = s.APIState.Client().PrivateAddress("1") 747 c.Assert(err, jc.ErrorIsNil) 748 c.Assert(addr, gc.Equals, "cloudlocal") 749 } 750 751 func (s *clientSuite) TestClientPrivateAddressUnit(c *gc.C) { 752 s.setUpScenario(c) 753 754 m1, err := s.State.Machine("1") 755 privateAddress := network.NewScopedAddress("private", network.ScopeCloudLocal) 756 err = m1.SetProviderAddresses(privateAddress) 757 c.Assert(err, jc.ErrorIsNil) 758 addr, err := s.APIState.Client().PrivateAddress("wordpress/0") 759 c.Assert(err, jc.ErrorIsNil) 760 c.Assert(addr, gc.Equals, "private") 761 } 762 763 func (s *clientSuite) TestClientFindTools(c *gc.C) { 764 result, err := s.APIState.Client().FindTools(99, -1, "", "") 765 c.Assert(err, jc.ErrorIsNil) 766 c.Assert(result.Error, jc.Satisfies, params.IsCodeNotFound) 767 toolstesting.UploadToStorage(c, s.DefaultToolsStorage, "released", version.MustParseBinary("2.99.0-precise-amd64")) 768 result, err = s.APIState.Client().FindTools(2, 99, "precise", "amd64") 769 c.Assert(err, jc.ErrorIsNil) 770 c.Assert(result.Error, gc.IsNil) 771 c.Assert(result.List, gc.HasLen, 1) 772 c.Assert(result.List[0].Version, gc.Equals, version.MustParseBinary("2.99.0-precise-amd64")) 773 url := fmt.Sprintf("https://%s/model/%s/tools/%s", 774 s.APIState.Addr(), coretesting.ModelTag.Id(), result.List[0].Version) 775 c.Assert(result.List[0].URL, gc.Equals, url) 776 } 777 778 func (s *clientSuite) checkMachine(c *gc.C, id, series, cons string) { 779 // Ensure the machine was actually created. 780 machine, err := s.BackingState.Machine(id) 781 c.Assert(err, jc.ErrorIsNil) 782 c.Assert(machine.Series(), gc.Equals, series) 783 c.Assert(machine.Jobs(), gc.DeepEquals, []state.MachineJob{state.JobHostUnits}) 784 machineConstraints, err := machine.Constraints() 785 c.Assert(err, jc.ErrorIsNil) 786 c.Assert(machineConstraints.String(), gc.Equals, cons) 787 } 788 789 func (s *clientSuite) TestClientAddMachinesDefaultSeries(c *gc.C) { 790 apiParams := make([]params.AddMachineParams, 3) 791 for i := 0; i < 3; i++ { 792 apiParams[i] = params.AddMachineParams{ 793 Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits}, 794 } 795 } 796 machines, err := s.APIState.Client().AddMachines(apiParams) 797 c.Assert(err, jc.ErrorIsNil) 798 c.Assert(len(machines), gc.Equals, 3) 799 for i, machineResult := range machines { 800 c.Assert(machineResult.Machine, gc.DeepEquals, strconv.Itoa(i)) 801 s.checkMachine(c, machineResult.Machine, series.LatestLts(), apiParams[i].Constraints.String()) 802 } 803 } 804 805 func (s *clientSuite) assertAddMachines(c *gc.C) { 806 apiParams := make([]params.AddMachineParams, 3) 807 for i := 0; i < 3; i++ { 808 apiParams[i] = params.AddMachineParams{ 809 Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits}, 810 } 811 } 812 machines, err := s.APIState.Client().AddMachines(apiParams) 813 c.Assert(err, jc.ErrorIsNil) 814 c.Assert(len(machines), gc.Equals, 3) 815 for i, machineResult := range machines { 816 c.Assert(machineResult.Machine, gc.DeepEquals, strconv.Itoa(i)) 817 s.checkMachine(c, machineResult.Machine, series.LatestLts(), apiParams[i].Constraints.String()) 818 } 819 } 820 821 func (s *clientSuite) assertAddMachinesBlocked(c *gc.C, msg string) { 822 apiParams := make([]params.AddMachineParams, 3) 823 for i := 0; i < 3; i++ { 824 apiParams[i] = params.AddMachineParams{ 825 Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits}, 826 } 827 } 828 _, err := s.APIState.Client().AddMachines(apiParams) 829 s.AssertBlocked(c, err, msg) 830 } 831 832 func (s *clientSuite) TestBlockDestroyClientAddMachinesDefaultSeries(c *gc.C) { 833 s.BlockDestroyModel(c, "TestBlockDestroyClientAddMachinesDefaultSeries") 834 s.assertAddMachines(c) 835 } 836 837 func (s *clientSuite) TestBlockRemoveClientAddMachinesDefaultSeries(c *gc.C) { 838 s.BlockRemoveObject(c, "TestBlockRemoveClientAddMachinesDefaultSeries") 839 s.assertAddMachines(c) 840 } 841 842 func (s *clientSuite) TestBlockChangesClientAddMachines(c *gc.C) { 843 s.BlockAllChanges(c, "TestBlockChangesClientAddMachines") 844 s.assertAddMachinesBlocked(c, "TestBlockChangesClientAddMachines") 845 } 846 847 func (s *clientSuite) TestClientAddMachinesWithSeries(c *gc.C) { 848 apiParams := make([]params.AddMachineParams, 3) 849 for i := 0; i < 3; i++ { 850 apiParams[i] = params.AddMachineParams{ 851 Series: "quantal", 852 Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits}, 853 } 854 } 855 machines, err := s.APIState.Client().AddMachines(apiParams) 856 c.Assert(err, jc.ErrorIsNil) 857 c.Assert(len(machines), gc.Equals, 3) 858 for i, machineResult := range machines { 859 c.Assert(machineResult.Machine, gc.DeepEquals, strconv.Itoa(i)) 860 s.checkMachine(c, machineResult.Machine, "quantal", apiParams[i].Constraints.String()) 861 } 862 } 863 864 func (s *clientSuite) TestClientAddMachineInsideMachine(c *gc.C) { 865 _, err := s.State.AddMachine("quantal", state.JobHostUnits) 866 c.Assert(err, jc.ErrorIsNil) 867 868 machines, err := s.APIState.Client().AddMachines([]params.AddMachineParams{{ 869 Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits}, 870 ContainerType: instance.LXD, 871 ParentId: "0", 872 Series: "quantal", 873 }}) 874 c.Assert(err, jc.ErrorIsNil) 875 c.Assert(machines, gc.HasLen, 1) 876 c.Assert(machines[0].Machine, gc.Equals, "0/lxd/0") 877 } 878 879 // updateConfig sets config variable with given key to a given value 880 // Asserts that no errors were encountered. 881 func (s *baseSuite) updateConfig(c *gc.C, key string, block bool) { 882 err := s.State.UpdateModelConfig(map[string]interface{}{key: block}, nil, nil) 883 c.Assert(err, jc.ErrorIsNil) 884 } 885 886 func (s *clientSuite) TestClientAddMachinesWithConstraints(c *gc.C) { 887 apiParams := make([]params.AddMachineParams, 3) 888 for i := 0; i < 3; i++ { 889 apiParams[i] = params.AddMachineParams{ 890 Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits}, 891 } 892 } 893 // The last machine has some constraints. 894 apiParams[2].Constraints = constraints.MustParse("mem=4G") 895 machines, err := s.APIState.Client().AddMachines(apiParams) 896 c.Assert(err, jc.ErrorIsNil) 897 c.Assert(len(machines), gc.Equals, 3) 898 for i, machineResult := range machines { 899 c.Assert(machineResult.Machine, gc.DeepEquals, strconv.Itoa(i)) 900 s.checkMachine(c, machineResult.Machine, series.LatestLts(), apiParams[i].Constraints.String()) 901 } 902 } 903 904 func (s *clientSuite) TestClientAddMachinesWithPlacement(c *gc.C) { 905 apiParams := make([]params.AddMachineParams, 4) 906 for i := range apiParams { 907 apiParams[i] = params.AddMachineParams{ 908 Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits}, 909 } 910 } 911 apiParams[0].Placement = instance.MustParsePlacement("lxd") 912 apiParams[1].Placement = instance.MustParsePlacement("lxd:0") 913 apiParams[1].ContainerType = instance.LXD 914 apiParams[2].Placement = instance.MustParsePlacement("controller:invalid") 915 apiParams[3].Placement = instance.MustParsePlacement("controller:valid") 916 machines, err := s.APIState.Client().AddMachines(apiParams) 917 c.Assert(err, jc.ErrorIsNil) 918 c.Assert(len(machines), gc.Equals, 4) 919 c.Assert(machines[0].Machine, gc.Equals, "0/lxd/0") 920 c.Assert(machines[1].Error, gc.ErrorMatches, "container type and placement are mutually exclusive") 921 c.Assert(machines[2].Error, gc.ErrorMatches, "cannot add a new machine: invalid placement is invalid") 922 c.Assert(machines[3].Machine, gc.Equals, "1") 923 924 m, err := s.BackingState.Machine(machines[3].Machine) 925 c.Assert(err, jc.ErrorIsNil) 926 c.Assert(m.Placement(), gc.DeepEquals, apiParams[3].Placement.Directive) 927 } 928 929 func (s *clientSuite) TestClientAddMachinesSomeErrors(c *gc.C) { 930 // Here we check that adding a number of containers correctly handles the 931 // case that some adds succeed and others fail and report the errors 932 // accordingly. 933 // We will set up params to the AddMachines API to attempt to create 3 machines. 934 // Machines 0 and 1 will be added successfully. 935 // Remaining machines will fail due to different reasons. 936 937 // Create a machine to host the requested containers. 938 host, err := s.State.AddMachine("quantal", state.JobHostUnits) 939 c.Assert(err, jc.ErrorIsNil) 940 // The host only supports lxd containers. 941 err = host.SetSupportedContainers([]instance.ContainerType{instance.LXD}) 942 c.Assert(err, jc.ErrorIsNil) 943 944 // Set up params for adding 3 containers. 945 apiParams := make([]params.AddMachineParams, 3) 946 for i := range apiParams { 947 apiParams[i] = params.AddMachineParams{ 948 Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits}, 949 } 950 } 951 // This will cause a add-machine to fail due to an unsupported container. 952 apiParams[2].ContainerType = instance.KVM 953 apiParams[2].ParentId = host.Id() 954 machines, err := s.APIState.Client().AddMachines(apiParams) 955 c.Assert(err, jc.ErrorIsNil) 956 c.Assert(len(machines), gc.Equals, 3) 957 958 // Check the results - machines 2 and 3 will have errors. 959 c.Check(machines[0].Machine, gc.Equals, "1") 960 c.Check(machines[0].Error, gc.IsNil) 961 c.Check(machines[1].Machine, gc.Equals, "2") 962 c.Check(machines[1].Error, gc.IsNil) 963 c.Check(machines[2].Error, gc.ErrorMatches, "cannot add a new machine: machine 0 cannot host kvm containers") 964 } 965 966 func (s *clientSuite) TestClientAddMachinesWithInstanceIdSomeErrors(c *gc.C) { 967 apiParams := make([]params.AddMachineParams, 3) 968 addrs := network.NewAddresses("1.2.3.4") 969 hc := instance.MustParseHardware("mem=4G") 970 for i := 0; i < 3; i++ { 971 apiParams[i] = params.AddMachineParams{ 972 Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits}, 973 InstanceId: instance.Id(fmt.Sprintf("1234-%d", i)), 974 Nonce: "foo", 975 HardwareCharacteristics: hc, 976 Addrs: params.FromNetworkAddresses(addrs...), 977 } 978 } 979 // This will cause the last add-machine to fail. 980 apiParams[2].Nonce = "" 981 machines, err := s.APIState.Client().AddMachines(apiParams) 982 c.Assert(err, jc.ErrorIsNil) 983 c.Assert(len(machines), gc.Equals, 3) 984 for i, machineResult := range machines { 985 if i == 2 { 986 c.Assert(machineResult.Error, gc.NotNil) 987 c.Assert(machineResult.Error, gc.ErrorMatches, "cannot add a new machine: cannot add a machine with an instance id and no nonce") 988 } else { 989 c.Assert(machineResult.Machine, gc.DeepEquals, strconv.Itoa(i)) 990 s.checkMachine(c, machineResult.Machine, series.LatestLts(), apiParams[i].Constraints.String()) 991 instanceId := fmt.Sprintf("1234-%d", i) 992 s.checkInstance(c, machineResult.Machine, instanceId, "foo", hc, addrs) 993 } 994 } 995 } 996 997 func (s *clientSuite) checkInstance(c *gc.C, id, instanceId, nonce string, 998 hc instance.HardwareCharacteristics, addr []network.Address) { 999 1000 machine, err := s.BackingState.Machine(id) 1001 c.Assert(err, jc.ErrorIsNil) 1002 machineInstanceId, err := machine.InstanceId() 1003 c.Assert(err, jc.ErrorIsNil) 1004 c.Assert(machine.CheckProvisioned(nonce), jc.IsTrue) 1005 c.Assert(machineInstanceId, gc.Equals, instance.Id(instanceId)) 1006 machineHardware, err := machine.HardwareCharacteristics() 1007 c.Assert(err, jc.ErrorIsNil) 1008 c.Assert(machineHardware.String(), gc.Equals, hc.String()) 1009 c.Assert(machine.Addresses(), gc.DeepEquals, addr) 1010 } 1011 1012 func (s *clientSuite) TestInjectMachinesStillExists(c *gc.C) { 1013 results := new(params.AddMachinesResults) 1014 // We need to use Call directly because the client interface 1015 // no longer refers to InjectMachine. 1016 args := params.AddMachines{ 1017 MachineParams: []params.AddMachineParams{{ 1018 Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits}, 1019 InstanceId: "i-foo", 1020 Nonce: "nonce", 1021 }}, 1022 } 1023 err := s.APIState.APICall("Client", 1, "", "AddMachines", args, &results) 1024 c.Assert(err, jc.ErrorIsNil) 1025 c.Assert(results.Machines, gc.HasLen, 1) 1026 } 1027 1028 func (s *clientSuite) TestProvisioningScript(c *gc.C) { 1029 // Inject a machine and then call the ProvisioningScript API. 1030 // The result should be the same as when calling MachineConfig, 1031 // converting it to a cloudinit.MachineConfig, and disabling 1032 // apt_upgrade. 1033 apiParams := params.AddMachineParams{ 1034 Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits}, 1035 InstanceId: instance.Id("1234"), 1036 Nonce: "foo", 1037 HardwareCharacteristics: instance.MustParseHardware("arch=amd64"), 1038 } 1039 machines, err := s.APIState.Client().AddMachines([]params.AddMachineParams{apiParams}) 1040 c.Assert(err, jc.ErrorIsNil) 1041 c.Assert(len(machines), gc.Equals, 1) 1042 machineId := machines[0].Machine 1043 // Call ProvisioningScript. Normally ProvisioningScript and 1044 // MachineConfig are mutually exclusive; both of them will 1045 // allocate a api password for the machine agent. 1046 script, err := s.APIState.Client().ProvisioningScript(params.ProvisioningScriptParams{ 1047 MachineId: machineId, 1048 Nonce: apiParams.Nonce, 1049 }) 1050 c.Assert(err, jc.ErrorIsNil) 1051 icfg, err := client.InstanceConfig(s.State, machineId, apiParams.Nonce, "") 1052 c.Assert(err, jc.ErrorIsNil) 1053 provisioningScript, err := manual.ProvisioningScript(icfg) 1054 c.Assert(err, jc.ErrorIsNil) 1055 // ProvisioningScript internally calls MachineConfig, 1056 // which allocates a new, random password. Everything 1057 // about the scripts should be the same other than 1058 // the line containing "oldpassword" from agent.conf. 1059 scriptLines := strings.Split(script, "\n") 1060 provisioningScriptLines := strings.Split(provisioningScript, "\n") 1061 c.Assert(scriptLines, gc.HasLen, len(provisioningScriptLines)) 1062 for i, line := range scriptLines { 1063 if strings.Contains(line, "oldpassword") { 1064 continue 1065 } 1066 c.Assert(line, gc.Equals, provisioningScriptLines[i]) 1067 } 1068 } 1069 1070 func (s *clientSuite) TestProvisioningScriptDisablePackageCommands(c *gc.C) { 1071 apiParams := params.AddMachineParams{ 1072 Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits}, 1073 InstanceId: instance.Id("1234"), 1074 Nonce: "foo", 1075 HardwareCharacteristics: instance.MustParseHardware("arch=amd64"), 1076 } 1077 machines, err := s.APIState.Client().AddMachines([]params.AddMachineParams{apiParams}) 1078 c.Assert(err, jc.ErrorIsNil) 1079 c.Assert(len(machines), gc.Equals, 1) 1080 machineId := machines[0].Machine 1081 1082 provParams := params.ProvisioningScriptParams{ 1083 MachineId: machineId, 1084 Nonce: apiParams.Nonce, 1085 } 1086 1087 setUpdateBehavior := func(update, upgrade bool) { 1088 s.State.UpdateModelConfig( 1089 map[string]interface{}{ 1090 "enable-os-upgrade": upgrade, 1091 "enable-os-refresh-update": update, 1092 }, 1093 nil, 1094 nil, 1095 ) 1096 } 1097 1098 // Test enabling package commands 1099 provParams.DisablePackageCommands = false 1100 setUpdateBehavior(true, true) 1101 script, err := s.APIState.Client().ProvisioningScript(provParams) 1102 c.Assert(err, jc.ErrorIsNil) 1103 c.Check(script, jc.Contains, "apt-get update") 1104 c.Check(script, jc.Contains, "apt-get upgrade") 1105 1106 // Test disabling package commands 1107 provParams.DisablePackageCommands = true 1108 setUpdateBehavior(false, false) 1109 script, err = s.APIState.Client().ProvisioningScript(provParams) 1110 c.Assert(err, jc.ErrorIsNil) 1111 c.Check(script, gc.Not(jc.Contains), "apt-get update") 1112 c.Check(script, gc.Not(jc.Contains), "apt-get upgrade") 1113 1114 // Test client-specified DisablePackageCommands trumps environment 1115 // config variables. 1116 provParams.DisablePackageCommands = true 1117 setUpdateBehavior(true, true) 1118 script, err = s.APIState.Client().ProvisioningScript(provParams) 1119 c.Assert(err, jc.ErrorIsNil) 1120 c.Check(script, gc.Not(jc.Contains), "apt-get update") 1121 c.Check(script, gc.Not(jc.Contains), "apt-get upgrade") 1122 1123 // Test that in the abasence of a client-specified 1124 // DisablePackageCommands we use what's set in environment config. 1125 provParams.DisablePackageCommands = false 1126 setUpdateBehavior(false, false) 1127 //provParams.UpdateBehavior = ¶ms.UpdateBehavior{false, false} 1128 script, err = s.APIState.Client().ProvisioningScript(provParams) 1129 c.Assert(err, jc.ErrorIsNil) 1130 c.Check(script, gc.Not(jc.Contains), "apt-get update") 1131 c.Check(script, gc.Not(jc.Contains), "apt-get upgrade") 1132 } 1133 1134 var resolveCharmTests = []struct { 1135 about string 1136 url string 1137 resolved string 1138 parseErr string 1139 resolveErr string 1140 }{{ 1141 about: "wordpress resolved", 1142 url: "cs:wordpress", 1143 resolved: "cs:trusty/wordpress", 1144 }, { 1145 about: "mysql resolved", 1146 url: "cs:mysql", 1147 resolved: "cs:precise/mysql", 1148 }, { 1149 about: "riak resolved", 1150 url: "cs:riak", 1151 resolved: "cs:trusty/riak", 1152 }, { 1153 about: "fully qualified char reference", 1154 url: "cs:utopic/riak-5", 1155 resolved: "cs:utopic/riak-5", 1156 }, { 1157 about: "charm with series and no revision", 1158 url: "cs:precise/wordpress", 1159 resolved: "cs:precise/wordpress", 1160 }, { 1161 about: "fully qualified reference not found", 1162 url: "cs:utopic/riak-42", 1163 resolveErr: `cannot resolve URL "cs:utopic/riak-42": charm not found`, 1164 }, { 1165 about: "reference not found", 1166 url: "cs:no-such", 1167 resolveErr: `cannot resolve URL "cs:no-such": charm or bundle not found`, 1168 }, { 1169 about: "invalid charm name", 1170 url: "cs:", 1171 parseErr: `URL has invalid charm or bundle name: "cs:"`, 1172 }, { 1173 about: "local charm", 1174 url: "local:wordpress", 1175 resolveErr: `only charm store charm references are supported, with cs: schema`, 1176 }} 1177 1178 func (s *clientRepoSuite) TestResolveCharm(c *gc.C) { 1179 // Add some charms to be resolved later. 1180 for _, url := range []string{ 1181 "precise/wordpress-1", 1182 "trusty/wordpress-2", 1183 "precise/mysql-3", 1184 "trusty/riak-4", 1185 "utopic/riak-5", 1186 } { 1187 s.UploadCharm(c, url, "wordpress") 1188 } 1189 1190 // Run the tests. 1191 for i, test := range resolveCharmTests { 1192 c.Logf("test %d: %s", i, test.about) 1193 1194 client := s.APIState.Client() 1195 ref, err := charm.ParseURL(test.url) 1196 if test.parseErr == "" { 1197 if !c.Check(err, jc.ErrorIsNil) { 1198 continue 1199 } 1200 } else { 1201 c.Assert(err, gc.NotNil) 1202 c.Check(err, gc.ErrorMatches, test.parseErr) 1203 continue 1204 } 1205 1206 curl, err := client.ResolveCharm(ref) 1207 if test.resolveErr == "" { 1208 c.Assert(err, jc.ErrorIsNil) 1209 c.Check(curl.String(), gc.Equals, test.resolved) 1210 continue 1211 } 1212 c.Check(err, gc.ErrorMatches, test.resolveErr) 1213 c.Check(curl, gc.IsNil) 1214 } 1215 } 1216 1217 func (s *clientSuite) TestRetryProvisioning(c *gc.C) { 1218 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 1219 c.Assert(err, jc.ErrorIsNil) 1220 now := time.Now() 1221 sInfo := status.StatusInfo{ 1222 Status: status.Error, 1223 Message: "error", 1224 Since: &now, 1225 } 1226 err = machine.SetStatus(sInfo) 1227 c.Assert(err, jc.ErrorIsNil) 1228 _, err = s.APIState.Client().RetryProvisioning(machine.Tag().(names.MachineTag)) 1229 c.Assert(err, jc.ErrorIsNil) 1230 1231 statusInfo, err := machine.Status() 1232 c.Assert(err, jc.ErrorIsNil) 1233 c.Assert(statusInfo.Status, gc.Equals, status.Error) 1234 c.Assert(statusInfo.Message, gc.Equals, "error") 1235 c.Assert(statusInfo.Data["transient"], jc.IsTrue) 1236 } 1237 1238 func (s *clientSuite) setupRetryProvisioning(c *gc.C) *state.Machine { 1239 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 1240 c.Assert(err, jc.ErrorIsNil) 1241 now := time.Now() 1242 sInfo := status.StatusInfo{ 1243 Status: status.Error, 1244 Message: "error", 1245 Since: &now, 1246 } 1247 err = machine.SetStatus(sInfo) 1248 c.Assert(err, jc.ErrorIsNil) 1249 return machine 1250 } 1251 1252 func (s *clientSuite) assertRetryProvisioning(c *gc.C, machine *state.Machine) { 1253 _, err := s.APIState.Client().RetryProvisioning(machine.Tag().(names.MachineTag)) 1254 c.Assert(err, jc.ErrorIsNil) 1255 statusInfo, err := machine.Status() 1256 c.Assert(err, jc.ErrorIsNil) 1257 c.Assert(statusInfo.Status, gc.Equals, status.Error) 1258 c.Assert(statusInfo.Message, gc.Equals, "error") 1259 c.Assert(statusInfo.Data["transient"], jc.IsTrue) 1260 } 1261 1262 func (s *clientSuite) assertRetryProvisioningBlocked(c *gc.C, machine *state.Machine, msg string) { 1263 _, err := s.APIState.Client().RetryProvisioning(machine.Tag().(names.MachineTag)) 1264 s.AssertBlocked(c, err, msg) 1265 } 1266 1267 func (s *clientSuite) TestBlockDestroyRetryProvisioning(c *gc.C) { 1268 m := s.setupRetryProvisioning(c) 1269 s.BlockDestroyModel(c, "TestBlockDestroyRetryProvisioning") 1270 s.assertRetryProvisioning(c, m) 1271 } 1272 1273 func (s *clientSuite) TestBlockRemoveRetryProvisioning(c *gc.C) { 1274 m := s.setupRetryProvisioning(c) 1275 s.BlockRemoveObject(c, "TestBlockRemoveRetryProvisioning") 1276 s.assertRetryProvisioning(c, m) 1277 } 1278 1279 func (s *clientSuite) TestBlockChangesRetryProvisioning(c *gc.C) { 1280 m := s.setupRetryProvisioning(c) 1281 s.BlockAllChanges(c, "TestBlockChangesRetryProvisioning") 1282 s.assertRetryProvisioningBlocked(c, m, "TestBlockChangesRetryProvisioning") 1283 } 1284 1285 func (s *clientSuite) TestAPIHostPorts(c *gc.C) { 1286 server1Addresses := []network.Address{{ 1287 Value: "server-1", 1288 Type: network.HostName, 1289 Scope: network.ScopePublic, 1290 }, { 1291 Value: "10.0.0.1", 1292 Type: network.IPv4Address, 1293 Scope: network.ScopeCloudLocal, 1294 }} 1295 server2Addresses := []network.Address{{ 1296 Value: "::1", 1297 Type: network.IPv6Address, 1298 Scope: network.ScopeMachineLocal, 1299 }} 1300 stateAPIHostPorts := [][]network.HostPort{ 1301 network.AddressesWithPort(server1Addresses, 123), 1302 network.AddressesWithPort(server2Addresses, 456), 1303 } 1304 1305 err := s.State.SetAPIHostPorts(stateAPIHostPorts) 1306 c.Assert(err, jc.ErrorIsNil) 1307 apiHostPorts, err := s.APIState.Client().APIHostPorts() 1308 c.Assert(err, jc.ErrorIsNil) 1309 c.Assert(apiHostPorts, gc.DeepEquals, stateAPIHostPorts) 1310 } 1311 1312 func (s *clientSuite) TestClientAgentVersion(c *gc.C) { 1313 current := version.MustParse("1.2.0") 1314 s.PatchValue(&jujuversion.Current, current) 1315 result, err := s.APIState.Client().AgentVersion() 1316 c.Assert(err, jc.ErrorIsNil) 1317 c.Assert(result, gc.Equals, current) 1318 } 1319 1320 func (s *clientSuite) assertDestroyMachineSuccess(c *gc.C, u *state.Unit, m0, m1, m2 *state.Machine) { 1321 err := s.APIState.Client().DestroyMachines("0", "1", "2") 1322 c.Assert(err, gc.ErrorMatches, `some machines were not destroyed: machine 0 is required by the model; machine 1 has unit "wordpress/0" assigned`) 1323 assertLife(c, m0, state.Alive) 1324 assertLife(c, m1, state.Alive) 1325 assertLife(c, m2, state.Dying) 1326 1327 err = u.UnassignFromMachine() 1328 c.Assert(err, jc.ErrorIsNil) 1329 err = s.APIState.Client().DestroyMachines("0", "1", "2") 1330 c.Assert(err, gc.ErrorMatches, `some machines were not destroyed: machine 0 is required by the model`) 1331 assertLife(c, m0, state.Alive) 1332 assertLife(c, m1, state.Dying) 1333 assertLife(c, m2, state.Dying) 1334 } 1335 1336 func (s *clientSuite) assertBlockedErrorAndLiveliness( 1337 c *gc.C, 1338 err error, 1339 msg string, 1340 living1 state.Living, 1341 living2 state.Living, 1342 living3 state.Living, 1343 living4 state.Living, 1344 ) { 1345 s.AssertBlocked(c, err, msg) 1346 assertLife(c, living1, state.Alive) 1347 assertLife(c, living2, state.Alive) 1348 assertLife(c, living3, state.Alive) 1349 assertLife(c, living4, state.Alive) 1350 } 1351 1352 func (s *clientSuite) AssertBlocked(c *gc.C, err error, msg string) { 1353 c.Assert(params.IsCodeOperationBlocked(err), jc.IsTrue, gc.Commentf("error: %#v", err)) 1354 c.Assert(errors.Cause(err), gc.DeepEquals, &rpc.RequestError{ 1355 Message: msg, 1356 Code: "operation is blocked", 1357 }) 1358 } 1359 1360 func (s *clientSuite) TestBlockRemoveDestroyMachines(c *gc.C) { 1361 m0, m1, m2, u := s.setupDestroyMachinesTest(c) 1362 s.BlockRemoveObject(c, "TestBlockRemoveDestroyMachines") 1363 err := s.APIState.Client().DestroyMachines("0", "1", "2") 1364 s.assertBlockedErrorAndLiveliness(c, err, "TestBlockRemoveDestroyMachines", m0, m1, m2, u) 1365 } 1366 1367 func (s *clientSuite) TestBlockChangesDestroyMachines(c *gc.C) { 1368 m0, m1, m2, u := s.setupDestroyMachinesTest(c) 1369 s.BlockAllChanges(c, "TestBlockChangesDestroyMachines") 1370 err := s.APIState.Client().DestroyMachines("0", "1", "2") 1371 s.assertBlockedErrorAndLiveliness(c, err, "TestBlockChangesDestroyMachines", m0, m1, m2, u) 1372 } 1373 1374 func (s *clientSuite) TestBlockDestoryDestroyMachines(c *gc.C) { 1375 m0, m1, m2, u := s.setupDestroyMachinesTest(c) 1376 s.BlockDestroyModel(c, "TestBlockDestoryDestroyMachines") 1377 s.assertDestroyMachineSuccess(c, u, m0, m1, m2) 1378 } 1379 1380 func (s *clientSuite) TestAnyBlockForceDestroyMachines(c *gc.C) { 1381 // force bypasses all blocks 1382 s.BlockAllChanges(c, "TestAnyBlockForceDestroyMachines") 1383 s.BlockDestroyModel(c, "TestAnyBlockForceDestroyMachines") 1384 s.BlockRemoveObject(c, "TestAnyBlockForceDestroyMachines") 1385 s.assertForceDestroyMachines(c) 1386 } 1387 1388 func (s *clientSuite) assertForceDestroyMachines(c *gc.C) { 1389 m0, m1, m2, u := s.setupDestroyMachinesTest(c) 1390 1391 err := s.APIState.Client().ForceDestroyMachines("0", "1", "2") 1392 c.Assert(err, gc.ErrorMatches, `some machines were not destroyed: machine is required by the model`) 1393 assertLife(c, m0, state.Alive) 1394 assertLife(c, m1, state.Alive) 1395 assertLife(c, m2, state.Alive) 1396 assertLife(c, u, state.Alive) 1397 1398 err = s.State.Cleanup() 1399 c.Assert(err, jc.ErrorIsNil) 1400 assertLife(c, m0, state.Alive) 1401 assertLife(c, m1, state.Dead) 1402 assertLife(c, m2, state.Dead) 1403 assertRemoved(c, u) 1404 }