github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/agent/provisioner/provisioner_test.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package provisioner_test 5 6 import ( 7 "fmt" 8 stdtesting "testing" 9 "time" 10 11 "github.com/golang/mock/gomock" 12 "github.com/juju/collections/set" 13 "github.com/juju/errors" 14 "github.com/juju/proxy" 15 jc "github.com/juju/testing/checkers" 16 gc "gopkg.in/check.v1" 17 "gopkg.in/juju/charm.v6" 18 "gopkg.in/juju/names.v2" 19 20 "github.com/juju/juju/apiserver/common" 21 commontesting "github.com/juju/juju/apiserver/common/testing" 22 "github.com/juju/juju/apiserver/facades/agent/provisioner" 23 "github.com/juju/juju/apiserver/facades/agent/provisioner/mocks" 24 "github.com/juju/juju/apiserver/params" 25 apiservertesting "github.com/juju/juju/apiserver/testing" 26 "github.com/juju/juju/container" 27 "github.com/juju/juju/core/constraints" 28 "github.com/juju/juju/core/instance" 29 "github.com/juju/juju/core/status" 30 "github.com/juju/juju/environs/config" 31 environtesting "github.com/juju/juju/environs/testing" 32 "github.com/juju/juju/juju/testing" 33 "github.com/juju/juju/network" 34 "github.com/juju/juju/network/containerizer" 35 "github.com/juju/juju/provider/dummy" 36 "github.com/juju/juju/state" 37 statetesting "github.com/juju/juju/state/testing" 38 "github.com/juju/juju/storage" 39 "github.com/juju/juju/storage/poolmanager" 40 "github.com/juju/juju/storage/provider" 41 coretesting "github.com/juju/juju/testing" 42 "github.com/juju/juju/testing/factory" 43 ) 44 45 func TestPackage(t *stdtesting.T) { 46 coretesting.MgoTestPackage(t) 47 } 48 49 type provisionerSuite struct { 50 testing.JujuConnSuite 51 52 machines []*state.Machine 53 54 authorizer apiservertesting.FakeAuthorizer 55 resources *common.Resources 56 provisioner *provisioner.ProvisionerAPIV6 57 } 58 59 var _ = gc.Suite(&provisionerSuite{}) 60 61 func (s *provisionerSuite) SetUpTest(c *gc.C) { 62 s.setUpTest(c, false) 63 } 64 65 func (s *provisionerSuite) setUpTest(c *gc.C, withController bool) { 66 if s.JujuConnSuite.ConfigAttrs == nil { 67 s.JujuConnSuite.ConfigAttrs = make(map[string]interface{}) 68 } 69 s.JujuConnSuite.ConfigAttrs["image-stream"] = "daily" 70 s.JujuConnSuite.SetUpTest(c) 71 72 // Reset previous machines (if any) and create 3 machines 73 // for the tests, plus an optional controller machine. 74 s.machines = nil 75 // Note that the specific machine ids allocated are assumed 76 // to be numerically consecutive from zero. 77 if withController { 78 s.machines = append(s.machines, testing.AddControllerMachine(c, s.State)) 79 } 80 for i := 0; i < 5; i++ { 81 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 82 c.Check(err, jc.ErrorIsNil) 83 s.machines = append(s.machines, machine) 84 } 85 86 // Create a FakeAuthorizer so we can check permissions, 87 // set up assuming we logged in as the controller. 88 s.authorizer = apiservertesting.FakeAuthorizer{ 89 Controller: true, 90 } 91 92 // Create the resource registry separately to track invocations to 93 // Register, and to register the root for tools URLs. 94 s.resources = common.NewResources() 95 96 // Create a provisioner API for the machine. 97 provisionerAPI, err := provisioner.NewProvisionerAPIV6( 98 s.State, 99 s.resources, 100 s.authorizer, 101 ) 102 c.Assert(err, jc.ErrorIsNil) 103 s.provisioner = provisionerAPI 104 } 105 106 type withoutControllerSuite struct { 107 provisionerSuite 108 *commontesting.ModelWatcherTest 109 } 110 111 var _ = gc.Suite(&withoutControllerSuite{}) 112 113 func (s *withoutControllerSuite) SetUpTest(c *gc.C) { 114 s.setUpTest(c, false) 115 s.ModelWatcherTest = commontesting.NewModelWatcherTest(s.provisioner, s.State, s.resources) 116 } 117 118 func (s *withoutControllerSuite) TestProvisionerFailsWithNonMachineAgentNonManagerUser(c *gc.C) { 119 anAuthorizer := s.authorizer 120 anAuthorizer.Controller = true 121 // Works with a controller, which is not a machine agent. 122 aProvisioner, err := provisioner.NewProvisionerAPI(s.State, s.resources, anAuthorizer) 123 c.Assert(err, jc.ErrorIsNil) 124 c.Assert(aProvisioner, gc.NotNil) 125 126 // But fails with neither a machine agent or a controller. 127 anAuthorizer.Controller = false 128 aProvisioner, err = provisioner.NewProvisionerAPI(s.State, s.resources, anAuthorizer) 129 c.Assert(err, gc.NotNil) 130 c.Assert(aProvisioner, gc.IsNil) 131 c.Assert(err, gc.ErrorMatches, "permission denied") 132 } 133 134 func (s *withoutControllerSuite) TestSetPasswords(c *gc.C) { 135 args := params.EntityPasswords{ 136 Changes: []params.EntityPassword{ 137 {Tag: s.machines[0].Tag().String(), Password: "xxx0-1234567890123457890"}, 138 {Tag: s.machines[1].Tag().String(), Password: "xxx1-1234567890123457890"}, 139 {Tag: s.machines[2].Tag().String(), Password: "xxx2-1234567890123457890"}, 140 {Tag: s.machines[3].Tag().String(), Password: "xxx3-1234567890123457890"}, 141 {Tag: s.machines[4].Tag().String(), Password: "xxx4-1234567890123457890"}, 142 {Tag: "machine-42", Password: "foo"}, 143 {Tag: "unit-foo-0", Password: "zzz"}, 144 {Tag: "application-bar", Password: "abc"}, 145 }, 146 } 147 results, err := s.provisioner.SetPasswords(args) 148 c.Assert(err, jc.ErrorIsNil) 149 c.Assert(results, gc.DeepEquals, params.ErrorResults{ 150 Results: []params.ErrorResult{ 151 {nil}, 152 {nil}, 153 {nil}, 154 {nil}, 155 {nil}, 156 {apiservertesting.NotFoundError("machine 42")}, 157 {apiservertesting.ErrUnauthorized}, 158 {apiservertesting.ErrUnauthorized}, 159 }, 160 }) 161 162 // Verify the changes to both machines succeeded. 163 for i, machine := range s.machines { 164 c.Logf("trying %q password", machine.Tag()) 165 err = machine.Refresh() 166 c.Assert(err, jc.ErrorIsNil) 167 changed := machine.PasswordValid(fmt.Sprintf("xxx%d-1234567890123457890", i)) 168 c.Assert(changed, jc.IsTrue) 169 } 170 } 171 172 func (s *withoutControllerSuite) TestShortSetPasswords(c *gc.C) { 173 args := params.EntityPasswords{ 174 Changes: []params.EntityPassword{ 175 {Tag: s.machines[1].Tag().String(), Password: "xxx1"}, 176 }, 177 } 178 results, err := s.provisioner.SetPasswords(args) 179 c.Assert(err, jc.ErrorIsNil) 180 c.Assert(results.Results, gc.HasLen, 1) 181 c.Assert(results.Results[0].Error, gc.ErrorMatches, 182 "password is only 4 bytes long, and is not a valid Agent password") 183 } 184 185 func (s *withoutControllerSuite) TestLifeAsMachineAgent(c *gc.C) { 186 // NOTE: This and the next call serve to test the two 187 // different authorization schemes: 188 // 1. Machine agents can access their own machine and 189 // any container that has their own machine as parent; 190 // 2. Controllers can access any machine without 191 // a parent. 192 // There's no need to repeat this test for each method, 193 // because the authorization logic is common. 194 195 // Login as a machine agent for machine 0. 196 anAuthorizer := s.authorizer 197 anAuthorizer.Controller = false 198 anAuthorizer.Tag = s.machines[0].Tag() 199 aProvisioner, err := provisioner.NewProvisionerAPI(s.State, s.resources, anAuthorizer) 200 c.Assert(err, jc.ErrorIsNil) 201 c.Assert(aProvisioner, gc.NotNil) 202 203 // Make the machine dead before trying to add containers. 204 err = s.machines[0].EnsureDead() 205 c.Assert(err, jc.ErrorIsNil) 206 207 // Create some containers to work on. 208 template := state.MachineTemplate{ 209 Series: "quantal", 210 Jobs: []state.MachineJob{state.JobHostUnits}, 211 } 212 var containers []*state.Machine 213 for i := 0; i < 3; i++ { 214 container, err := s.State.AddMachineInsideMachine(template, s.machines[0].Id(), instance.LXD) 215 c.Check(err, jc.ErrorIsNil) 216 containers = append(containers, container) 217 } 218 // Make one container dead. 219 err = containers[1].EnsureDead() 220 c.Assert(err, jc.ErrorIsNil) 221 222 args := params.Entities{Entities: []params.Entity{ 223 {Tag: s.machines[0].Tag().String()}, 224 {Tag: s.machines[1].Tag().String()}, 225 {Tag: containers[0].Tag().String()}, 226 {Tag: containers[1].Tag().String()}, 227 {Tag: containers[2].Tag().String()}, 228 {Tag: "machine-42"}, 229 {Tag: "unit-foo-0"}, 230 {Tag: "application-bar"}, 231 }} 232 result, err := aProvisioner.Life(args) 233 c.Assert(err, jc.ErrorIsNil) 234 c.Assert(result, gc.DeepEquals, params.LifeResults{ 235 Results: []params.LifeResult{ 236 {Life: "dead"}, 237 {Error: apiservertesting.ErrUnauthorized}, 238 {Life: "alive"}, 239 {Life: "dead"}, 240 {Life: "alive"}, 241 {Error: apiservertesting.ErrUnauthorized}, 242 {Error: apiservertesting.ErrUnauthorized}, 243 {Error: apiservertesting.ErrUnauthorized}, 244 }, 245 }) 246 } 247 248 func (s *withoutControllerSuite) TestLifeAsController(c *gc.C) { 249 err := s.machines[1].EnsureDead() 250 c.Assert(err, jc.ErrorIsNil) 251 err = s.machines[1].Refresh() 252 c.Assert(err, jc.ErrorIsNil) 253 c.Assert(s.machines[0].Life(), gc.Equals, state.Alive) 254 c.Assert(s.machines[1].Life(), gc.Equals, state.Dead) 255 c.Assert(s.machines[2].Life(), gc.Equals, state.Alive) 256 257 args := params.Entities{Entities: []params.Entity{ 258 {Tag: s.machines[0].Tag().String()}, 259 {Tag: s.machines[1].Tag().String()}, 260 {Tag: s.machines[2].Tag().String()}, 261 {Tag: "machine-42"}, 262 {Tag: "unit-foo-0"}, 263 {Tag: "application-bar"}, 264 }} 265 result, err := s.provisioner.Life(args) 266 c.Assert(err, jc.ErrorIsNil) 267 c.Assert(result, gc.DeepEquals, params.LifeResults{ 268 Results: []params.LifeResult{ 269 {Life: "alive"}, 270 {Life: "dead"}, 271 {Life: "alive"}, 272 {Error: apiservertesting.NotFoundError("machine 42")}, 273 {Error: apiservertesting.ErrUnauthorized}, 274 {Error: apiservertesting.ErrUnauthorized}, 275 }, 276 }) 277 278 // Remove the subordinate and make sure it's detected. 279 err = s.machines[1].Remove() 280 c.Assert(err, jc.ErrorIsNil) 281 err = s.machines[1].Refresh() 282 c.Assert(err, jc.Satisfies, errors.IsNotFound) 283 284 result, err = s.provisioner.Life(params.Entities{ 285 Entities: []params.Entity{ 286 {Tag: s.machines[1].Tag().String()}, 287 }, 288 }) 289 c.Assert(err, jc.ErrorIsNil) 290 c.Assert(result, gc.DeepEquals, params.LifeResults{ 291 Results: []params.LifeResult{ 292 {Error: apiservertesting.NotFoundError("machine 1")}, 293 }, 294 }) 295 } 296 297 func (s *withoutControllerSuite) TestRemove(c *gc.C) { 298 err := s.machines[1].EnsureDead() 299 c.Assert(err, jc.ErrorIsNil) 300 s.assertLife(c, 0, state.Alive) 301 s.assertLife(c, 1, state.Dead) 302 s.assertLife(c, 2, state.Alive) 303 304 args := params.Entities{Entities: []params.Entity{ 305 {Tag: s.machines[0].Tag().String()}, 306 {Tag: s.machines[1].Tag().String()}, 307 {Tag: s.machines[2].Tag().String()}, 308 {Tag: "machine-42"}, 309 {Tag: "unit-foo-0"}, 310 {Tag: "application-bar"}, 311 }} 312 result, err := s.provisioner.Remove(args) 313 c.Assert(err, jc.ErrorIsNil) 314 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 315 Results: []params.ErrorResult{ 316 {¶ms.Error{Message: `cannot remove entity "machine-0": still alive`}}, 317 {nil}, 318 {¶ms.Error{Message: `cannot remove entity "machine-2": still alive`}}, 319 {apiservertesting.NotFoundError("machine 42")}, 320 {apiservertesting.ErrUnauthorized}, 321 {apiservertesting.ErrUnauthorized}, 322 }, 323 }) 324 325 // Verify the changes. 326 s.assertLife(c, 0, state.Alive) 327 err = s.machines[2].Refresh() 328 c.Assert(err, jc.ErrorIsNil) 329 s.assertLife(c, 2, state.Alive) 330 } 331 332 func (s *withoutControllerSuite) TestSetStatus(c *gc.C) { 333 now := time.Now() 334 sInfo := status.StatusInfo{ 335 Status: status.Started, 336 Message: "blah", 337 Since: &now, 338 } 339 err := s.machines[0].SetStatus(sInfo) 340 c.Assert(err, jc.ErrorIsNil) 341 sInfo = status.StatusInfo{ 342 Status: status.Stopped, 343 Message: "foo", 344 Since: &now, 345 } 346 err = s.machines[1].SetStatus(sInfo) 347 c.Assert(err, jc.ErrorIsNil) 348 sInfo = status.StatusInfo{ 349 Status: status.Error, 350 Message: "not really", 351 Since: &now, 352 } 353 err = s.machines[2].SetStatus(sInfo) 354 c.Assert(err, jc.ErrorIsNil) 355 356 args := params.SetStatus{ 357 Entities: []params.EntityStatusArgs{ 358 {Tag: s.machines[0].Tag().String(), Status: status.Error.String(), Info: "not really", 359 Data: map[string]interface{}{"foo": "bar"}}, 360 {Tag: s.machines[1].Tag().String(), Status: status.Stopped.String(), Info: "foobar"}, 361 {Tag: s.machines[2].Tag().String(), Status: status.Started.String(), Info: "again"}, 362 {Tag: "machine-42", Status: status.Started.String(), Info: "blah"}, 363 {Tag: "unit-foo-0", Status: status.Stopped.String(), Info: "foobar"}, 364 {Tag: "application-bar", Status: status.Stopped.String(), Info: "foobar"}, 365 }} 366 result, err := s.provisioner.SetStatus(args) 367 c.Assert(err, jc.ErrorIsNil) 368 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 369 Results: []params.ErrorResult{ 370 {nil}, 371 {nil}, 372 {nil}, 373 {apiservertesting.NotFoundError("machine 42")}, 374 {apiservertesting.ErrUnauthorized}, 375 {apiservertesting.ErrUnauthorized}, 376 }, 377 }) 378 379 // Verify the changes. 380 s.assertStatus(c, 0, status.Error, "not really", map[string]interface{}{"foo": "bar"}) 381 s.assertStatus(c, 1, status.Stopped, "foobar", map[string]interface{}{}) 382 s.assertStatus(c, 2, status.Started, "again", map[string]interface{}{}) 383 } 384 385 func (s *withoutControllerSuite) TestSetInstanceStatus(c *gc.C) { 386 now := time.Now() 387 sInfo := status.StatusInfo{ 388 Status: status.Provisioning, 389 Message: "blah", 390 Since: &now, 391 } 392 err := s.machines[0].SetInstanceStatus(sInfo) 393 c.Assert(err, jc.ErrorIsNil) 394 sInfo = status.StatusInfo{ 395 Status: status.Running, 396 Message: "foo", 397 Since: &now, 398 } 399 err = s.machines[1].SetInstanceStatus(sInfo) 400 c.Assert(err, jc.ErrorIsNil) 401 sInfo = status.StatusInfo{ 402 Status: status.Error, 403 Message: "not really", 404 Since: &now, 405 } 406 err = s.machines[2].SetInstanceStatus(sInfo) 407 c.Assert(err, jc.ErrorIsNil) 408 409 args := params.SetStatus{ 410 Entities: []params.EntityStatusArgs{ 411 {Tag: s.machines[0].Tag().String(), Status: status.Provisioning.String(), Info: "not really", 412 Data: map[string]interface{}{"foo": "bar"}}, 413 {Tag: s.machines[1].Tag().String(), Status: status.Running.String(), Info: "foobar"}, 414 {Tag: s.machines[2].Tag().String(), Status: status.ProvisioningError.String(), Info: "again"}, 415 {Tag: "machine-42", Status: status.Provisioning.String(), Info: "blah"}, 416 {Tag: "unit-foo-0", Status: status.Error.String(), Info: "foobar"}, 417 {Tag: "application-bar", Status: status.ProvisioningError.String(), Info: "foobar"}, 418 }} 419 result, err := s.provisioner.SetInstanceStatus(args) 420 c.Assert(err, jc.ErrorIsNil) 421 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 422 Results: []params.ErrorResult{ 423 {nil}, 424 {nil}, 425 {nil}, 426 {apiservertesting.NotFoundError("machine 42")}, 427 {apiservertesting.ErrUnauthorized}, 428 {apiservertesting.ErrUnauthorized}, 429 }, 430 }) 431 432 // Verify the changes. 433 s.assertInstanceStatus(c, 0, status.Provisioning, "not really", map[string]interface{}{"foo": "bar"}) 434 s.assertInstanceStatus(c, 1, status.Running, "foobar", map[string]interface{}{}) 435 s.assertInstanceStatus(c, 2, status.ProvisioningError, "again", map[string]interface{}{}) 436 // ProvisioningError also has a special case which is to set the machine to Error 437 s.assertStatus(c, 2, status.Error, "again", map[string]interface{}{}) 438 } 439 440 func (s *withoutControllerSuite) TestMachinesWithTransientErrors(c *gc.C) { 441 now := time.Now() 442 sInfo := status.StatusInfo{ 443 Status: status.Provisioning, 444 Message: "blah", 445 Since: &now, 446 } 447 err := s.machines[0].SetInstanceStatus(sInfo) 448 c.Assert(err, jc.ErrorIsNil) 449 sInfo = status.StatusInfo{ 450 Status: status.ProvisioningError, 451 Message: "transient error", 452 Data: map[string]interface{}{"transient": true, "foo": "bar"}, 453 Since: &now, 454 } 455 err = s.machines[1].SetInstanceStatus(sInfo) 456 c.Assert(err, jc.ErrorIsNil) 457 sInfo = status.StatusInfo{ 458 Status: status.ProvisioningError, 459 Message: "error", 460 Data: map[string]interface{}{"transient": false}, 461 Since: &now, 462 } 463 err = s.machines[2].SetInstanceStatus(sInfo) 464 c.Assert(err, jc.ErrorIsNil) 465 sInfo = status.StatusInfo{ 466 Status: status.Error, 467 Message: "error", 468 Since: &now, 469 } 470 err = s.machines[3].SetInstanceStatus(sInfo) 471 c.Assert(err, jc.ErrorIsNil) 472 // Machine 4 is provisioned but error not reset yet. 473 sInfo = status.StatusInfo{ 474 Status: status.Error, 475 Message: "transient error", 476 Data: map[string]interface{}{"transient": true, "foo": "bar"}, 477 Since: &now, 478 } 479 err = s.machines[4].SetInstanceStatus(sInfo) 480 c.Assert(err, jc.ErrorIsNil) 481 hwChars := instance.MustParseHardware("arch=i386", "mem=4G") 482 err = s.machines[4].SetProvisioned("i-am", "", "fake_nonce", &hwChars) 483 c.Assert(err, jc.ErrorIsNil) 484 485 result, err := s.provisioner.MachinesWithTransientErrors() 486 c.Assert(err, jc.ErrorIsNil) 487 c.Assert(result, gc.DeepEquals, params.StatusResults{ 488 Results: []params.StatusResult{ 489 {Id: "1", Life: "alive", Status: "provisioning error", Info: "transient error", 490 Data: map[string]interface{}{"transient": true, "foo": "bar"}}, 491 }, 492 }) 493 } 494 495 func (s *withoutControllerSuite) TestMachinesWithTransientErrorsPermission(c *gc.C) { 496 // Machines where there's permission issues are omitted. 497 anAuthorizer := s.authorizer 498 anAuthorizer.Controller = false 499 anAuthorizer.Tag = names.NewMachineTag("1") 500 aProvisioner, err := provisioner.NewProvisionerAPI(s.State, s.resources, 501 anAuthorizer) 502 now := time.Now() 503 sInfo := status.StatusInfo{ 504 Status: status.Running, 505 Message: "blah", 506 Since: &now, 507 } 508 err = s.machines[0].SetInstanceStatus(sInfo) 509 c.Assert(err, jc.ErrorIsNil) 510 sInfo = status.StatusInfo{ 511 Status: status.ProvisioningError, 512 Message: "transient error", 513 Data: map[string]interface{}{"transient": true, "foo": "bar"}, 514 Since: &now, 515 } 516 err = s.machines[1].SetInstanceStatus(sInfo) 517 c.Assert(err, jc.ErrorIsNil) 518 sInfo = status.StatusInfo{ 519 Status: status.ProvisioningError, 520 Message: "error", 521 Data: map[string]interface{}{"transient": false}, 522 Since: &now, 523 } 524 err = s.machines[2].SetInstanceStatus(sInfo) 525 c.Assert(err, jc.ErrorIsNil) 526 sInfo = status.StatusInfo{ 527 Status: status.ProvisioningError, 528 Message: "error", 529 Since: &now, 530 } 531 err = s.machines[3].SetInstanceStatus(sInfo) 532 c.Assert(err, jc.ErrorIsNil) 533 534 result, err := aProvisioner.MachinesWithTransientErrors() 535 c.Assert(err, jc.ErrorIsNil) 536 c.Assert(result, gc.DeepEquals, params.StatusResults{ 537 Results: []params.StatusResult{{ 538 Id: "1", Life: "alive", Status: "provisioning error", 539 Info: "transient error", 540 Data: map[string]interface{}{"transient": true, "foo": "bar"}, 541 }, 542 }, 543 }) 544 } 545 546 func (s *withoutControllerSuite) TestEnsureDead(c *gc.C) { 547 err := s.machines[1].EnsureDead() 548 c.Assert(err, jc.ErrorIsNil) 549 s.assertLife(c, 0, state.Alive) 550 s.assertLife(c, 1, state.Dead) 551 s.assertLife(c, 2, state.Alive) 552 553 args := params.Entities{Entities: []params.Entity{ 554 {Tag: s.machines[0].Tag().String()}, 555 {Tag: s.machines[1].Tag().String()}, 556 {Tag: s.machines[2].Tag().String()}, 557 {Tag: "machine-42"}, 558 {Tag: "unit-foo-0"}, 559 {Tag: "application-bar"}, 560 }} 561 result, err := s.provisioner.EnsureDead(args) 562 c.Assert(err, jc.ErrorIsNil) 563 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 564 Results: []params.ErrorResult{ 565 {nil}, 566 {nil}, 567 {nil}, 568 {apiservertesting.NotFoundError("machine 42")}, 569 {apiservertesting.ErrUnauthorized}, 570 {apiservertesting.ErrUnauthorized}, 571 }, 572 }) 573 574 // Verify the changes. 575 s.assertLife(c, 0, state.Dead) 576 s.assertLife(c, 1, state.Dead) 577 s.assertLife(c, 2, state.Dead) 578 } 579 580 func (s *withoutControllerSuite) assertLife(c *gc.C, index int, expectLife state.Life) { 581 err := s.machines[index].Refresh() 582 c.Assert(err, jc.ErrorIsNil) 583 c.Assert(s.machines[index].Life(), gc.Equals, expectLife) 584 } 585 586 func (s *withoutControllerSuite) assertStatus(c *gc.C, index int, expectStatus status.Status, expectInfo string, 587 expectData map[string]interface{}) { 588 589 statusInfo, err := s.machines[index].Status() 590 c.Assert(err, jc.ErrorIsNil) 591 c.Assert(statusInfo.Status, gc.Equals, expectStatus) 592 c.Assert(statusInfo.Message, gc.Equals, expectInfo) 593 c.Assert(statusInfo.Data, gc.DeepEquals, expectData) 594 } 595 596 func (s *withoutControllerSuite) assertInstanceStatus(c *gc.C, index int, expectStatus status.Status, expectInfo string, 597 expectData map[string]interface{}) { 598 599 statusInfo, err := s.machines[index].InstanceStatus() 600 c.Assert(err, jc.ErrorIsNil) 601 c.Assert(statusInfo.Status, gc.Equals, expectStatus) 602 c.Assert(statusInfo.Message, gc.Equals, expectInfo) 603 c.Assert(statusInfo.Data, gc.DeepEquals, expectData) 604 } 605 606 func (s *withoutControllerSuite) TestWatchContainers(c *gc.C) { 607 c.Assert(s.resources.Count(), gc.Equals, 0) 608 609 args := params.WatchContainers{Params: []params.WatchContainer{ 610 {MachineTag: s.machines[0].Tag().String(), ContainerType: string(instance.LXD)}, 611 {MachineTag: s.machines[1].Tag().String(), ContainerType: string(instance.KVM)}, 612 {MachineTag: "machine-42", ContainerType: ""}, 613 {MachineTag: "unit-foo-0", ContainerType: ""}, 614 {MachineTag: "application-bar", ContainerType: ""}, 615 }} 616 result, err := s.provisioner.WatchContainers(args) 617 c.Assert(err, jc.ErrorIsNil) 618 c.Assert(result, gc.DeepEquals, params.StringsWatchResults{ 619 Results: []params.StringsWatchResult{ 620 {StringsWatcherId: "1", Changes: []string{}}, 621 {StringsWatcherId: "2", Changes: []string{}}, 622 {Error: apiservertesting.NotFoundError("machine 42")}, 623 {Error: apiservertesting.ErrUnauthorized}, 624 {Error: apiservertesting.ErrUnauthorized}, 625 }, 626 }) 627 628 // Verify the resources were registered and stop them when done. 629 c.Assert(s.resources.Count(), gc.Equals, 2) 630 m0Watcher := s.resources.Get("1") 631 defer statetesting.AssertStop(c, m0Watcher) 632 m1Watcher := s.resources.Get("2") 633 defer statetesting.AssertStop(c, m1Watcher) 634 635 // Check that the Watch has consumed the initial event ("returned" 636 // in the Watch call) 637 wc0 := statetesting.NewStringsWatcherC(c, s.State, m0Watcher.(state.StringsWatcher)) 638 wc0.AssertNoChange() 639 wc1 := statetesting.NewStringsWatcherC(c, s.State, m1Watcher.(state.StringsWatcher)) 640 wc1.AssertNoChange() 641 } 642 643 func (s *withoutControllerSuite) TestWatchAllContainers(c *gc.C) { 644 c.Assert(s.resources.Count(), gc.Equals, 0) 645 646 args := params.WatchContainers{Params: []params.WatchContainer{ 647 {MachineTag: s.machines[0].Tag().String()}, 648 {MachineTag: s.machines[1].Tag().String()}, 649 {MachineTag: "machine-42"}, 650 {MachineTag: "unit-foo-0"}, 651 {MachineTag: "application-bar"}, 652 }} 653 result, err := s.provisioner.WatchAllContainers(args) 654 c.Assert(err, jc.ErrorIsNil) 655 c.Assert(result, gc.DeepEquals, params.StringsWatchResults{ 656 Results: []params.StringsWatchResult{ 657 {StringsWatcherId: "1", Changes: []string{}}, 658 {StringsWatcherId: "2", Changes: []string{}}, 659 {Error: apiservertesting.NotFoundError("machine 42")}, 660 {Error: apiservertesting.ErrUnauthorized}, 661 {Error: apiservertesting.ErrUnauthorized}, 662 }, 663 }) 664 665 // Verify the resources were registered and stop them when done. 666 c.Assert(s.resources.Count(), gc.Equals, 2) 667 m0Watcher := s.resources.Get("1") 668 defer statetesting.AssertStop(c, m0Watcher) 669 m1Watcher := s.resources.Get("2") 670 defer statetesting.AssertStop(c, m1Watcher) 671 672 // Check that the Watch has consumed the initial event ("returned" 673 // in the Watch call) 674 wc0 := statetesting.NewStringsWatcherC(c, s.State, m0Watcher.(state.StringsWatcher)) 675 wc0.AssertNoChange() 676 wc1 := statetesting.NewStringsWatcherC(c, s.State, m1Watcher.(state.StringsWatcher)) 677 wc1.AssertNoChange() 678 } 679 680 func (s *withoutControllerSuite) TestWatchContainersCharmProfiles(c *gc.C) { 681 c.Assert(s.resources.Count(), gc.Equals, 0) 682 683 args := params.WatchContainers{Params: []params.WatchContainer{ 684 {MachineTag: s.machines[0].Tag().String(), ContainerType: string(instance.LXD)}, 685 {MachineTag: s.machines[1].Tag().String(), ContainerType: string(instance.KVM)}, 686 {MachineTag: "machine-42", ContainerType: ""}, 687 {MachineTag: "unit-foo-0", ContainerType: ""}, 688 {MachineTag: "application-bar", ContainerType: ""}, 689 }} 690 result, err := s.provisioner.WatchContainersCharmProfiles(args) 691 c.Assert(err, jc.ErrorIsNil) 692 c.Assert(result, gc.DeepEquals, params.StringsWatchResults{ 693 Results: []params.StringsWatchResult{ 694 {StringsWatcherId: "1", Changes: []string{}}, 695 {StringsWatcherId: "2", Changes: []string{}}, 696 {Error: apiservertesting.NotFoundError("machine 42")}, 697 {Error: apiservertesting.ErrUnauthorized}, 698 {Error: apiservertesting.ErrUnauthorized}, 699 }, 700 }) 701 702 // Verify the resources were registered and stop them when done. 703 c.Assert(s.resources.Count(), gc.Equals, 2) 704 m0Watcher := s.resources.Get("1") 705 defer statetesting.AssertStop(c, m0Watcher) 706 m1Watcher := s.resources.Get("2") 707 defer statetesting.AssertStop(c, m1Watcher) 708 709 // Check that the Watch has consumed the initial event "returned" 710 // in the Watch call) 711 wc0 := statetesting.NewStringsWatcherC(c, s.State, m0Watcher.(state.StringsWatcher)) 712 wc0.AssertNoChange() 713 wc1 := statetesting.NewStringsWatcherC(c, s.State, m1Watcher.(state.StringsWatcher)) 714 wc1.AssertNoChange() 715 } 716 717 func (s *withoutControllerSuite) TestWatchModelMachinesCharmProfiles(c *gc.C) { 718 c.Assert(s.resources.Count(), gc.Equals, 0) 719 720 got, err := s.provisioner.WatchModelMachinesCharmProfiles() 721 c.Assert(err, jc.ErrorIsNil) 722 want := params.StringsWatchResult{ 723 StringsWatcherId: "1", 724 Changes: []string{}, 725 } 726 c.Assert(got.StringsWatcherId, gc.Equals, want.StringsWatcherId) 727 c.Assert(got.Changes, jc.SameContents, want.Changes) 728 729 // Verify the resources were registered and stop them when done. 730 c.Assert(s.resources.Count(), gc.Equals, 1) 731 resource := s.resources.Get("1") 732 defer statetesting.AssertStop(c, resource) 733 734 // Check that the Watch has consumed the initial event "returned" 735 // in the Watch call) 736 wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher)) 737 wc.AssertNoChange() 738 739 // Make sure WatchModelMachines fails with a machine agent login. 740 anAuthorizer := s.authorizer 741 anAuthorizer.Tag = names.NewMachineTag("1") 742 anAuthorizer.Controller = false 743 aProvisioner, err := provisioner.NewProvisionerAPI(s.State, s.resources, anAuthorizer) 744 c.Assert(err, jc.ErrorIsNil) 745 746 result, err := aProvisioner.WatchModelMachinesCharmProfiles() 747 c.Assert(err, gc.ErrorMatches, "permission denied") 748 c.Assert(result, gc.DeepEquals, params.StringsWatchResult{}) 749 } 750 751 func (s *withoutControllerSuite) TestModelConfigNonManager(c *gc.C) { 752 // Now test it with a non-controller and make sure 753 // the secret attributes are masked. 754 anAuthorizer := s.authorizer 755 anAuthorizer.Tag = names.NewMachineTag("1") 756 anAuthorizer.Controller = false 757 aProvisioner, err := provisioner.NewProvisionerAPI(s.State, s.resources, 758 anAuthorizer) 759 c.Assert(err, jc.ErrorIsNil) 760 s.AssertModelConfig(c, aProvisioner) 761 } 762 763 func (s *withoutControllerSuite) TestStatus(c *gc.C) { 764 now := time.Now() 765 sInfo := status.StatusInfo{ 766 Status: status.Started, 767 Message: "blah", 768 Since: &now, 769 } 770 err := s.machines[0].SetStatus(sInfo) 771 c.Assert(err, jc.ErrorIsNil) 772 sInfo = status.StatusInfo{ 773 Status: status.Stopped, 774 Message: "foo", 775 Since: &now, 776 } 777 err = s.machines[1].SetStatus(sInfo) 778 c.Assert(err, jc.ErrorIsNil) 779 sInfo = status.StatusInfo{ 780 Status: status.Error, 781 Message: "not really", 782 Data: map[string]interface{}{"foo": "bar"}, 783 Since: &now, 784 } 785 err = s.machines[2].SetStatus(sInfo) 786 c.Assert(err, jc.ErrorIsNil) 787 788 args := params.Entities{Entities: []params.Entity{ 789 {Tag: s.machines[0].Tag().String()}, 790 {Tag: s.machines[1].Tag().String()}, 791 {Tag: s.machines[2].Tag().String()}, 792 {Tag: "machine-42"}, 793 {Tag: "unit-foo-0"}, 794 {Tag: "application-bar"}, 795 }} 796 result, err := s.provisioner.Status(args) 797 c.Assert(err, jc.ErrorIsNil) 798 // Zero out the updated timestamps so we can easily check the results. 799 for i, statusResult := range result.Results { 800 r := statusResult 801 if r.Status != "" { 802 c.Assert(r.Since, gc.NotNil) 803 } 804 r.Since = nil 805 result.Results[i] = r 806 } 807 c.Assert(result, gc.DeepEquals, params.StatusResults{ 808 Results: []params.StatusResult{ 809 {Status: status.Started.String(), Info: "blah", Data: map[string]interface{}{}}, 810 {Status: status.Stopped.String(), Info: "foo", Data: map[string]interface{}{}}, 811 {Status: status.Error.String(), Info: "not really", Data: map[string]interface{}{"foo": "bar"}}, 812 {Error: apiservertesting.NotFoundError("machine 42")}, 813 {Error: apiservertesting.ErrUnauthorized}, 814 {Error: apiservertesting.ErrUnauthorized}, 815 }, 816 }) 817 } 818 819 func (s *withoutControllerSuite) TestInstanceStatus(c *gc.C) { 820 now := time.Now() 821 sInfo := status.StatusInfo{ 822 Status: status.Provisioning, 823 Message: "blah", 824 Since: &now, 825 } 826 err := s.machines[0].SetInstanceStatus(sInfo) 827 c.Assert(err, jc.ErrorIsNil) 828 sInfo = status.StatusInfo{ 829 Status: status.Running, 830 Message: "foo", 831 Since: &now, 832 } 833 err = s.machines[1].SetInstanceStatus(sInfo) 834 c.Assert(err, jc.ErrorIsNil) 835 sInfo = status.StatusInfo{ 836 Status: status.ProvisioningError, 837 Message: "not really", 838 Data: map[string]interface{}{"foo": "bar"}, 839 Since: &now, 840 } 841 err = s.machines[2].SetInstanceStatus(sInfo) 842 c.Assert(err, jc.ErrorIsNil) 843 844 args := params.Entities{Entities: []params.Entity{ 845 {Tag: s.machines[0].Tag().String()}, 846 {Tag: s.machines[1].Tag().String()}, 847 {Tag: s.machines[2].Tag().String()}, 848 {Tag: "machine-42"}, 849 {Tag: "unit-foo-0"}, 850 {Tag: "application-bar"}, 851 }} 852 result, err := s.provisioner.InstanceStatus(args) 853 c.Assert(err, jc.ErrorIsNil) 854 // Zero out the updated timestamps so we can easily check the results. 855 for i, statusResult := range result.Results { 856 r := statusResult 857 if r.Status != "" { 858 c.Assert(r.Since, gc.NotNil) 859 } 860 r.Since = nil 861 result.Results[i] = r 862 } 863 c.Assert(result, gc.DeepEquals, params.StatusResults{ 864 Results: []params.StatusResult{ 865 {Status: status.Provisioning.String(), Info: "blah", Data: map[string]interface{}{}}, 866 {Status: status.Running.String(), Info: "foo", Data: map[string]interface{}{}}, 867 {Status: status.ProvisioningError.String(), Info: "not really", Data: map[string]interface{}{"foo": "bar"}}, 868 {Error: apiservertesting.NotFoundError("machine 42")}, 869 {Error: apiservertesting.ErrUnauthorized}, 870 {Error: apiservertesting.ErrUnauthorized}, 871 }, 872 }) 873 } 874 875 func (s *withoutControllerSuite) TestSeries(c *gc.C) { 876 // Add a machine with different series. 877 foobarMachine := s.Factory.MakeMachine(c, &factory.MachineParams{Series: "foobar"}) 878 args := params.Entities{Entities: []params.Entity{ 879 {Tag: s.machines[0].Tag().String()}, 880 {Tag: foobarMachine.Tag().String()}, 881 {Tag: s.machines[2].Tag().String()}, 882 {Tag: "machine-42"}, 883 {Tag: "unit-foo-0"}, 884 {Tag: "application-bar"}, 885 }} 886 result, err := s.provisioner.Series(args) 887 c.Assert(err, jc.ErrorIsNil) 888 c.Assert(result, gc.DeepEquals, params.StringResults{ 889 Results: []params.StringResult{ 890 {Result: s.machines[0].Series()}, 891 {Result: foobarMachine.Series()}, 892 {Result: s.machines[2].Series()}, 893 {Error: apiservertesting.NotFoundError("machine 42")}, 894 {Error: apiservertesting.ErrUnauthorized}, 895 {Error: apiservertesting.ErrUnauthorized}, 896 }, 897 }) 898 } 899 900 func (s *withoutControllerSuite) TestAvailabilityZone(c *gc.C) { 901 availabilityZone := "ru-north-siberia" 902 emptyAz := "" 903 hcWithAZ := instance.HardwareCharacteristics{AvailabilityZone: &availabilityZone} 904 hcWithEmptyAZ := instance.HardwareCharacteristics{AvailabilityZone: &emptyAz} 905 hcWithNilAz := instance.HardwareCharacteristics{AvailabilityZone: nil} 906 907 // add machines with different availability zones: string, empty string, nil 908 azMachine, _ := s.Factory.MakeMachineReturningPassword(c, &factory.MachineParams{ 909 Characteristics: &hcWithAZ, 910 }) 911 912 emptyAzMachine, _ := s.Factory.MakeMachineReturningPassword(c, &factory.MachineParams{ 913 Characteristics: &hcWithEmptyAZ, 914 }) 915 916 nilAzMachine, _ := s.Factory.MakeMachineReturningPassword(c, &factory.MachineParams{ 917 Characteristics: &hcWithNilAz, 918 }) 919 args := params.Entities{Entities: []params.Entity{ 920 {Tag: azMachine.Tag().String()}, 921 {Tag: emptyAzMachine.Tag().String()}, 922 {Tag: nilAzMachine.Tag().String()}, 923 }} 924 result, err := s.provisioner.AvailabilityZone(args) 925 c.Assert(err, jc.ErrorIsNil) 926 c.Assert(result, gc.DeepEquals, params.StringResults{ 927 Results: []params.StringResult{ 928 {Result: availabilityZone}, 929 {Result: emptyAz}, 930 {Result: emptyAz}, 931 }, 932 }) 933 } 934 935 func (s *withoutControllerSuite) TestKeepInstance(c *gc.C) { 936 // Add a machine with keep-instance = true. 937 foobarMachine := s.Factory.MakeMachine(c, &factory.MachineParams{InstanceId: "1234"}) 938 err := foobarMachine.SetKeepInstance(true) 939 c.Assert(err, jc.ErrorIsNil) 940 941 args := params.Entities{Entities: []params.Entity{ 942 {Tag: s.machines[0].Tag().String()}, 943 {Tag: foobarMachine.Tag().String()}, 944 {Tag: s.machines[2].Tag().String()}, 945 {Tag: "machine-42"}, 946 {Tag: "unit-foo-0"}, 947 {Tag: "application-bar"}, 948 }} 949 result, err := s.provisioner.KeepInstance(args) 950 c.Assert(err, jc.ErrorIsNil) 951 c.Assert(result, gc.DeepEquals, params.BoolResults{ 952 Results: []params.BoolResult{ 953 {Result: false}, 954 {Result: true}, 955 {Result: false}, 956 {Error: apiservertesting.NotFoundError("machine 42")}, 957 {Error: apiservertesting.ErrUnauthorized}, 958 {Error: apiservertesting.ErrUnauthorized}, 959 }, 960 }) 961 } 962 963 func (s *withoutControllerSuite) TestDistributionGroup(c *gc.C) { 964 addUnits := func(name string, machines ...*state.Machine) (units []*state.Unit) { 965 app := s.AddTestingApplication(c, name, s.AddTestingCharm(c, name)) 966 for _, m := range machines { 967 unit, err := app.AddUnit(state.AddUnitParams{}) 968 c.Assert(err, jc.ErrorIsNil) 969 err = unit.AssignToMachine(m) 970 c.Assert(err, jc.ErrorIsNil) 971 units = append(units, unit) 972 } 973 return units 974 } 975 setProvisioned := func(id string) { 976 m, err := s.State.Machine(id) 977 c.Assert(err, jc.ErrorIsNil) 978 err = m.SetProvisioned(instance.Id("machine-"+id+"-inst"), "", "nonce", nil) 979 c.Assert(err, jc.ErrorIsNil) 980 } 981 982 mysqlUnit := addUnits("mysql", s.machines[0], s.machines[3])[0] 983 wordpressUnits := addUnits("wordpress", s.machines[0], s.machines[1], s.machines[2]) 984 985 // Unassign wordpress/1 from machine-1. 986 // The unit should not show up in the results. 987 err := wordpressUnits[1].UnassignFromMachine() 988 c.Assert(err, jc.ErrorIsNil) 989 990 // Provision machines 1, 2 and 3. Machine-0 remains 991 // unprovisioned, and machine-1 has no units, and so 992 // neither will show up in the results. 993 setProvisioned("1") 994 setProvisioned("2") 995 setProvisioned("3") 996 997 // Add a few controllers, provision two of them. 998 _, err = s.State.EnableHA(3, constraints.Value{}, "quantal", nil) 999 c.Assert(err, jc.ErrorIsNil) 1000 setProvisioned("5") 1001 setProvisioned("7") 1002 1003 // Create a logging service, subordinate to mysql. 1004 s.AddTestingApplication(c, "logging", s.AddTestingCharm(c, "logging")) 1005 eps, err := s.State.InferEndpoints("mysql", "logging") 1006 c.Assert(err, jc.ErrorIsNil) 1007 rel, err := s.State.AddRelation(eps...) 1008 c.Assert(err, jc.ErrorIsNil) 1009 ru, err := rel.Unit(mysqlUnit) 1010 c.Assert(err, jc.ErrorIsNil) 1011 err = ru.EnterScope(nil) 1012 c.Assert(err, jc.ErrorIsNil) 1013 1014 args := params.Entities{Entities: []params.Entity{ 1015 {Tag: s.machines[0].Tag().String()}, 1016 {Tag: s.machines[1].Tag().String()}, 1017 {Tag: s.machines[2].Tag().String()}, 1018 {Tag: s.machines[3].Tag().String()}, 1019 {Tag: "machine-5"}, 1020 }} 1021 result, err := s.provisioner.DistributionGroup(args) 1022 c.Assert(err, jc.ErrorIsNil) 1023 c.Assert(result, gc.DeepEquals, params.DistributionGroupResults{ 1024 Results: []params.DistributionGroupResult{ 1025 {Result: []instance.Id{"machine-2-inst", "machine-3-inst"}}, 1026 {Result: []instance.Id{}}, 1027 {Result: []instance.Id{"machine-2-inst"}}, 1028 {Result: []instance.Id{"machine-3-inst"}}, 1029 {Result: []instance.Id{"machine-5-inst", "machine-7-inst"}}, 1030 }, 1031 }) 1032 } 1033 1034 func (s *withoutControllerSuite) TestDistributionGroupControllerAuth(c *gc.C) { 1035 args := params.Entities{Entities: []params.Entity{ 1036 {Tag: "machine-0"}, 1037 {Tag: "machine-42"}, 1038 {Tag: "machine-0-lxd-99"}, 1039 {Tag: "unit-foo-0"}, 1040 {Tag: "application-bar"}, 1041 }} 1042 result, err := s.provisioner.DistributionGroup(args) 1043 c.Assert(err, jc.ErrorIsNil) 1044 c.Assert(result, gc.DeepEquals, params.DistributionGroupResults{ 1045 Results: []params.DistributionGroupResult{ 1046 // controller may access any top-level machines. 1047 {Result: []instance.Id{}}, 1048 {Error: apiservertesting.NotFoundError("machine 42")}, 1049 // only a machine agent for the container or its 1050 // parent may access it. 1051 {Error: apiservertesting.ErrUnauthorized}, 1052 // non-machines always unauthorized 1053 {Error: apiservertesting.ErrUnauthorized}, 1054 {Error: apiservertesting.ErrUnauthorized}, 1055 }, 1056 }) 1057 } 1058 1059 func (s *withoutControllerSuite) TestDistributionGroupMachineAgentAuth(c *gc.C) { 1060 anAuthorizer := s.authorizer 1061 anAuthorizer.Tag = names.NewMachineTag("1") 1062 anAuthorizer.Controller = false 1063 provisioner, err := provisioner.NewProvisionerAPI(s.State, s.resources, anAuthorizer) 1064 c.Check(err, jc.ErrorIsNil) 1065 args := params.Entities{Entities: []params.Entity{ 1066 {Tag: "machine-0"}, 1067 {Tag: "machine-1"}, 1068 {Tag: "machine-42"}, 1069 {Tag: "machine-0-lxd-99"}, 1070 {Tag: "machine-1-lxd-99"}, 1071 {Tag: "machine-1-lxd-99-lxd-100"}, 1072 }} 1073 result, err := provisioner.DistributionGroup(args) 1074 c.Assert(err, jc.ErrorIsNil) 1075 c.Assert(result, gc.DeepEquals, params.DistributionGroupResults{ 1076 Results: []params.DistributionGroupResult{ 1077 {Error: apiservertesting.ErrUnauthorized}, 1078 {Result: []instance.Id{}}, 1079 {Error: apiservertesting.ErrUnauthorized}, 1080 // only a machine agent for the container or its 1081 // parent may access it. 1082 {Error: apiservertesting.ErrUnauthorized}, 1083 {Error: apiservertesting.NotFoundError("machine 1/lxd/99")}, 1084 {Error: apiservertesting.ErrUnauthorized}, 1085 }, 1086 }) 1087 } 1088 1089 func (s *withoutControllerSuite) TestDistributionGroupByMachineId(c *gc.C) { 1090 addUnits := func(name string, machines ...*state.Machine) (units []*state.Unit) { 1091 app := s.AddTestingApplication(c, name, s.AddTestingCharm(c, name)) 1092 for _, m := range machines { 1093 unit, err := app.AddUnit(state.AddUnitParams{}) 1094 c.Assert(err, jc.ErrorIsNil) 1095 err = unit.AssignToMachine(m) 1096 c.Assert(err, jc.ErrorIsNil) 1097 units = append(units, unit) 1098 } 1099 return units 1100 } 1101 setProvisioned := func(id string) { 1102 m, err := s.State.Machine(id) 1103 c.Assert(err, jc.ErrorIsNil) 1104 err = m.SetProvisioned(instance.Id("machine-"+id+"-inst"), "", "nonce", nil) 1105 c.Assert(err, jc.ErrorIsNil) 1106 } 1107 1108 _ = addUnits("mysql", s.machines[0], s.machines[3])[0] 1109 wordpressUnits := addUnits("wordpress", s.machines[0], s.machines[1], s.machines[2]) 1110 1111 // Unassign wordpress/1 from machine-1. 1112 // The unit should not show up in the results. 1113 err := wordpressUnits[1].UnassignFromMachine() 1114 c.Assert(err, jc.ErrorIsNil) 1115 1116 // Provision machines 1, 2 and 3. Machine-0 remains 1117 // unprovisioned. 1118 setProvisioned("1") 1119 setProvisioned("2") 1120 setProvisioned("3") 1121 1122 // Add a few controllers, provision two of them. 1123 _, err = s.State.EnableHA(3, constraints.Value{}, "quantal", nil) 1124 c.Assert(err, jc.ErrorIsNil) 1125 setProvisioned("5") 1126 setProvisioned("7") 1127 1128 args := params.Entities{Entities: []params.Entity{ 1129 {Tag: s.machines[0].Tag().String()}, 1130 {Tag: s.machines[1].Tag().String()}, 1131 {Tag: s.machines[2].Tag().String()}, 1132 {Tag: s.machines[3].Tag().String()}, 1133 {Tag: "machine-5"}, 1134 }} 1135 provisionerV5 := provisioner.ProvisionerAPIV5{s.provisioner} 1136 result, err := provisionerV5.DistributionGroupByMachineId(args) 1137 c.Assert(err, jc.ErrorIsNil) 1138 c.Assert(result, gc.DeepEquals, params.StringsResults{ 1139 Results: []params.StringsResult{ 1140 {Result: []string{"2", "3"}}, 1141 {Result: []string{}}, 1142 {Result: []string{"0"}}, 1143 {Result: []string{"0"}}, 1144 {Result: []string{"6", "7"}}, 1145 }, 1146 }) 1147 } 1148 1149 func (s *withoutControllerSuite) TestDistributionGroupByMachineIdControllerAuth(c *gc.C) { 1150 args := params.Entities{Entities: []params.Entity{ 1151 {Tag: "machine-0"}, 1152 {Tag: "machine-42"}, 1153 {Tag: "machine-0-lxd-99"}, 1154 {Tag: "unit-foo-0"}, 1155 {Tag: "application-bar"}, 1156 }} 1157 provisionerV5 := provisioner.ProvisionerAPIV5{s.provisioner} 1158 result, err := provisionerV5.DistributionGroupByMachineId(args) 1159 c.Assert(err, jc.ErrorIsNil) 1160 c.Assert(result, gc.DeepEquals, params.StringsResults{ 1161 Results: []params.StringsResult{ 1162 // controller may access any top-level machines. 1163 {Result: []string{}, Error: nil}, 1164 {Result: nil, Error: apiservertesting.NotFoundError("machine 42")}, 1165 // only a machine agent for the container or its 1166 // parent may access it. 1167 {Result: nil, Error: apiservertesting.ErrUnauthorized}, 1168 // non-machines always unauthorized 1169 {Result: nil, Error: apiservertesting.ErrUnauthorized}, 1170 {Result: nil, Error: apiservertesting.ErrUnauthorized}, 1171 }, 1172 }) 1173 } 1174 1175 func (s *withoutControllerSuite) TestDistributionGroupByMachineIdMachineAgentAuth(c *gc.C) { 1176 anAuthorizer := s.authorizer 1177 anAuthorizer.Tag = names.NewMachineTag("1") 1178 anAuthorizer.Controller = false 1179 provisionerV5, err := provisioner.NewProvisionerAPIV5(s.State, s.resources, anAuthorizer) 1180 c.Check(err, jc.ErrorIsNil) 1181 args := params.Entities{Entities: []params.Entity{ 1182 {Tag: "machine-0"}, 1183 {Tag: "machine-1"}, 1184 {Tag: "machine-42"}, 1185 {Tag: "machine-0-lxd-99"}, 1186 {Tag: "machine-1-lxd-99"}, 1187 {Tag: "machine-1-lxd-99-lxd-100"}, 1188 }} 1189 result, err := provisionerV5.DistributionGroupByMachineId(args) 1190 c.Assert(err, jc.ErrorIsNil) 1191 c.Assert(result, gc.DeepEquals, params.StringsResults{ 1192 Results: []params.StringsResult{ 1193 {Result: nil, Error: apiservertesting.ErrUnauthorized}, 1194 {Result: []string{}, Error: nil}, 1195 {Result: nil, Error: apiservertesting.ErrUnauthorized}, 1196 // only a machine agent for the container or its 1197 // parent may access it. 1198 {Result: nil, Error: apiservertesting.ErrUnauthorized}, 1199 {Result: nil, Error: apiservertesting.NotFoundError("machine 1/lxd/99")}, 1200 {Result: nil, Error: apiservertesting.ErrUnauthorized}, 1201 }, 1202 }) 1203 } 1204 1205 func (s *provisionerSuite) TestConstraints(c *gc.C) { 1206 // Add a machine with some constraints. 1207 cons := constraints.MustParse("cores=123", "mem=8G") 1208 template := state.MachineTemplate{ 1209 Series: "quantal", 1210 Jobs: []state.MachineJob{state.JobHostUnits}, 1211 Constraints: cons, 1212 } 1213 consMachine, err := s.State.AddOneMachine(template) 1214 c.Assert(err, jc.ErrorIsNil) 1215 1216 machine0Constraints, err := s.machines[0].Constraints() 1217 c.Assert(err, jc.ErrorIsNil) 1218 1219 args := params.Entities{Entities: []params.Entity{ 1220 {Tag: s.machines[0].Tag().String()}, 1221 {Tag: consMachine.Tag().String()}, 1222 {Tag: "machine-42"}, 1223 {Tag: "unit-foo-0"}, 1224 {Tag: "application-bar"}, 1225 }} 1226 result, err := s.provisioner.Constraints(args) 1227 c.Assert(err, jc.ErrorIsNil) 1228 c.Assert(result, gc.DeepEquals, params.ConstraintsResults{ 1229 Results: []params.ConstraintsResult{ 1230 {Constraints: machine0Constraints}, 1231 {Constraints: template.Constraints}, 1232 {Error: apiservertesting.NotFoundError("machine 42")}, 1233 {Error: apiservertesting.ErrUnauthorized}, 1234 {Error: apiservertesting.ErrUnauthorized}, 1235 }, 1236 }) 1237 } 1238 1239 func (s *withoutControllerSuite) TestSetInstanceInfo(c *gc.C) { 1240 pm := poolmanager.New(state.NewStateSettings(s.State), storage.ChainedProviderRegistry{ 1241 dummy.StorageProviders(), 1242 provider.CommonStorageProviders(), 1243 }) 1244 _, err := pm.Create("static-pool", "static", map[string]interface{}{"foo": "bar"}) 1245 c.Assert(err, jc.ErrorIsNil) 1246 err = s.Model.UpdateModelConfig(map[string]interface{}{ 1247 "storage-default-block-source": "static-pool", 1248 }, nil) 1249 c.Assert(err, jc.ErrorIsNil) 1250 1251 // Provision machine 0 first. 1252 hwChars := instance.MustParseHardware("arch=i386", "mem=4G") 1253 err = s.machines[0].SetInstanceInfo("i-am", "", "fake_nonce", &hwChars, nil, nil, nil, nil, nil) 1254 c.Assert(err, jc.ErrorIsNil) 1255 1256 volumesMachine, err := s.State.AddOneMachine(state.MachineTemplate{ 1257 Series: "quantal", 1258 Jobs: []state.MachineJob{state.JobHostUnits}, 1259 Volumes: []state.HostVolumeParams{{ 1260 Volume: state.VolumeParams{Size: 1000}, 1261 }}, 1262 }) 1263 c.Assert(err, jc.ErrorIsNil) 1264 1265 args := params.InstancesInfo{Machines: []params.InstanceInfo{{ 1266 Tag: s.machines[0].Tag().String(), 1267 InstanceId: "i-was", 1268 Nonce: "fake_nonce", 1269 }, { 1270 Tag: s.machines[1].Tag().String(), 1271 InstanceId: "i-will", 1272 Nonce: "fake_nonce", 1273 Characteristics: &hwChars, 1274 }, { 1275 Tag: s.machines[2].Tag().String(), 1276 InstanceId: "i-am-too", 1277 Nonce: "fake", 1278 Characteristics: nil, 1279 }, { 1280 Tag: volumesMachine.Tag().String(), 1281 InstanceId: "i-am-also", 1282 Nonce: "fake", 1283 Volumes: []params.Volume{{ 1284 VolumeTag: "volume-0", 1285 Info: params.VolumeInfo{ 1286 VolumeId: "vol-0", 1287 Size: 1234, 1288 }, 1289 }}, 1290 VolumeAttachments: map[string]params.VolumeAttachmentInfo{ 1291 "volume-0": { 1292 DeviceName: "sda", 1293 }, 1294 }, 1295 }, 1296 {Tag: "machine-42"}, 1297 {Tag: "unit-foo-0"}, 1298 {Tag: "application-bar"}, 1299 }} 1300 result, err := s.provisioner.SetInstanceInfo(args) 1301 c.Assert(err, jc.ErrorIsNil) 1302 c.Assert(result, jc.DeepEquals, params.ErrorResults{ 1303 Results: []params.ErrorResult{ 1304 {¶ms.Error{ 1305 Message: `cannot record provisioning info for "i-was": cannot set instance data for machine "0": already set`, 1306 }}, 1307 {nil}, 1308 {nil}, 1309 {nil}, 1310 {apiservertesting.NotFoundError("machine 42")}, 1311 {apiservertesting.ErrUnauthorized}, 1312 {apiservertesting.ErrUnauthorized}, 1313 }, 1314 }) 1315 1316 // Verify machine 1 and 2 were provisioned. 1317 c.Assert(s.machines[1].Refresh(), gc.IsNil) 1318 c.Assert(s.machines[2].Refresh(), gc.IsNil) 1319 1320 instanceId, err := s.machines[1].InstanceId() 1321 c.Assert(err, jc.ErrorIsNil) 1322 c.Check(instanceId, gc.Equals, instance.Id("i-will")) 1323 instanceId, err = s.machines[2].InstanceId() 1324 c.Assert(err, jc.ErrorIsNil) 1325 c.Check(instanceId, gc.Equals, instance.Id("i-am-too")) 1326 c.Check(s.machines[1].CheckProvisioned("fake_nonce"), jc.IsTrue) 1327 c.Check(s.machines[2].CheckProvisioned("fake"), jc.IsTrue) 1328 gotHardware, err := s.machines[1].HardwareCharacteristics() 1329 c.Assert(err, jc.ErrorIsNil) 1330 c.Check(gotHardware, gc.DeepEquals, &hwChars) 1331 1332 // Verify the machine with requested volumes was provisioned, and the 1333 // volume information recorded in state. 1334 sb, err := state.NewStorageBackend(s.State) 1335 c.Assert(err, jc.ErrorIsNil) 1336 volumeAttachments, err := sb.MachineVolumeAttachments(volumesMachine.MachineTag()) 1337 c.Assert(err, jc.ErrorIsNil) 1338 c.Assert(volumeAttachments, gc.HasLen, 1) 1339 volumeAttachmentInfo, err := volumeAttachments[0].Info() 1340 c.Assert(err, jc.ErrorIsNil) 1341 c.Assert(volumeAttachmentInfo, gc.Equals, state.VolumeAttachmentInfo{DeviceName: "sda"}) 1342 volume, err := sb.Volume(volumeAttachments[0].Volume()) 1343 c.Assert(err, jc.ErrorIsNil) 1344 volumeInfo, err := volume.Info() 1345 c.Assert(err, jc.ErrorIsNil) 1346 c.Assert(volumeInfo, gc.Equals, state.VolumeInfo{VolumeId: "vol-0", Pool: "static-pool", Size: 1234}) 1347 1348 // Verify the machine without requested volumes still has no volume 1349 // attachments recorded in state. 1350 volumeAttachments, err = sb.MachineVolumeAttachments(s.machines[1].MachineTag()) 1351 c.Assert(err, jc.ErrorIsNil) 1352 c.Assert(volumeAttachments, gc.HasLen, 0) 1353 } 1354 1355 func (s *withoutControllerSuite) TestInstanceId(c *gc.C) { 1356 // Provision 2 machines first. 1357 err := s.machines[0].SetProvisioned("i-am", "", "fake_nonce", nil) 1358 c.Assert(err, jc.ErrorIsNil) 1359 hwChars := instance.MustParseHardware("arch=i386", "mem=4G") 1360 err = s.machines[1].SetProvisioned("i-am-not", "", "fake_nonce", &hwChars) 1361 c.Assert(err, jc.ErrorIsNil) 1362 1363 args := params.Entities{Entities: []params.Entity{ 1364 {Tag: s.machines[0].Tag().String()}, 1365 {Tag: s.machines[1].Tag().String()}, 1366 {Tag: s.machines[2].Tag().String()}, 1367 {Tag: "machine-42"}, 1368 {Tag: "unit-foo-0"}, 1369 {Tag: "application-bar"}, 1370 }} 1371 result, err := s.provisioner.InstanceId(args) 1372 c.Assert(err, jc.ErrorIsNil) 1373 c.Assert(result, gc.DeepEquals, params.StringResults{ 1374 Results: []params.StringResult{ 1375 {Result: "i-am"}, 1376 {Result: "i-am-not"}, 1377 {Error: apiservertesting.NotProvisionedError("2")}, 1378 {Error: apiservertesting.NotFoundError("machine 42")}, 1379 {Error: apiservertesting.ErrUnauthorized}, 1380 {Error: apiservertesting.ErrUnauthorized}, 1381 }, 1382 }) 1383 } 1384 1385 func (s *withoutControllerSuite) TestWatchModelMachines(c *gc.C) { 1386 c.Assert(s.resources.Count(), gc.Equals, 0) 1387 1388 got, err := s.provisioner.WatchModelMachines() 1389 c.Assert(err, jc.ErrorIsNil) 1390 want := params.StringsWatchResult{ 1391 StringsWatcherId: "1", 1392 Changes: []string{"0", "1", "2", "3", "4"}, 1393 } 1394 c.Assert(got.StringsWatcherId, gc.Equals, want.StringsWatcherId) 1395 c.Assert(got.Changes, jc.SameContents, want.Changes) 1396 1397 // Verify the resources were registered and stop them when done. 1398 c.Assert(s.resources.Count(), gc.Equals, 1) 1399 resource := s.resources.Get("1") 1400 defer statetesting.AssertStop(c, resource) 1401 1402 // Check that the Watch has consumed the initial event ("returned" 1403 // in the Watch call) 1404 wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher)) 1405 wc.AssertNoChange() 1406 1407 // Make sure WatchModelMachines fails with a machine agent login. 1408 anAuthorizer := s.authorizer 1409 anAuthorizer.Tag = names.NewMachineTag("1") 1410 anAuthorizer.Controller = false 1411 aProvisioner, err := provisioner.NewProvisionerAPI(s.State, s.resources, anAuthorizer) 1412 c.Assert(err, jc.ErrorIsNil) 1413 1414 result, err := aProvisioner.WatchModelMachines() 1415 c.Assert(err, gc.ErrorMatches, "permission denied") 1416 c.Assert(result, gc.DeepEquals, params.StringsWatchResult{}) 1417 } 1418 1419 func (s *provisionerSuite) getManagerConfig(c *gc.C, typ instance.ContainerType) map[string]string { 1420 args := params.ContainerManagerConfigParams{Type: typ} 1421 results, err := s.provisioner.ContainerManagerConfig(args) 1422 c.Assert(err, jc.ErrorIsNil) 1423 return results.ManagerConfig 1424 } 1425 1426 func (s *withoutControllerSuite) TestContainerManagerConfigDefaults(c *gc.C) { 1427 cfg := s.getManagerConfig(c, instance.KVM) 1428 c.Assert(cfg, jc.DeepEquals, map[string]string{ 1429 container.ConfigModelUUID: coretesting.ModelTag.Id(), 1430 config.ContainerImageStreamKey: "released", 1431 }) 1432 } 1433 1434 type withImageMetadataSuite struct { 1435 provisionerSuite 1436 } 1437 1438 var _ = gc.Suite(&withImageMetadataSuite{}) 1439 1440 func (s *withImageMetadataSuite) SetUpTest(c *gc.C) { 1441 s.ConfigAttrs = map[string]interface{}{ 1442 config.ContainerImageStreamKey: "daily", 1443 config.ContainerImageMetadataURLKey: "https://images.linuxcontainers.org/", 1444 } 1445 s.setUpTest(c, false) 1446 } 1447 1448 func (s *withImageMetadataSuite) TestContainerManagerConfigImageMetadata(c *gc.C) { 1449 cfg := s.getManagerConfig(c, instance.LXD) 1450 c.Assert(cfg, jc.DeepEquals, map[string]string{ 1451 container.ConfigModelUUID: coretesting.ModelTag.Id(), 1452 config.ContainerImageStreamKey: "daily", 1453 config.ContainerImageMetadataURLKey: "https://images.linuxcontainers.org/", 1454 }) 1455 } 1456 func (s *withoutControllerSuite) TestContainerConfig(c *gc.C) { 1457 attrs := map[string]interface{}{ 1458 "juju-http-proxy": "http://proxy.example.com:9000", 1459 "apt-https-proxy": "https://proxy.example.com:9000", 1460 "allow-lxd-loop-mounts": true, 1461 "apt-mirror": "http://example.mirror.com", 1462 "cloudinit-userdata": validCloudInitUserData, 1463 "container-inherit-properties": "ca-certs,apt-primary", 1464 } 1465 err := s.Model.UpdateModelConfig(attrs, nil) 1466 c.Assert(err, jc.ErrorIsNil) 1467 expectedAPTProxy := proxy.Settings{ 1468 Http: "http://proxy.example.com:9000", 1469 Https: "https://proxy.example.com:9000", 1470 NoProxy: "127.0.0.1,localhost,::1", 1471 } 1472 1473 expectedProxy := proxy.Settings{ 1474 Http: "http://proxy.example.com:9000", 1475 NoProxy: "127.0.0.1,localhost,::1", 1476 } 1477 1478 results, err := s.provisioner.ContainerConfig() 1479 c.Check(err, jc.ErrorIsNil) 1480 c.Check(results.UpdateBehavior, gc.Not(gc.IsNil)) 1481 c.Check(results.ProviderType, gc.Equals, "dummy") 1482 c.Check(results.AuthorizedKeys, gc.Equals, s.Environ.Config().AuthorizedKeys()) 1483 c.Check(results.SSLHostnameVerification, jc.IsTrue) 1484 c.Check(results.LegacyProxy.HasProxySet(), jc.IsFalse) 1485 c.Check(results.JujuProxy, gc.DeepEquals, expectedProxy) 1486 c.Check(results.AptProxy, gc.DeepEquals, expectedAPTProxy) 1487 c.Check(results.AptMirror, gc.DeepEquals, "http://example.mirror.com") 1488 c.Check(results.CloudInitUserData, gc.DeepEquals, map[string]interface{}{ 1489 "packages": []interface{}{"python-keystoneclient", "python-glanceclient"}, 1490 "preruncmd": []interface{}{"mkdir /tmp/preruncmd", "mkdir /tmp/preruncmd2"}, 1491 "postruncmd": []interface{}{"mkdir /tmp/postruncmd", "mkdir /tmp/postruncmd2"}, 1492 "package_upgrade": false}) 1493 c.Check(results.ContainerInheritProperties, gc.DeepEquals, "ca-certs,apt-primary") 1494 } 1495 1496 func (s *withoutControllerSuite) TestContainerConfigLegacy(c *gc.C) { 1497 attrs := map[string]interface{}{ 1498 "http-proxy": "http://proxy.example.com:9000", 1499 "apt-https-proxy": "https://proxy.example.com:9000", 1500 "allow-lxd-loop-mounts": true, 1501 "apt-mirror": "http://example.mirror.com", 1502 "cloudinit-userdata": validCloudInitUserData, 1503 "container-inherit-properties": "ca-certs,apt-primary", 1504 } 1505 err := s.Model.UpdateModelConfig(attrs, nil) 1506 c.Assert(err, jc.ErrorIsNil) 1507 expectedAPTProxy := proxy.Settings{ 1508 Http: "http://proxy.example.com:9000", 1509 Https: "https://proxy.example.com:9000", 1510 NoProxy: "127.0.0.1,localhost,::1", 1511 } 1512 1513 expectedProxy := proxy.Settings{ 1514 Http: "http://proxy.example.com:9000", 1515 NoProxy: "127.0.0.1,localhost,::1", 1516 } 1517 1518 results, err := s.provisioner.ContainerConfig() 1519 c.Check(err, jc.ErrorIsNil) 1520 c.Check(results.UpdateBehavior, gc.Not(gc.IsNil)) 1521 c.Check(results.ProviderType, gc.Equals, "dummy") 1522 c.Check(results.AuthorizedKeys, gc.Equals, s.Environ.Config().AuthorizedKeys()) 1523 c.Check(results.SSLHostnameVerification, jc.IsTrue) 1524 c.Check(results.LegacyProxy, gc.DeepEquals, expectedProxy) 1525 c.Check(results.JujuProxy.HasProxySet(), jc.IsFalse) 1526 c.Check(results.AptProxy, gc.DeepEquals, expectedAPTProxy) 1527 c.Check(results.AptMirror, gc.DeepEquals, "http://example.mirror.com") 1528 c.Check(results.CloudInitUserData, gc.DeepEquals, map[string]interface{}{ 1529 "packages": []interface{}{"python-keystoneclient", "python-glanceclient"}, 1530 "preruncmd": []interface{}{"mkdir /tmp/preruncmd", "mkdir /tmp/preruncmd2"}, 1531 "postruncmd": []interface{}{"mkdir /tmp/postruncmd", "mkdir /tmp/postruncmd2"}, 1532 "package_upgrade": false}) 1533 c.Check(results.ContainerInheritProperties, gc.DeepEquals, "ca-certs,apt-primary") 1534 } 1535 1536 func (s *withoutControllerSuite) TestContainerConfigV5(c *gc.C) { 1537 attrs := map[string]interface{}{ 1538 "http-proxy": "http://proxy.example.com:9000", 1539 "apt-https-proxy": "https://proxy.example.com:9000", 1540 "allow-lxd-loop-mounts": true, 1541 "apt-mirror": "http://example.mirror.com", 1542 "cloudinit-userdata": validCloudInitUserData, 1543 "container-inherit-properties": "ca-certs,apt-primary", 1544 } 1545 err := s.Model.UpdateModelConfig(attrs, nil) 1546 c.Assert(err, jc.ErrorIsNil) 1547 expectedAPTProxy := proxy.Settings{ 1548 Http: "http://proxy.example.com:9000", 1549 Https: "https://proxy.example.com:9000", 1550 NoProxy: "127.0.0.1,localhost,::1", 1551 } 1552 1553 expectedProxy := proxy.Settings{ 1554 Http: "http://proxy.example.com:9000", 1555 NoProxy: "127.0.0.1,localhost,::1", 1556 } 1557 1558 provisionerV5, err := provisioner.NewProvisionerAPIV5(s.State, s.resources, s.authorizer) 1559 c.Check(err, jc.ErrorIsNil) 1560 1561 var results params.ContainerConfigV5 1562 results, err = provisionerV5.ContainerConfig() 1563 c.Check(err, jc.ErrorIsNil) 1564 1565 c.Check(results.UpdateBehavior, gc.Not(gc.IsNil)) 1566 c.Check(results.ProviderType, gc.Equals, "dummy") 1567 c.Check(results.AuthorizedKeys, gc.Equals, s.Environ.Config().AuthorizedKeys()) 1568 c.Check(results.SSLHostnameVerification, jc.IsTrue) 1569 c.Check(results.Proxy, gc.DeepEquals, expectedProxy) 1570 c.Check(results.AptProxy, gc.DeepEquals, expectedAPTProxy) 1571 c.Check(results.AptMirror, gc.DeepEquals, "http://example.mirror.com") 1572 c.Check(results.CloudInitUserData, gc.DeepEquals, map[string]interface{}{ 1573 "packages": []interface{}{"python-keystoneclient", "python-glanceclient"}, 1574 "preruncmd": []interface{}{"mkdir /tmp/preruncmd", "mkdir /tmp/preruncmd2"}, 1575 "postruncmd": []interface{}{"mkdir /tmp/postruncmd", "mkdir /tmp/postruncmd2"}, 1576 "package_upgrade": false}) 1577 c.Check(results.ContainerInheritProperties, gc.DeepEquals, "ca-certs,apt-primary") 1578 } 1579 1580 func (s *withoutControllerSuite) TestSetSupportedContainers(c *gc.C) { 1581 args := params.MachineContainersParams{Params: []params.MachineContainers{{ 1582 MachineTag: "machine-0", 1583 ContainerTypes: []instance.ContainerType{instance.LXD}, 1584 }, { 1585 MachineTag: "machine-1", 1586 ContainerTypes: []instance.ContainerType{instance.LXD, instance.KVM}, 1587 }}} 1588 results, err := s.provisioner.SetSupportedContainers(args) 1589 c.Assert(err, jc.ErrorIsNil) 1590 c.Assert(results.Results, gc.HasLen, 2) 1591 for _, result := range results.Results { 1592 c.Assert(result.Error, gc.IsNil) 1593 } 1594 m0, err := s.State.Machine("0") 1595 c.Assert(err, jc.ErrorIsNil) 1596 containers, ok := m0.SupportedContainers() 1597 c.Assert(ok, jc.IsTrue) 1598 c.Assert(containers, gc.DeepEquals, []instance.ContainerType{instance.LXD}) 1599 m1, err := s.State.Machine("1") 1600 c.Assert(err, jc.ErrorIsNil) 1601 containers, ok = m1.SupportedContainers() 1602 c.Assert(ok, jc.IsTrue) 1603 c.Assert(containers, gc.DeepEquals, []instance.ContainerType{instance.LXD, instance.KVM}) 1604 } 1605 1606 func (s *withoutControllerSuite) TestSetSupportedContainersPermissions(c *gc.C) { 1607 // Login as a machine agent for machine 0. 1608 anAuthorizer := s.authorizer 1609 anAuthorizer.Controller = false 1610 anAuthorizer.Tag = s.machines[0].Tag() 1611 aProvisioner, err := provisioner.NewProvisionerAPI(s.State, s.resources, anAuthorizer) 1612 c.Assert(err, jc.ErrorIsNil) 1613 c.Assert(aProvisioner, gc.NotNil) 1614 1615 args := params.MachineContainersParams{ 1616 Params: []params.MachineContainers{{ 1617 MachineTag: "machine-0", 1618 ContainerTypes: []instance.ContainerType{instance.LXD}, 1619 }, { 1620 MachineTag: "machine-1", 1621 ContainerTypes: []instance.ContainerType{instance.LXD}, 1622 }, { 1623 MachineTag: "machine-42", 1624 ContainerTypes: []instance.ContainerType{instance.LXD}, 1625 }, 1626 }, 1627 } 1628 // Only machine 0 can have it's containers updated. 1629 results, err := aProvisioner.SetSupportedContainers(args) 1630 c.Assert(results, gc.DeepEquals, params.ErrorResults{ 1631 Results: []params.ErrorResult{ 1632 {Error: nil}, 1633 {Error: apiservertesting.ErrUnauthorized}, 1634 {Error: apiservertesting.ErrUnauthorized}, 1635 }, 1636 }) 1637 } 1638 1639 func (s *withoutControllerSuite) TestSupportsNoContainers(c *gc.C) { 1640 args := params.MachineContainersParams{ 1641 Params: []params.MachineContainers{ 1642 { 1643 MachineTag: "machine-0", 1644 }, 1645 }, 1646 } 1647 results, err := s.provisioner.SetSupportedContainers(args) 1648 c.Assert(err, jc.ErrorIsNil) 1649 c.Assert(results.Results, gc.HasLen, 1) 1650 c.Assert(results.Results[0].Error, gc.IsNil) 1651 m0, err := s.State.Machine("0") 1652 c.Assert(err, jc.ErrorIsNil) 1653 containers, ok := m0.SupportedContainers() 1654 c.Assert(ok, jc.IsTrue) 1655 c.Assert(containers, gc.DeepEquals, []instance.ContainerType{}) 1656 } 1657 1658 var _ = gc.Suite(&withControllerSuite{}) 1659 1660 type withControllerSuite struct { 1661 provisionerSuite 1662 } 1663 1664 func (s *withControllerSuite) SetUpTest(c *gc.C) { 1665 s.provisionerSuite.setUpTest(c, true) 1666 } 1667 1668 func (s *withControllerSuite) TestAPIAddresses(c *gc.C) { 1669 hostPorts := [][]network.HostPort{ 1670 network.NewHostPorts(1234, "0.1.2.3"), 1671 } 1672 err := s.State.SetAPIHostPorts(hostPorts) 1673 c.Assert(err, jc.ErrorIsNil) 1674 1675 result, err := s.provisioner.APIAddresses() 1676 c.Assert(err, jc.ErrorIsNil) 1677 c.Assert(result, gc.DeepEquals, params.StringsResult{ 1678 Result: []string{"0.1.2.3:1234"}, 1679 }) 1680 } 1681 1682 func (s *withControllerSuite) TestStateAddresses(c *gc.C) { 1683 addresses, err := s.State.Addresses() 1684 c.Assert(err, jc.ErrorIsNil) 1685 1686 result, err := s.provisioner.StateAddresses() 1687 c.Assert(err, jc.ErrorIsNil) 1688 c.Assert(result, gc.DeepEquals, params.StringsResult{ 1689 Result: addresses, 1690 }) 1691 } 1692 1693 func (s *withControllerSuite) TestCACert(c *gc.C) { 1694 result, err := s.provisioner.CACert() 1695 c.Assert(err, jc.ErrorIsNil) 1696 c.Assert(result, gc.DeepEquals, params.BytesResult{ 1697 Result: []byte(coretesting.CACert), 1698 }) 1699 } 1700 1701 func (s *withoutControllerSuite) TestWatchMachineErrorRetry(c *gc.C) { 1702 s.WaitForModelWatchersIdle(c, s.Model.UUID()) 1703 s.PatchValue(&provisioner.ErrorRetryWaitDelay, 2*coretesting.ShortWait) 1704 c.Assert(s.resources.Count(), gc.Equals, 0) 1705 1706 _, err := s.provisioner.WatchMachineErrorRetry() 1707 c.Assert(err, jc.ErrorIsNil) 1708 1709 // Verify the resources were registered and stop them when done. 1710 c.Assert(s.resources.Count(), gc.Equals, 1) 1711 resource := s.resources.Get("1") 1712 defer statetesting.AssertStop(c, resource) 1713 1714 // Check that the Watch has consumed the initial event ("returned" 1715 // in the Watch call) 1716 wc := statetesting.NewNotifyWatcherC(c, s.State, resource.(state.NotifyWatcher)) 1717 wc.AssertNoChange() 1718 1719 // We should now get a time triggered change. 1720 wc.AssertOneChange() 1721 1722 // Make sure WatchMachineErrorRetry fails with a machine agent login. 1723 anAuthorizer := s.authorizer 1724 anAuthorizer.Tag = names.NewMachineTag("1") 1725 anAuthorizer.Controller = false 1726 aProvisioner, err := provisioner.NewProvisionerAPI(s.State, s.resources, anAuthorizer) 1727 c.Assert(err, jc.ErrorIsNil) 1728 1729 result, err := aProvisioner.WatchMachineErrorRetry() 1730 c.Assert(err, gc.ErrorMatches, "permission denied") 1731 c.Assert(result, gc.DeepEquals, params.NotifyWatchResult{}) 1732 } 1733 1734 func (s *withoutControllerSuite) TestFindTools(c *gc.C) { 1735 args := params.FindToolsParams{ 1736 MajorVersion: -1, 1737 MinorVersion: -1, 1738 } 1739 result, err := s.provisioner.FindTools(args) 1740 c.Assert(err, jc.ErrorIsNil) 1741 c.Assert(result.Error, gc.IsNil) 1742 c.Assert(result.List, gc.Not(gc.HasLen), 0) 1743 for _, tools := range result.List { 1744 url := fmt.Sprintf("https://%s/model/%s/tools/%s", 1745 s.APIState.Addr(), coretesting.ModelTag.Id(), tools.Version) 1746 c.Assert(tools.URL, gc.Equals, url) 1747 } 1748 } 1749 1750 func (s *withoutControllerSuite) TestMarkMachinesForRemoval(c *gc.C) { 1751 err := s.machines[0].EnsureDead() 1752 c.Assert(err, jc.ErrorIsNil) 1753 err = s.machines[2].EnsureDead() 1754 c.Assert(err, jc.ErrorIsNil) 1755 1756 res, err := s.provisioner.MarkMachinesForRemoval(params.Entities{ 1757 Entities: []params.Entity{ 1758 {Tag: "machine-2"}, // ok 1759 {Tag: "machine-100"}, // not found 1760 {Tag: "machine-0"}, // ok 1761 {Tag: "machine-1"}, // not dead 1762 {Tag: "machine-0-lxd-5"}, // unauthorised 1763 {Tag: "application-thing"}, // only machines allowed 1764 }, 1765 }) 1766 c.Assert(err, jc.ErrorIsNil) 1767 results := res.Results 1768 c.Assert(results, gc.HasLen, 6) 1769 c.Check(results[0].Error, gc.IsNil) 1770 c.Check(*results[1].Error, gc.Equals, 1771 *common.ServerError(errors.NotFoundf("machine 100"))) 1772 c.Check(*results[1].Error, jc.Satisfies, params.IsCodeNotFound) 1773 c.Check(results[2].Error, gc.IsNil) 1774 c.Check(*results[3].Error, gc.Equals, 1775 *common.ServerError(errors.New("cannot remove machine 1: machine is not dead"))) 1776 c.Check(*results[4].Error, gc.Equals, *apiservertesting.ErrUnauthorized) 1777 c.Check(*results[5].Error, gc.Equals, 1778 *common.ServerError(errors.New(`"application-thing" is not a valid machine tag`))) 1779 1780 removals, err := s.State.AllMachineRemovals() 1781 c.Assert(err, jc.ErrorIsNil) 1782 c.Check(removals, jc.SameContents, []string{"0", "2"}) 1783 } 1784 1785 // TODO(jam): 2017-02-15 We seem to be lacking most of direct unit tests around ProcessOneContainer 1786 // Some of the use cases we need to be testing are: 1787 // 1) Provider can allocate addresses, should result in a container with 1788 // addresses requested from the provider, and 'static' configuration on those 1789 // devices. 1790 // 2) Provider cannot allocate addresses, currently this should make us use 1791 // 'lxdbr0' and DHCP allocated addresses. 1792 // 3) Provider could allocate DHCP based addresses on the host device, which would let us 1793 // use a bridge on the device and DHCP. (Currently not supported, but desirable for 1794 // vSphere and Manual and probably LXD providers.) 1795 // Addition (manadart 2018-10-09): To begin accommodating the deficiencies noted 1796 // above, the new suite below uses mocks for tests ill-suited to the dummy 1797 // provider. We could reasonably re-write the tests above over time to use the 1798 // new suite. 1799 1800 type provisionerMockSuite struct { 1801 coretesting.BaseSuite 1802 1803 environ *environtesting.MockNetworkingEnviron 1804 host *mocks.MockMachine 1805 container *mocks.MockMachine 1806 device *mocks.MockLinkLayerDevice 1807 parentDevice *mocks.MockLinkLayerDevice 1808 1809 unit *mocks.MockUnit 1810 application *mocks.MockApplication 1811 charm *mocks.MockCharm 1812 } 1813 1814 var _ = gc.Suite(&provisionerMockSuite{}) 1815 1816 // Even when the provider supports container addresses, manually provisioned 1817 // machines should fall back to DHCP. 1818 func (s *provisionerMockSuite) TestManuallyProvisionedHostsUseDHCPForContainers(c *gc.C) { 1819 defer s.setup(c).Finish() 1820 1821 s.expectManuallyProvisionedHostsUseDHCPForContainers() 1822 1823 res := params.MachineNetworkConfigResults{ 1824 Results: []params.MachineNetworkConfigResult{{}}, 1825 } 1826 ctx := provisioner.NewPrepareOrGetContext(res, false) 1827 1828 // ProviderCallContext is not required by this logical path; we pass nil. 1829 err := ctx.ProcessOneContainer(s.environ, nil, 0, s.host, s.container) 1830 c.Assert(err, jc.ErrorIsNil) 1831 c.Assert(res.Results[0].Config, gc.HasLen, 1) 1832 1833 cfg := res.Results[0].Config[0] 1834 c.Check(cfg.ConfigType, gc.Equals, "dhcp") 1835 c.Check(cfg.ProviderSubnetId, gc.Equals, "") 1836 c.Check(cfg.VLANTag, gc.Equals, 0) 1837 } 1838 1839 func (s *provisionerMockSuite) expectManuallyProvisionedHostsUseDHCPForContainers() { 1840 s.expectNetworkingEnviron() 1841 s.expectLinkLayerDevices() 1842 1843 emptySpace := "" 1844 1845 cExp := s.container.EXPECT() 1846 cExp.InstanceId().Return(instance.UnknownId, errors.NotProvisionedf("idk-lol")) 1847 cExp.DesiredSpaces().Return(set.NewStrings(emptySpace), nil) 1848 cExp.Id().Return("lxd/0").AnyTimes() 1849 cExp.SetLinkLayerDevices(gomock.Any()).Return(nil) 1850 cExp.AllLinkLayerDevices().Return([]containerizer.LinkLayerDevice{s.device}, nil) 1851 1852 hExp := s.host.EXPECT() 1853 hExp.Id().Return("0").AnyTimes() 1854 hExp.LinkLayerDevicesForSpaces(gomock.Any()).Return( 1855 map[string][]containerizer.LinkLayerDevice{emptySpace: {s.device}}, nil) 1856 // Crucial behavioural trait. Set false to test failure. 1857 hExp.IsManual().Return(true, nil) 1858 hExp.InstanceId().Return(instance.Id("manual:10.0.0.66"), nil) 1859 1860 } 1861 1862 // expectNetworkingEnviron stubs an environ that supports container networking. 1863 func (s *provisionerMockSuite) expectNetworkingEnviron() { 1864 eExp := s.environ.EXPECT() 1865 eExp.Config().Return(&config.Config{}).AnyTimes() 1866 eExp.SupportsContainerAddresses(gomock.Any()).Return(true, nil).AnyTimes() 1867 } 1868 1869 // expectLinkLayerDevices mocks a link-layer device and its parent, 1870 // suitable for use as a bridge network for containers. 1871 func (s *provisionerMockSuite) expectLinkLayerDevices() { 1872 devName := "eth0" 1873 mtu := uint(1500) 1874 mac := network.GenerateVirtualMACAddress() 1875 deviceArgs := state.LinkLayerDeviceArgs{ 1876 Name: devName, 1877 Type: state.EthernetDevice, 1878 MACAddress: mac, 1879 MTU: mtu, 1880 } 1881 1882 dExp := s.device.EXPECT() 1883 dExp.Name().Return(devName).AnyTimes() 1884 dExp.Type().Return(state.BridgeDevice).AnyTimes() 1885 dExp.MTU().Return(mtu).AnyTimes() 1886 dExp.EthernetDeviceForBridge(devName).Return(deviceArgs, nil).MinTimes(1) 1887 dExp.ParentDevice().Return(s.parentDevice, nil) 1888 dExp.MACAddress().Return(mac) 1889 dExp.IsAutoStart().Return(true) 1890 dExp.IsUp().Return(true) 1891 1892 pExp := s.parentDevice.EXPECT() 1893 // The address itself is unimportant, so we can use an empty one. 1894 // What is important is that there is one there to flex the path we are 1895 // testing. 1896 pExp.Addresses().Return([]*state.Address{{}}, nil) 1897 pExp.Name().Return(devName).MinTimes(1) 1898 } 1899 1900 func (s *provisionerMockSuite) TestContainerAlreadyProvisionedError(c *gc.C) { 1901 defer s.setup(c).Finish() 1902 1903 exp := s.container.EXPECT() 1904 exp.InstanceId().Return(instance.Id("juju-8ebd6c-0"), nil) 1905 exp.Id().Return("0/lxd/0") 1906 1907 res := params.MachineNetworkConfigResults{ 1908 Results: []params.MachineNetworkConfigResult{{}}, 1909 } 1910 ctx := provisioner.NewPrepareOrGetContext(res, true) 1911 1912 // ProviderCallContext is not required by this logical path; we pass nil. 1913 err := ctx.ProcessOneContainer(s.environ, nil, 0, s.host, s.container) 1914 c.Assert(err, gc.ErrorMatches, `container "0/lxd/0" already provisioned as "juju-8ebd6c-0"`) 1915 } 1916 1917 func (s *provisionerMockSuite) TestGetContainerProfileInfo(c *gc.C) { 1918 ctrl := s.setup(c) 1919 defer ctrl.Finish() 1920 s.expectCharmLXDProfiles(ctrl) 1921 1922 s.application.EXPECT().Name().Return("application") 1923 s.charm.EXPECT().Revision().Return(3) 1924 s.charm.EXPECT().LXDProfile().Return( 1925 &charm.LXDProfile{ 1926 Config: map[string]string{ 1927 "security.nesting": "true", 1928 "security.privileged": "true", 1929 }, 1930 }) 1931 1932 res := params.ContainerProfileResults{ 1933 Results: []params.ContainerProfileResult{{}}, 1934 } 1935 ctx := provisioner.NewContainerProfileContext(res, "testme") 1936 1937 // ProviderCallContext is not required by this logical path; we pass nil. 1938 err := ctx.ProcessOneContainer(s.environ, nil, 0, s.host, s.container) 1939 c.Assert(err, jc.ErrorIsNil) 1940 c.Assert(res.Results, gc.HasLen, 1) 1941 c.Assert(res.Results[0].Error, gc.IsNil) 1942 c.Assert(res.Results[0].LXDProfiles, gc.HasLen, 1) 1943 profile := res.Results[0].LXDProfiles[0] 1944 c.Check(profile.Name, gc.Equals, "juju-testme-application-3") 1945 c.Check(profile.Profile.Config, gc.DeepEquals, 1946 map[string]string{ 1947 "security.nesting": "true", 1948 "security.privileged": "true", 1949 }, 1950 ) 1951 } 1952 1953 func (s *provisionerMockSuite) TestGetContainerProfileInfoNoProfile(c *gc.C) { 1954 ctrl := s.setup(c) 1955 defer ctrl.Finish() 1956 s.expectCharmLXDProfiles(ctrl) 1957 1958 s.charm.EXPECT().LXDProfile().Return(nil) 1959 s.unit.EXPECT().Name().Return("application/0") 1960 1961 res := params.ContainerProfileResults{ 1962 Results: []params.ContainerProfileResult{{}}, 1963 } 1964 ctx := provisioner.NewContainerProfileContext(res, "testme") 1965 1966 // ProviderCallContext is not required by this logical path; we pass nil. 1967 err := ctx.ProcessOneContainer(s.environ, nil, 0, s.host, s.container) 1968 c.Assert(err, jc.ErrorIsNil) 1969 c.Assert(res.Results, gc.HasLen, 1) 1970 c.Assert(res.Results[0].Error, gc.IsNil) 1971 c.Assert(res.Results[0].LXDProfiles, gc.HasLen, 0) 1972 } 1973 1974 func (s *provisionerMockSuite) expectCharmLXDProfiles(ctrl *gomock.Controller) { 1975 s.unit = mocks.NewMockUnit(ctrl) 1976 s.application = mocks.NewMockApplication(ctrl) 1977 s.charm = mocks.NewMockCharm(ctrl) 1978 1979 s.container.EXPECT().Units().Return([]containerizer.Unit{s.unit}, nil) 1980 s.unit.EXPECT().Application().Return(s.application, nil) 1981 s.application.EXPECT().Charm().Return(s.charm, false, nil) 1982 } 1983 1984 func (s *provisionerMockSuite) setup(c *gc.C) *gomock.Controller { 1985 ctrl := gomock.NewController(c) 1986 1987 s.environ = environtesting.NewMockNetworkingEnviron(ctrl) 1988 s.host = mocks.NewMockMachine(ctrl) 1989 s.container = mocks.NewMockMachine(ctrl) 1990 s.device = mocks.NewMockLinkLayerDevice(ctrl) 1991 s.parentDevice = mocks.NewMockLinkLayerDevice(ctrl) 1992 1993 return ctrl 1994 } 1995 1996 type provisionerProfileMockSuite struct { 1997 coretesting.BaseSuite 1998 1999 backend *mocks.MockProfileBackend 2000 charm *mocks.MockProfileCharm 2001 machine *mocks.MockProfileMachine 2002 } 2003 2004 var _ = gc.Suite(&provisionerProfileMockSuite{}) 2005 2006 func (s *provisionerProfileMockSuite) TestMachineChangeProfileChangeInfoRemoveUnit(c *gc.C) { 2007 defer s.setup(c).Finish() 2008 2009 mExp := s.machine.EXPECT() 2010 mExp.CharmProfiles().Return([]string{ 2011 "default", 2012 "juju-testme", 2013 "juju-testme-lxd-profile-alt-2", 2014 "juju-testme-application-1", 2015 }, nil) 2016 mExp.UpgradeCharmProfileCharmURL().Return("", nil) 2017 mExp.UpgradeCharmProfileApplication().Return("lxd-profile-alt", nil) 2018 2019 result, err := provisioner.MachineChangeProfileChangeInfo(s.machine, s.backend) 2020 c.Assert(err, gc.IsNil) 2021 c.Assert(result.Error, gc.IsNil) 2022 c.Assert(result.OldProfileName, gc.Equals, "juju-testme-lxd-profile-alt-2") 2023 } 2024 2025 func (s *provisionerProfileMockSuite) TestMachineChangeProfileChangeInfoRemoveProfile(c *gc.C) { 2026 defer s.setup(c).Finish() 2027 2028 charmURLString := "local:bionic/lxd-profile-alt-3" 2029 mExp := s.machine.EXPECT() 2030 mExp.CharmProfiles().Return([]string{ 2031 "default", 2032 "juju-testme", 2033 "juju-testme-lxd-profile-alt-2", 2034 "juju-testme-application-1", 2035 }, nil) 2036 mExp.UpgradeCharmProfileCharmURL().Return(charmURLString, nil) 2037 mExp.UpgradeCharmProfileApplication().Return("lxd-profile-alt", nil) 2038 mExp.Id().Return("2") 2039 2040 cExp := s.charm.EXPECT() 2041 cExp.Revision().Return(3) 2042 cExp.LXDProfile().Return(&charm.LXDProfile{}) 2043 cExp.Meta().Return(&charm.Meta{Subordinate: false}) 2044 2045 chURL, err := charm.ParseURL(charmURLString) 2046 c.Assert(err, jc.ErrorIsNil) 2047 s.backend.EXPECT().Charm(gomock.Eq(chURL)).Return(s.charm, nil) 2048 2049 result, err := provisioner.MachineChangeProfileChangeInfo(s.machine, s.backend) 2050 c.Assert(err, gc.IsNil) 2051 c.Assert(result.Error, gc.IsNil) 2052 c.Assert(result.OldProfileName, gc.Equals, "juju-testme-lxd-profile-alt-2") 2053 c.Assert(result.NewProfileName, gc.Equals, "") 2054 } 2055 2056 func (s *provisionerProfileMockSuite) TestMachineChangeProfileChangeInfoAddProfile(c *gc.C) { 2057 defer s.setup(c).Finish() 2058 2059 charmURLString := "local:bionic/lxd-profile-alt-3" 2060 mExp := s.machine.EXPECT() 2061 mExp.CharmProfiles().Return([]string{ 2062 "default", 2063 "juju-testme", 2064 "juju-testme-application-1", 2065 }, nil) 2066 mExp.UpgradeCharmProfileCharmURL().Return(charmURLString, nil) 2067 mExp.UpgradeCharmProfileApplication().Return("lxd-profile-alt", nil) 2068 mExp.Id().Return("2") 2069 mExp.ModelName().Return("testme") 2070 2071 cExp := s.charm.EXPECT() 2072 cExp.Revision().Return(3) 2073 cExp.LXDProfile().Return(&charm.LXDProfile{ 2074 Config: map[string]string{"security.privilaged": "true"}, 2075 Description: "profile to test", 2076 }) 2077 cExp.Meta().Return(&charm.Meta{Subordinate: false}) 2078 2079 chURL, err := charm.ParseURL(charmURLString) 2080 c.Assert(err, jc.ErrorIsNil) 2081 s.backend.EXPECT().Charm(gomock.Eq(chURL)).Return(s.charm, nil) 2082 2083 result, err := provisioner.MachineChangeProfileChangeInfo(s.machine, s.backend) 2084 c.Assert(err, gc.IsNil) 2085 c.Assert(result.Error, gc.IsNil) 2086 c.Assert(result.NewProfileName, gc.Equals, "juju-testme-lxd-profile-alt-3") 2087 c.Assert(result.OldProfileName, gc.Equals, "") 2088 c.Assert(result.Profile, gc.DeepEquals, ¶ms.CharmLXDProfile{ 2089 Config: map[string]string{"security.privilaged": "true"}, 2090 Description: "profile to test", 2091 }) 2092 c.Assert(result.Subordinate, jc.IsFalse) 2093 } 2094 2095 func (s *provisionerProfileMockSuite) TestMachineChangeProfileChangeInfoChangeProfile(c *gc.C) { 2096 defer s.setup(c).Finish() 2097 2098 charmURLString := "local:bionic/lxd-profile-alt-3" 2099 mExp := s.machine.EXPECT() 2100 mExp.CharmProfiles().Return([]string{ 2101 "default", 2102 "juju-testme", 2103 "juju-testme-lxd-profile-alt-2", 2104 "juju-testme-application-1", 2105 }, nil) 2106 mExp.UpgradeCharmProfileCharmURL().Return(charmURLString, nil) 2107 mExp.UpgradeCharmProfileApplication().Return("lxd-profile-alt", nil) 2108 mExp.Id().Return("2") 2109 2110 cExp := s.charm.EXPECT() 2111 cExp.Revision().Return(3) 2112 cExp.LXDProfile().Return(&charm.LXDProfile{ 2113 Config: map[string]string{"security.privilaged": "true"}, 2114 Description: "profile to test", 2115 }) 2116 cExp.Meta().Return(&charm.Meta{Subordinate: true}) 2117 2118 chURL, err := charm.ParseURL(charmURLString) 2119 c.Assert(err, jc.ErrorIsNil) 2120 s.backend.EXPECT().Charm(gomock.Eq(chURL)).Return(s.charm, nil) 2121 2122 result, err := provisioner.MachineChangeProfileChangeInfo(s.machine, s.backend) 2123 c.Assert(err, gc.IsNil) 2124 c.Assert(result.Error, gc.IsNil) 2125 c.Assert(result.OldProfileName, gc.Equals, "juju-testme-lxd-profile-alt-2") 2126 c.Assert(result.NewProfileName, gc.Equals, "juju-testme-lxd-profile-alt-3") 2127 c.Assert(result.Profile, gc.DeepEquals, ¶ms.CharmLXDProfile{ 2128 Config: map[string]string{"security.privilaged": "true"}, 2129 Description: "profile to test", 2130 }) 2131 c.Assert(result.Subordinate, jc.IsTrue) 2132 } 2133 2134 func (s *provisionerProfileMockSuite) setup(c *gc.C) *gomock.Controller { 2135 ctrl := gomock.NewController(c) 2136 2137 s.backend = mocks.NewMockProfileBackend(ctrl) 2138 s.charm = mocks.NewMockProfileCharm(ctrl) 2139 s.machine = mocks.NewMockProfileMachine(ctrl) 2140 2141 return ctrl 2142 }