github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/facades/client/controller/controller_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package controller_test 5 6 import ( 7 stdcontext "context" 8 "encoding/json" 9 "regexp" 10 "time" 11 12 "github.com/juju/clock" 13 "github.com/juju/errors" 14 "github.com/juju/loggo" 15 "github.com/juju/names/v5" 16 "github.com/juju/pubsub/v2" 17 jc "github.com/juju/testing/checkers" 18 "github.com/juju/utils/v3" 19 "github.com/juju/version/v2" 20 "github.com/juju/worker/v3/workertest" 21 "github.com/kr/pretty" 22 "github.com/prometheus/client_golang/prometheus" 23 gc "gopkg.in/check.v1" 24 "gopkg.in/macaroon.v2" 25 26 "github.com/juju/juju/apiserver" 27 "github.com/juju/juju/apiserver/common" 28 "github.com/juju/juju/apiserver/facade/facadetest" 29 "github.com/juju/juju/apiserver/facades/client/controller" 30 apiservertesting "github.com/juju/juju/apiserver/testing" 31 "github.com/juju/juju/cloud" 32 corecontroller "github.com/juju/juju/controller" 33 "github.com/juju/juju/core/cache" 34 coremultiwatcher "github.com/juju/juju/core/multiwatcher" 35 "github.com/juju/juju/core/permission" 36 "github.com/juju/juju/docker" 37 "github.com/juju/juju/environs" 38 environscloudspec "github.com/juju/juju/environs/cloudspec" 39 "github.com/juju/juju/environs/config" 40 pscontroller "github.com/juju/juju/pubsub/controller" 41 "github.com/juju/juju/rpc/params" 42 "github.com/juju/juju/state" 43 statetesting "github.com/juju/juju/state/testing" 44 "github.com/juju/juju/testing" 45 "github.com/juju/juju/testing/factory" 46 "github.com/juju/juju/worker/gate" 47 "github.com/juju/juju/worker/modelcache" 48 "github.com/juju/juju/worker/multiwatcher" 49 ) 50 51 type controllerSuite struct { 52 statetesting.StateSuite 53 54 controller *controller.ControllerAPI 55 resources *common.Resources 56 authorizer apiservertesting.FakeAuthorizer 57 hub *pubsub.StructuredHub 58 context facadetest.Context 59 } 60 61 var _ = gc.Suite(&controllerSuite{}) 62 63 func (s *controllerSuite) SetUpTest(c *gc.C) { 64 // Initial config needs to be set before the StateSuite SetUpTest. 65 s.InitialConfig = testing.CustomModelConfig(c, testing.Attrs{ 66 "name": "controller", 67 }) 68 69 s.StateSuite.SetUpTest(c) 70 71 allWatcherBacking, err := state.NewAllWatcherBacking(s.StatePool) 72 c.Assert(err, jc.ErrorIsNil) 73 multiWatcherWorker, err := multiwatcher.NewWorker(multiwatcher.Config{ 74 Clock: clock.WallClock, 75 Logger: loggo.GetLogger("test"), 76 Backing: allWatcherBacking, 77 PrometheusRegisterer: noopRegisterer{}, 78 }) 79 c.Assert(err, jc.ErrorIsNil) 80 // The worker itself is a coremultiwatcher.Factory. 81 s.AddCleanup(func(c *gc.C) { workertest.CleanKill(c, multiWatcherWorker) }) 82 83 initialized := gate.NewLock() 84 s.hub = pubsub.NewStructuredHub(nil) 85 modelCache, err := modelcache.NewWorker(modelcache.Config{ 86 StatePool: s.StatePool, 87 Hub: s.hub, 88 InitializedGate: initialized, 89 Logger: loggo.GetLogger("test"), 90 WatcherFactory: multiWatcherWorker.WatchController, 91 PrometheusRegisterer: noopRegisterer{}, 92 Cleanup: func() {}, 93 }.WithDefaultRestartStrategy()) 94 c.Assert(err, jc.ErrorIsNil) 95 s.AddCleanup(func(c *gc.C) { workertest.CleanKill(c, modelCache) }) 96 97 select { 98 case <-initialized.Unlocked(): 99 case <-time.After(10 * time.Second): 100 c.Error("model cache not initialized after 10 seconds") 101 } 102 103 var cacheController *cache.Controller 104 err = modelcache.ExtractCacheController(modelCache, &cacheController) 105 c.Assert(err, jc.ErrorIsNil) 106 107 s.resources = common.NewResources() 108 s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() }) 109 110 s.authorizer = apiservertesting.FakeAuthorizer{ 111 Tag: s.Owner, 112 AdminTag: s.Owner, 113 } 114 115 s.context = facadetest.Context{ 116 State_: s.State, 117 StatePool_: s.StatePool, 118 Resources_: s.resources, 119 Auth_: s.authorizer, 120 Controller_: cacheController, 121 Hub_: s.hub, 122 MultiwatcherFactory_: multiWatcherWorker, 123 } 124 controller, err := controller.LatestAPI(s.context) 125 c.Assert(err, jc.ErrorIsNil) 126 s.controller = controller 127 128 loggo.GetLogger("juju.apiserver.controller").SetLogLevel(loggo.TRACE) 129 } 130 131 func (s *controllerSuite) TestNewAPIRefusesNonClient(c *gc.C) { 132 anAuthoriser := apiservertesting.FakeAuthorizer{ 133 Tag: names.NewUnitTag("mysql/0"), 134 } 135 endPoint, err := controller.LatestAPI( 136 facadetest.Context{ 137 State_: s.State, 138 StatePool_: s.StatePool, 139 Resources_: s.resources, 140 Auth_: anAuthoriser, 141 }) 142 c.Assert(endPoint, gc.IsNil) 143 c.Assert(err, gc.ErrorMatches, "permission denied") 144 } 145 146 func (s *controllerSuite) checkModelMatches(c *gc.C, model params.Model, expected *state.Model) { 147 c.Check(model.Name, gc.Equals, expected.Name()) 148 c.Check(model.UUID, gc.Equals, expected.UUID()) 149 c.Check(model.OwnerTag, gc.Equals, expected.Owner().String()) 150 } 151 152 func (s *controllerSuite) TestAllModels(c *gc.C) { 153 admin := s.Factory.MakeUser(c, &factory.UserParams{Name: "foobar"}) 154 155 s.Factory.MakeModel(c, &factory.ModelParams{ 156 Name: "owned", Owner: admin.UserTag()}).Close() 157 remoteUserTag := names.NewUserTag("user@remote") 158 st := s.Factory.MakeModel(c, &factory.ModelParams{ 159 Name: "user", Owner: remoteUserTag}) 160 defer st.Close() 161 model, err := st.Model() 162 c.Assert(err, jc.ErrorIsNil) 163 164 model.AddUser( 165 state.UserAccessSpec{ 166 User: admin.UserTag(), 167 CreatedBy: remoteUserTag, 168 DisplayName: "Foo Bar", 169 Access: permission.WriteAccess}) 170 171 s.Factory.MakeModel(c, &factory.ModelParams{ 172 Name: "no-access", Owner: remoteUserTag}).Close() 173 174 response, err := s.controller.AllModels() 175 c.Assert(err, jc.ErrorIsNil) 176 // The results are sorted. 177 expected := []string{"controller", "no-access", "owned", "user"} 178 var obtained []string 179 for _, userModel := range response.UserModels { 180 c.Assert(userModel.Type, gc.Equals, "iaas") 181 obtained = append(obtained, userModel.Name) 182 stateModel, ph, err := s.StatePool.GetModel(userModel.UUID) 183 c.Assert(err, jc.ErrorIsNil) 184 defer ph.Release() 185 s.checkModelMatches(c, userModel.Model, stateModel) 186 } 187 c.Assert(obtained, jc.DeepEquals, expected) 188 } 189 190 func (s *controllerSuite) TestHostedModelConfigs_OnlyHostedModelsReturned(c *gc.C) { 191 owner := s.Factory.MakeUser(c, nil) 192 s.Factory.MakeModel(c, &factory.ModelParams{ 193 Name: "first", Owner: owner.UserTag()}).Close() 194 remoteUserTag := names.NewUserTag("user@remote") 195 s.Factory.MakeModel(c, &factory.ModelParams{ 196 Name: "second", Owner: remoteUserTag}).Close() 197 198 results, err := s.controller.HostedModelConfigs() 199 c.Assert(err, jc.ErrorIsNil) 200 c.Assert(len(results.Models), gc.Equals, 2) 201 202 one := results.Models[0] 203 two := results.Models[1] 204 205 c.Assert(one.Name, gc.Equals, "first") 206 c.Assert(one.OwnerTag, gc.Equals, owner.UserTag().String()) 207 c.Assert(two.Name, gc.Equals, "second") 208 c.Assert(two.OwnerTag, gc.Equals, remoteUserTag.String()) 209 } 210 211 func (s *controllerSuite) makeCloudSpec(c *gc.C, pSpec *params.CloudSpec) environscloudspec.CloudSpec { 212 c.Assert(pSpec, gc.NotNil) 213 var credential *cloud.Credential 214 if pSpec.Credential != nil { 215 credentialValue := cloud.NewCredential( 216 cloud.AuthType(pSpec.Credential.AuthType), 217 pSpec.Credential.Attributes, 218 ) 219 credential = &credentialValue 220 } 221 spec := environscloudspec.CloudSpec{ 222 Type: pSpec.Type, 223 Name: pSpec.Name, 224 Region: pSpec.Region, 225 Endpoint: pSpec.Endpoint, 226 IdentityEndpoint: pSpec.IdentityEndpoint, 227 StorageEndpoint: pSpec.StorageEndpoint, 228 Credential: credential, 229 } 230 c.Assert(spec.Validate(), jc.ErrorIsNil) 231 return spec 232 } 233 234 func (s *controllerSuite) TestHostedModelConfigs_CanOpenEnviron(c *gc.C) { 235 owner := s.Factory.MakeUser(c, nil) 236 s.Factory.MakeModel(c, &factory.ModelParams{ 237 Name: "first", Owner: owner.UserTag()}).Close() 238 remoteUserTag := names.NewUserTag("user@remote") 239 s.Factory.MakeModel(c, &factory.ModelParams{ 240 Name: "second", Owner: remoteUserTag}).Close() 241 242 results, err := s.controller.HostedModelConfigs() 243 c.Assert(err, jc.ErrorIsNil) 244 c.Assert(len(results.Models), gc.Equals, 2) 245 246 for _, model := range results.Models { 247 c.Assert(model.Error, gc.IsNil) 248 249 cfg, err := config.New(config.NoDefaults, model.Config) 250 c.Assert(err, jc.ErrorIsNil) 251 spec := s.makeCloudSpec(c, model.CloudSpec) 252 _, err = environs.New(stdcontext.TODO(), environs.OpenParams{ 253 Cloud: spec, 254 Config: cfg, 255 }) 256 c.Assert(err, jc.ErrorIsNil) 257 } 258 } 259 260 func (s *controllerSuite) TestListBlockedModels(c *gc.C) { 261 st := s.Factory.MakeModel(c, &factory.ModelParams{ 262 Name: "test"}) 263 defer st.Close() 264 265 s.State.SwitchBlockOn(state.DestroyBlock, "TestBlockDestroyModel") 266 s.State.SwitchBlockOn(state.ChangeBlock, "TestChangeBlock") 267 st.SwitchBlockOn(state.DestroyBlock, "TestBlockDestroyModel") 268 st.SwitchBlockOn(state.ChangeBlock, "TestChangeBlock") 269 270 list, err := s.controller.ListBlockedModels() 271 c.Assert(err, jc.ErrorIsNil) 272 273 c.Assert(list.Models, jc.DeepEquals, []params.ModelBlockInfo{ 274 { 275 Name: "controller", 276 UUID: s.State.ModelUUID(), 277 OwnerTag: s.Owner.String(), 278 Blocks: []string{ 279 "BlockDestroy", 280 "BlockChange", 281 }, 282 }, 283 { 284 Name: "test", 285 UUID: st.ModelUUID(), 286 OwnerTag: s.Owner.String(), 287 Blocks: []string{ 288 "BlockDestroy", 289 "BlockChange", 290 }, 291 }, 292 }) 293 294 } 295 296 func (s *controllerSuite) TestListBlockedModelsNoBlocks(c *gc.C) { 297 list, err := s.controller.ListBlockedModels() 298 c.Assert(err, jc.ErrorIsNil) 299 c.Assert(list.Models, gc.HasLen, 0) 300 } 301 302 func (s *controllerSuite) TestModelConfig(c *gc.C) { 303 cfg, err := s.controller.ModelConfig() 304 c.Assert(err, jc.ErrorIsNil) 305 c.Assert(cfg.Config["name"], jc.DeepEquals, params.ConfigValue{Value: "controller"}) 306 } 307 308 func (s *controllerSuite) TestModelConfigFromNonController(c *gc.C) { 309 st := s.Factory.MakeModel(c, &factory.ModelParams{ 310 Name: "test"}) 311 defer st.Close() 312 313 authorizer := &apiservertesting.FakeAuthorizer{ 314 Tag: s.Owner, 315 AdminTag: s.Owner, 316 } 317 controller, err := controller.NewControllerAPIv11( 318 facadetest.Context{ 319 State_: st, 320 StatePool_: s.StatePool, 321 Resources_: common.NewResources(), 322 Auth_: authorizer, 323 }) 324 325 c.Assert(err, jc.ErrorIsNil) 326 cfg, err := controller.ModelConfig() 327 c.Assert(err, jc.ErrorIsNil) 328 c.Assert(cfg.Config["name"], jc.DeepEquals, params.ConfigValue{Value: "controller"}) 329 } 330 331 func (s *controllerSuite) TestControllerConfig(c *gc.C) { 332 cfg, err := s.controller.ControllerConfig() 333 c.Assert(err, jc.ErrorIsNil) 334 cfgFromDB, err := s.State.ControllerConfig() 335 c.Assert(err, jc.ErrorIsNil) 336 c.Assert(cfg.Config["controller-uuid"], gc.Equals, cfgFromDB.ControllerUUID()) 337 c.Assert(cfg.Config["state-port"], gc.Equals, cfgFromDB.StatePort()) 338 c.Assert(cfg.Config["api-port"], gc.Equals, cfgFromDB.APIPort()) 339 } 340 341 func (s *controllerSuite) TestControllerConfigFromNonController(c *gc.C) { 342 st := s.Factory.MakeModel(c, &factory.ModelParams{ 343 Name: "test"}) 344 defer st.Close() 345 346 authorizer := &apiservertesting.FakeAuthorizer{Tag: s.Owner} 347 controller, err := controller.NewControllerAPIv11( 348 facadetest.Context{ 349 State_: st, 350 StatePool_: s.StatePool, 351 Resources_: common.NewResources(), 352 Auth_: authorizer, 353 }) 354 c.Assert(err, jc.ErrorIsNil) 355 cfg, err := controller.ControllerConfig() 356 c.Assert(err, jc.ErrorIsNil) 357 cfgFromDB, err := s.State.ControllerConfig() 358 c.Assert(err, jc.ErrorIsNil) 359 c.Assert(cfg.Config["controller-uuid"], gc.Equals, cfgFromDB.ControllerUUID()) 360 c.Assert(cfg.Config["state-port"], gc.Equals, cfgFromDB.StatePort()) 361 c.Assert(cfg.Config["api-port"], gc.Equals, cfgFromDB.APIPort()) 362 } 363 364 func (s *controllerSuite) TestRemoveBlocks(c *gc.C) { 365 st := s.Factory.MakeModel(c, &factory.ModelParams{ 366 Name: "test"}) 367 defer st.Close() 368 369 s.State.SwitchBlockOn(state.DestroyBlock, "TestBlockDestroyModel") 370 s.State.SwitchBlockOn(state.ChangeBlock, "TestChangeBlock") 371 st.SwitchBlockOn(state.DestroyBlock, "TestBlockDestroyModel") 372 st.SwitchBlockOn(state.ChangeBlock, "TestChangeBlock") 373 374 err := s.controller.RemoveBlocks(params.RemoveBlocksArgs{All: true}) 375 c.Assert(err, jc.ErrorIsNil) 376 377 blocks, err := s.State.AllBlocksForController() 378 c.Assert(err, jc.ErrorIsNil) 379 c.Assert(blocks, gc.HasLen, 0) 380 } 381 382 func (s *controllerSuite) TestRemoveBlocksNotAll(c *gc.C) { 383 err := s.controller.RemoveBlocks(params.RemoveBlocksArgs{}) 384 c.Assert(err, gc.ErrorMatches, "not supported") 385 } 386 387 func (s *controllerSuite) TestWatchAllModels(c *gc.C) { 388 watcherId, err := s.controller.WatchAllModels() 389 c.Assert(err, jc.ErrorIsNil) 390 391 var disposed bool 392 watcherAPI_, err := apiserver.NewAllWatcher(facadetest.Context{ 393 State_: s.State, 394 StatePool_: s.StatePool, 395 Resources_: s.resources, 396 Auth_: s.authorizer, 397 ID_: watcherId.AllWatcherId, 398 Dispose_: func() { disposed = true }, 399 }) 400 c.Assert(err, jc.ErrorIsNil) 401 watcherAPI := watcherAPI_.(*apiserver.SrvAllWatcher) 402 defer func() { 403 err := watcherAPI.Stop() 404 c.Assert(err, jc.ErrorIsNil) 405 c.Assert(disposed, jc.IsTrue) 406 }() 407 408 done := make(chan bool) 409 defer close(done) 410 resultC := make(chan params.AllWatcherNextResults) 411 go func() { 412 for { 413 select { 414 case <-done: 415 return 416 default: 417 result, err := watcherAPI.Next() 418 if err != nil { 419 c.Assert(err, jc.Satisfies, coremultiwatcher.IsErrStopped) 420 return 421 } 422 resultC <- result 423 } 424 } 425 }() 426 427 select { 428 case result := <-resultC: 429 // Expect to see the initial model be reported. 430 deltas := result.Deltas 431 c.Assert(deltas, gc.HasLen, 1) 432 modelInfo := deltas[0].Entity.(*params.ModelUpdate) 433 c.Assert(modelInfo.ModelUUID, gc.Equals, s.State.ModelUUID()) 434 c.Assert(modelInfo.IsController, gc.Equals, s.State.IsController()) 435 case <-time.After(testing.LongWait): 436 c.Fatal("timed out") 437 } 438 439 // To ensure we really watch all models, make another one. 440 st := s.Factory.MakeModel(c, &factory.ModelParams{ 441 Name: "test"}) 442 defer st.Close() 443 444 // Update the model agent versions to ensure settings changes cause an update. 445 err = s.State.SetModelAgentVersion(version.MustParse("2.6.666"), nil, true) 446 c.Assert(err, jc.ErrorIsNil) 447 err = st.SetModelAgentVersion(version.MustParse("2.6.667"), nil, true) 448 c.Assert(err, jc.ErrorIsNil) 449 expectedVersions := map[string]string{ 450 s.State.ModelUUID(): "2.6.666", 451 st.ModelUUID(): "2.6.667", 452 } 453 454 for resultCount := 0; resultCount != 2; { 455 select { 456 case result := <-resultC: 457 c.Logf("got change: %# v", pretty.Formatter(result)) 458 for _, d := range result.Deltas { 459 if d.Removed { 460 continue 461 } 462 modelInfo, ok := d.Entity.(*params.ModelUpdate) 463 if !ok { 464 continue 465 } 466 if modelInfo.Config["agent-version"] == expectedVersions[modelInfo.ModelUUID] { 467 resultCount = resultCount + 1 468 } 469 } 470 case <-time.After(testing.LongWait): 471 c.Fatalf("timed out waiting for 2 model updates, got %d", resultCount) 472 } 473 } 474 } 475 476 func (s *controllerSuite) TestInitiateMigration(c *gc.C) { 477 // Create two hosted models to migrate. 478 st1 := s.Factory.MakeModel(c, nil) 479 defer st1.Close() 480 model1, err := st1.Model() 481 c.Assert(err, jc.ErrorIsNil) 482 483 st2 := s.Factory.MakeModel(c, nil) 484 defer st2.Close() 485 model2, err := st2.Model() 486 c.Assert(err, jc.ErrorIsNil) 487 488 mac, err := macaroon.New([]byte("secret"), []byte("id"), "location", macaroon.LatestVersion) 489 c.Assert(err, jc.ErrorIsNil) 490 macsJSON, err := json.Marshal([]macaroon.Slice{{mac}}) 491 c.Assert(err, jc.ErrorIsNil) 492 493 controller.SetPrecheckResult(s, nil) 494 495 // Kick off migrations 496 args := params.InitiateMigrationArgs{ 497 Specs: []params.MigrationSpec{ 498 { 499 ModelTag: model1.ModelTag().String(), 500 TargetInfo: params.MigrationTargetInfo{ 501 ControllerTag: randomControllerTag(), 502 ControllerAlias: "", // intentionally left empty; simulates older client 503 Addrs: []string{"1.1.1.1:1111", "2.2.2.2:2222"}, 504 CACert: "cert1", 505 AuthTag: names.NewUserTag("admin1").String(), 506 Password: "secret1", 507 }, 508 }, { 509 ModelTag: model2.ModelTag().String(), 510 TargetInfo: params.MigrationTargetInfo{ 511 ControllerTag: randomControllerTag(), 512 ControllerAlias: "target-controller", 513 Addrs: []string{"3.3.3.3:3333"}, 514 CACert: "cert2", 515 AuthTag: names.NewUserTag("admin2").String(), 516 Macaroons: string(macsJSON), 517 Password: "secret2", 518 }, 519 }, 520 }, 521 } 522 out, err := s.controller.InitiateMigration(args) 523 c.Assert(err, jc.ErrorIsNil) 524 c.Assert(out.Results, gc.HasLen, 2) 525 526 states := []*state.State{st1, st2} 527 for i, spec := range args.Specs { 528 c.Log(i) 529 st := states[i] 530 result := out.Results[i] 531 532 c.Assert(result.Error, gc.IsNil) 533 c.Check(result.ModelTag, gc.Equals, spec.ModelTag) 534 expectedId := st.ModelUUID() + ":0" 535 c.Check(result.MigrationId, gc.Equals, expectedId) 536 537 // Ensure the migration made it into the DB correctly. 538 mig, err := st.LatestMigration() 539 c.Assert(err, jc.ErrorIsNil) 540 c.Check(mig.Id(), gc.Equals, expectedId) 541 c.Check(mig.ModelUUID(), gc.Equals, st.ModelUUID()) 542 c.Check(mig.InitiatedBy(), gc.Equals, s.Owner.Id()) 543 544 targetInfo, err := mig.TargetInfo() 545 c.Assert(err, jc.ErrorIsNil) 546 c.Check(targetInfo.ControllerTag.String(), gc.Equals, spec.TargetInfo.ControllerTag) 547 c.Check(targetInfo.ControllerAlias, gc.Equals, spec.TargetInfo.ControllerAlias) 548 c.Check(targetInfo.Addrs, jc.SameContents, spec.TargetInfo.Addrs) 549 c.Check(targetInfo.CACert, gc.Equals, spec.TargetInfo.CACert) 550 c.Check(targetInfo.AuthTag.String(), gc.Equals, spec.TargetInfo.AuthTag) 551 c.Check(targetInfo.Password, gc.Equals, spec.TargetInfo.Password) 552 553 if spec.TargetInfo.Macaroons != "" { 554 macJSONdb, err := json.Marshal(targetInfo.Macaroons) 555 c.Assert(err, jc.ErrorIsNil) 556 c.Check(string(macJSONdb), gc.Equals, spec.TargetInfo.Macaroons) 557 } 558 } 559 } 560 561 func (s *controllerSuite) TestInitiateMigrationSpecError(c *gc.C) { 562 // Create a hosted model to migrate. 563 st := s.Factory.MakeModel(c, nil) 564 defer st.Close() 565 model, err := st.Model() 566 c.Assert(err, jc.ErrorIsNil) 567 568 // Kick off the migration with missing details. 569 args := params.InitiateMigrationArgs{ 570 Specs: []params.MigrationSpec{{ 571 ModelTag: model.ModelTag().String(), 572 // TargetInfo missing 573 }}, 574 } 575 out, err := s.controller.InitiateMigration(args) 576 c.Assert(err, jc.ErrorIsNil) 577 c.Assert(out.Results, gc.HasLen, 1) 578 result := out.Results[0] 579 c.Check(result.ModelTag, gc.Equals, args.Specs[0].ModelTag) 580 c.Check(result.MigrationId, gc.Equals, "") 581 c.Check(result.Error, gc.ErrorMatches, "controller tag: .+ is not a valid tag") 582 } 583 584 func (s *controllerSuite) TestInitiateMigrationPartialFailure(c *gc.C) { 585 st := s.Factory.MakeModel(c, nil) 586 defer st.Close() 587 controller.SetPrecheckResult(s, nil) 588 589 m, err := st.Model() 590 c.Assert(err, jc.ErrorIsNil) 591 592 args := params.InitiateMigrationArgs{ 593 Specs: []params.MigrationSpec{ 594 { 595 ModelTag: m.ModelTag().String(), 596 TargetInfo: params.MigrationTargetInfo{ 597 ControllerTag: randomControllerTag(), 598 Addrs: []string{"1.1.1.1:1111", "2.2.2.2:2222"}, 599 CACert: "cert", 600 AuthTag: names.NewUserTag("admin").String(), 601 Password: "secret", 602 }, 603 }, { 604 ModelTag: randomModelTag(), // Doesn't exist. 605 }, 606 }, 607 } 608 out, err := s.controller.InitiateMigration(args) 609 c.Assert(err, jc.ErrorIsNil) 610 c.Assert(out.Results, gc.HasLen, 2) 611 612 c.Check(out.Results[0].ModelTag, gc.Equals, m.ModelTag().String()) 613 c.Check(out.Results[0].Error, gc.IsNil) 614 615 c.Check(out.Results[1].ModelTag, gc.Equals, args.Specs[1].ModelTag) 616 c.Check(out.Results[1].Error, gc.ErrorMatches, "model not found") 617 } 618 619 func (s *controllerSuite) TestInitiateMigrationInvalidMacaroons(c *gc.C) { 620 st := s.Factory.MakeModel(c, nil) 621 defer st.Close() 622 623 m, err := st.Model() 624 c.Assert(err, jc.ErrorIsNil) 625 626 args := params.InitiateMigrationArgs{ 627 Specs: []params.MigrationSpec{ 628 { 629 ModelTag: m.ModelTag().String(), 630 TargetInfo: params.MigrationTargetInfo{ 631 ControllerTag: randomControllerTag(), 632 Addrs: []string{"1.1.1.1:1111", "2.2.2.2:2222"}, 633 CACert: "cert", 634 AuthTag: names.NewUserTag("admin").String(), 635 Macaroons: "BLAH", 636 }, 637 }, 638 }, 639 } 640 out, err := s.controller.InitiateMigration(args) 641 c.Assert(err, jc.ErrorIsNil) 642 c.Assert(out.Results, gc.HasLen, 1) 643 result := out.Results[0] 644 c.Check(result.ModelTag, gc.Equals, args.Specs[0].ModelTag) 645 c.Check(result.Error, gc.ErrorMatches, "invalid macaroons: .+") 646 } 647 648 func (s *controllerSuite) TestInitiateMigrationPrecheckFail(c *gc.C) { 649 st := s.Factory.MakeModel(c, nil) 650 defer st.Close() 651 652 controller.SetPrecheckResult(s, errors.New("boom")) 653 654 m, err := st.Model() 655 c.Assert(err, jc.ErrorIsNil) 656 657 args := params.InitiateMigrationArgs{ 658 Specs: []params.MigrationSpec{{ 659 ModelTag: m.ModelTag().String(), 660 TargetInfo: params.MigrationTargetInfo{ 661 ControllerTag: randomControllerTag(), 662 Addrs: []string{"1.1.1.1:1111"}, 663 CACert: "cert1", 664 AuthTag: names.NewUserTag("admin1").String(), 665 Password: "secret1", 666 }, 667 }}, 668 } 669 out, err := s.controller.InitiateMigration(args) 670 c.Assert(err, jc.ErrorIsNil) 671 c.Assert(out.Results, gc.HasLen, 1) 672 c.Check(out.Results[0].Error, gc.ErrorMatches, "boom") 673 674 active, err := st.IsMigrationActive() 675 c.Assert(err, jc.ErrorIsNil) 676 c.Check(active, jc.IsFalse) 677 } 678 679 func randomControllerTag() string { 680 uuid := utils.MustNewUUID().String() 681 return names.NewControllerTag(uuid).String() 682 } 683 684 func randomModelTag() string { 685 uuid := utils.MustNewUUID().String() 686 return names.NewModelTag(uuid).String() 687 } 688 689 func (s *controllerSuite) modifyControllerAccess(c *gc.C, user names.UserTag, action params.ControllerAction, access string) error { 690 args := params.ModifyControllerAccessRequest{ 691 Changes: []params.ModifyControllerAccess{{ 692 UserTag: user.String(), 693 Action: action, 694 Access: access, 695 }}} 696 result, err := s.controller.ModifyControllerAccess(args) 697 c.Assert(err, jc.ErrorIsNil) 698 return result.OneError() 699 } 700 701 func (s *controllerSuite) controllerGrant(c *gc.C, user names.UserTag, access string) error { 702 return s.modifyControllerAccess(c, user, params.GrantControllerAccess, access) 703 } 704 705 func (s *controllerSuite) controllerRevoke(c *gc.C, user names.UserTag, access string) error { 706 return s.modifyControllerAccess(c, user, params.RevokeControllerAccess, access) 707 } 708 709 func (s *controllerSuite) TestGrantMissingUserFails(c *gc.C) { 710 user := names.NewLocalUserTag("foobar") 711 err := s.controllerGrant(c, user, string(permission.SuperuserAccess)) 712 expectedErr := `could not grant controller access: user "foobar" does not exist locally: user "foobar" not found` 713 c.Assert(err, gc.ErrorMatches, expectedErr) 714 } 715 716 func (s *controllerSuite) TestRevokeSuperuserLeavesLoginAccess(c *gc.C) { 717 user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true}) 718 719 err := s.controllerGrant(c, user.UserTag(), string(permission.SuperuserAccess)) 720 c.Assert(err, gc.IsNil) 721 ctag := names.NewControllerTag(s.State.ControllerUUID()) 722 controllerUser, err := s.State.UserAccess(user.UserTag(), ctag) 723 c.Assert(err, jc.ErrorIsNil) 724 c.Assert(controllerUser.Access, gc.Equals, permission.SuperuserAccess) 725 726 err = s.controllerRevoke(c, user.UserTag(), string(permission.SuperuserAccess)) 727 c.Assert(err, gc.IsNil) 728 729 controllerUser, err = s.State.UserAccess(user.UserTag(), controllerUser.Object) 730 c.Assert(err, jc.ErrorIsNil) 731 c.Assert(controllerUser.Access, gc.Equals, permission.LoginAccess) 732 } 733 734 func (s *controllerSuite) TestRevokeLoginRemovesControllerUser(c *gc.C) { 735 user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true}) 736 err := s.controllerRevoke(c, user.UserTag(), string(permission.LoginAccess)) 737 c.Assert(err, gc.IsNil) 738 739 ctag := names.NewControllerTag(s.State.ControllerUUID()) 740 _, err = s.State.UserAccess(user.UserTag(), ctag) 741 742 c.Assert(errors.IsNotFound(err), jc.IsTrue) 743 } 744 745 func (s *controllerSuite) TestRevokeControllerMissingUser(c *gc.C) { 746 user := names.NewLocalUserTag("foobar") 747 err := s.controllerRevoke(c, user, string(permission.SuperuserAccess)) 748 expectedErr := `could not look up controller access for user: user "foobar" not found` 749 c.Assert(err, gc.ErrorMatches, expectedErr) 750 } 751 752 func (s *controllerSuite) TestGrantOnlyGreaterAccess(c *gc.C) { 753 user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true}) 754 755 err := s.controllerGrant(c, user.UserTag(), string(permission.SuperuserAccess)) 756 c.Assert(err, gc.IsNil) 757 ctag := names.NewControllerTag(s.State.ControllerUUID()) 758 controllerUser, err := s.State.UserAccess(user.UserTag(), ctag) 759 c.Assert(err, jc.ErrorIsNil) 760 c.Assert(controllerUser.Access, gc.Equals, permission.SuperuserAccess) 761 762 err = s.controllerGrant(c, user.UserTag(), string(permission.SuperuserAccess)) 763 expectedErr := `could not grant controller access: user already has "superuser" access or greater` 764 c.Assert(err, gc.ErrorMatches, expectedErr) 765 } 766 767 func (s *controllerSuite) TestGrantControllerAddRemoteUser(c *gc.C) { 768 userTag := names.NewUserTag("foobar@ubuntuone") 769 770 err := s.controllerGrant(c, userTag, string(permission.SuperuserAccess)) 771 c.Assert(err, jc.ErrorIsNil) 772 773 ctag := names.NewControllerTag(s.State.ControllerUUID()) 774 controllerUser, err := s.State.UserAccess(userTag, ctag) 775 c.Assert(err, jc.ErrorIsNil) 776 777 c.Assert(controllerUser.Access, gc.Equals, permission.SuperuserAccess) 778 } 779 780 func (s *controllerSuite) TestGrantControllerInvalidUserTag(c *gc.C) { 781 for _, testParam := range []struct { 782 tag string 783 validTag bool 784 }{{ 785 tag: "unit-foo/0", 786 validTag: true, 787 }, { 788 tag: "application-foo", 789 validTag: true, 790 }, { 791 tag: "relation-wordpress:db mysql:db", 792 validTag: true, 793 }, { 794 tag: "machine-0", 795 validTag: true, 796 }, { 797 tag: "user@local", 798 validTag: false, 799 }, { 800 tag: "user-Mua^h^h^h^arh", 801 validTag: true, 802 }, { 803 tag: "user@", 804 validTag: false, 805 }, { 806 tag: "user@ubuntuone", 807 validTag: false, 808 }, { 809 tag: "user@ubuntuone", 810 validTag: false, 811 }, { 812 tag: "@ubuntuone", 813 validTag: false, 814 }, { 815 tag: "in^valid.", 816 validTag: false, 817 }, { 818 tag: "", 819 validTag: false, 820 }, 821 } { 822 var expectedErr string 823 errPart := `could not modify controller access: "` + regexp.QuoteMeta(testParam.tag) + `" is not a valid ` 824 825 if testParam.validTag { 826 // The string is a valid tag, but not a user tag. 827 expectedErr = errPart + `user tag` 828 } else { 829 // The string is not a valid tag of any kind. 830 expectedErr = errPart + `tag` 831 } 832 833 args := params.ModifyControllerAccessRequest{ 834 Changes: []params.ModifyControllerAccess{{ 835 UserTag: testParam.tag, 836 Action: params.GrantControllerAccess, 837 Access: string(permission.SuperuserAccess), 838 }}} 839 840 result, err := s.controller.ModifyControllerAccess(args) 841 c.Assert(err, jc.ErrorIsNil) 842 c.Assert(result.OneError(), gc.ErrorMatches, expectedErr) 843 } 844 } 845 846 func (s *controllerSuite) TestModifyControllerAccessEmptyArgs(c *gc.C) { 847 args := params.ModifyControllerAccessRequest{Changes: []params.ModifyControllerAccess{{}}} 848 849 result, err := s.controller.ModifyControllerAccess(args) 850 c.Assert(err, jc.ErrorIsNil) 851 expectedErr := `"" controller access not valid` 852 c.Assert(result.OneError(), gc.ErrorMatches, expectedErr) 853 } 854 855 func (s *controllerSuite) TestModifyControllerAccessInvalidAction(c *gc.C) { 856 var dance params.ControllerAction = "dance" 857 args := params.ModifyControllerAccessRequest{ 858 Changes: []params.ModifyControllerAccess{{ 859 UserTag: "user-user@local", 860 Action: dance, 861 Access: string(permission.LoginAccess), 862 }}} 863 864 result, err := s.controller.ModifyControllerAccess(args) 865 c.Assert(err, jc.ErrorIsNil) 866 expectedErr := `unknown action "dance"` 867 c.Assert(result.OneError(), gc.ErrorMatches, expectedErr) 868 } 869 870 func (s *controllerSuite) TestGetControllerAccess(c *gc.C) { 871 user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true}) 872 user2 := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true}) 873 874 err := s.controllerGrant(c, user.UserTag(), string(permission.SuperuserAccess)) 875 c.Assert(err, gc.IsNil) 876 req := params.Entities{ 877 Entities: []params.Entity{{Tag: user.Tag().String()}, {Tag: user2.Tag().String()}}, 878 } 879 results, err := s.controller.GetControllerAccess(req) 880 c.Assert(err, jc.ErrorIsNil) 881 c.Assert(results.Results, gc.DeepEquals, []params.UserAccessResult{{ 882 Result: ¶ms.UserAccess{ 883 Access: "superuser", 884 UserTag: user.Tag().String(), 885 }}, { 886 Result: ¶ms.UserAccess{ 887 Access: "login", 888 UserTag: user2.Tag().String(), 889 }}}) 890 } 891 892 func (s *controllerSuite) TestGetControllerAccessPermissions(c *gc.C) { 893 // Set up the user making the call. 894 user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true}) 895 anAuthoriser := apiservertesting.FakeAuthorizer{ 896 Tag: user.Tag(), 897 } 898 endpoint, err := controller.NewControllerAPIv11( 899 facadetest.Context{ 900 State_: s.State, 901 StatePool_: s.StatePool, 902 Resources_: s.resources, 903 Auth_: anAuthoriser, 904 }) 905 c.Assert(err, jc.ErrorIsNil) 906 args := params.ModifyControllerAccessRequest{ 907 Changes: []params.ModifyControllerAccess{{ 908 UserTag: user.Tag().String(), 909 Action: params.GrantControllerAccess, 910 Access: "superuser", 911 }}} 912 result, err := s.controller.ModifyControllerAccess(args) 913 c.Assert(err, jc.ErrorIsNil) 914 c.Assert(result.OneError(), jc.ErrorIsNil) 915 916 // We ask for permissions for a different user as well as ourselves. 917 differentUser := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true}) 918 req := params.Entities{ 919 Entities: []params.Entity{{Tag: user.Tag().String()}, {Tag: differentUser.Tag().String()}}, 920 } 921 results, err := endpoint.GetControllerAccess(req) 922 c.Assert(err, jc.ErrorIsNil) 923 c.Assert(results.Results, gc.HasLen, 2) 924 c.Assert(*results.Results[0].Result, jc.DeepEquals, params.UserAccess{ 925 Access: "superuser", 926 UserTag: user.Tag().String(), 927 }) 928 c.Assert(*results.Results[1].Error, gc.DeepEquals, params.Error{ 929 Message: "permission denied", Code: "unauthorized access", 930 }) 931 } 932 933 func (s *controllerSuite) TestModelStatus(c *gc.C) { 934 // Check that we don't err out immediately if a model errs. 935 results, err := s.controller.ModelStatus(params.Entities{[]params.Entity{{ 936 Tag: "bad-tag", 937 }, { 938 Tag: s.Model.ModelTag().String(), 939 }}}) 940 c.Assert(err, jc.ErrorIsNil) 941 c.Assert(results.Results, gc.HasLen, 2) 942 c.Assert(results.Results[0].Error, gc.ErrorMatches, `"bad-tag" is not a valid tag`) 943 944 // Check that we don't err out if a model errs even if some firsts in collection pass. 945 results, err = s.controller.ModelStatus(params.Entities{[]params.Entity{{ 946 Tag: s.Model.ModelTag().String(), 947 }, { 948 Tag: "bad-tag", 949 }}}) 950 c.Assert(err, jc.ErrorIsNil) 951 c.Assert(results.Results, gc.HasLen, 2) 952 c.Assert(results.Results[1].Error, gc.ErrorMatches, `"bad-tag" is not a valid tag`) 953 954 // Check that we return successfully if no errors. 955 results, err = s.controller.ModelStatus(params.Entities{[]params.Entity{{ 956 Tag: s.Model.ModelTag().String(), 957 }}}) 958 c.Assert(err, jc.ErrorIsNil) 959 c.Assert(results.Results, gc.HasLen, 1) 960 } 961 962 func (s *controllerSuite) TestConfigSet(c *gc.C) { 963 config, err := s.State.ControllerConfig() 964 c.Assert(err, jc.ErrorIsNil) 965 // Sanity check. 966 c.Assert(config.AuditingEnabled(), gc.Equals, false) 967 968 err = s.controller.ConfigSet(params.ControllerConfigSet{Config: map[string]interface{}{ 969 "auditing-enabled": true, 970 }}) 971 c.Assert(err, jc.ErrorIsNil) 972 973 config, err = s.State.ControllerConfig() 974 c.Assert(err, jc.ErrorIsNil) 975 c.Assert(config.AuditingEnabled(), gc.Equals, true) 976 } 977 978 func (s *controllerSuite) TestConfigSetRequiresSuperUser(c *gc.C) { 979 user := s.Factory.MakeUser(c, &factory.UserParams{ 980 Access: permission.ReadAccess, 981 }) 982 anAuthoriser := apiservertesting.FakeAuthorizer{ 983 Tag: user.Tag(), 984 } 985 endpoint, err := controller.NewControllerAPIv11( 986 facadetest.Context{ 987 State_: s.State, 988 StatePool_: s.StatePool, 989 Resources_: s.resources, 990 Auth_: anAuthoriser, 991 }) 992 c.Assert(err, jc.ErrorIsNil) 993 994 err = endpoint.ConfigSet(params.ControllerConfigSet{Config: map[string]interface{}{ 995 "something": 23, 996 }}) 997 998 c.Assert(err, gc.ErrorMatches, "permission denied") 999 } 1000 1001 func (s *controllerSuite) TestConfigSetPublishesEvent(c *gc.C) { 1002 done := make(chan struct{}) 1003 var config corecontroller.Config 1004 s.hub.Subscribe(pscontroller.ConfigChanged, func(topic string, data pscontroller.ConfigChangedMessage, err error) { 1005 c.Check(err, jc.ErrorIsNil) 1006 config = data.Config 1007 close(done) 1008 }) 1009 1010 err := s.controller.ConfigSet(params.ControllerConfigSet{Config: map[string]interface{}{ 1011 "features": []string{"foo", "bar"}, 1012 }}) 1013 c.Assert(err, jc.ErrorIsNil) 1014 1015 select { 1016 case <-done: 1017 case <-time.After(testing.LongWait): 1018 c.Fatal("no event sent}") 1019 } 1020 1021 c.Assert(config.Features().SortedValues(), jc.DeepEquals, []string{"bar", "foo"}) 1022 } 1023 1024 func (s *controllerSuite) TestConfigSetCAASImageRepo(c *gc.C) { 1025 config, err := s.State.ControllerConfig() 1026 c.Assert(err, jc.ErrorIsNil) 1027 c.Assert(config.CAASImageRepo(), gc.Equals, "") 1028 1029 err = s.controller.ConfigSet(params.ControllerConfigSet{Config: map[string]interface{}{ 1030 "caas-image-repo": "juju-repo.local", 1031 }}) 1032 c.Assert(err, gc.ErrorMatches, `cannot change caas-image-repo as it is not currently set`) 1033 1034 err = s.State.UpdateControllerConfig(map[string]interface{}{ 1035 "caas-image-repo": "jujusolutions", 1036 }, nil) 1037 c.Assert(err, jc.ErrorIsNil) 1038 1039 err = s.controller.ConfigSet(params.ControllerConfigSet{Config: map[string]interface{}{ 1040 "caas-image-repo": "juju-repo.local", 1041 }}) 1042 c.Assert(err, gc.ErrorMatches, `cannot change caas-image-repo: repository read-only, only authentication can be updated`) 1043 1044 err = s.controller.ConfigSet(params.ControllerConfigSet{Config: map[string]interface{}{ 1045 "caas-image-repo": `{"repository":"jujusolutions","username":"foo","password":"bar"}`, 1046 }}) 1047 c.Assert(err, gc.ErrorMatches, `cannot change caas-image-repo: unable to add authentication details`) 1048 1049 err = s.State.UpdateControllerConfig(map[string]interface{}{ 1050 "caas-image-repo": `{"repository":"jujusolutions","username":"bar","password":"foo"}`, 1051 }, nil) 1052 c.Assert(err, jc.ErrorIsNil) 1053 1054 err = s.controller.ConfigSet(params.ControllerConfigSet{Config: map[string]interface{}{ 1055 "caas-image-repo": `{"repository":"jujusolutions","username":"foo","password":"bar"}`, 1056 }}) 1057 c.Assert(err, jc.ErrorIsNil) 1058 1059 config, err = s.State.ControllerConfig() 1060 c.Assert(err, jc.ErrorIsNil) 1061 repoDetails, err := docker.NewImageRepoDetails(config.CAASImageRepo()) 1062 c.Assert(err, jc.ErrorIsNil) 1063 c.Assert(repoDetails, gc.DeepEquals, docker.ImageRepoDetails{ 1064 Repository: "jujusolutions", 1065 BasicAuthConfig: docker.BasicAuthConfig{ 1066 Username: "foo", 1067 Password: "bar", 1068 }, 1069 }) 1070 } 1071 1072 func (s *controllerSuite) TestMongoVersion(c *gc.C) { 1073 result, err := s.controller.MongoVersion() 1074 c.Assert(err, jc.ErrorIsNil) 1075 1076 var resErr *params.Error 1077 c.Assert(result.Error, gc.Equals, resErr) 1078 // We can't guarantee which version of mongo is running, so let's just 1079 // attempt to match it to a very basic version (major.minor.patch) 1080 c.Assert(result.Result, gc.Matches, "^([0-9]{1,}).([0-9]{1,}).([0-9]{1,})$") 1081 } 1082 1083 func (s *controllerSuite) TestIdentityProviderURL(c *gc.C) { 1084 // Preserve default controller config as we will be mutating it just 1085 // for this test 1086 defer func(orig map[string]interface{}) { 1087 s.ControllerConfig = orig 1088 }(s.ControllerConfig) 1089 1090 // Our default test configuration does not specify an IdentityURL 1091 urlRes, err := s.controller.IdentityProviderURL() 1092 c.Assert(err, jc.ErrorIsNil) 1093 c.Assert(urlRes.Result, gc.Equals, "") 1094 1095 // IdentityURL cannot be changed after bootstrap; we need to spin up 1096 // another controller with IdentityURL pre-configured 1097 s.TearDownTest(c) 1098 expURL := "https://api.jujucharms.com/identity" 1099 s.ControllerConfig = map[string]interface{}{ 1100 corecontroller.IdentityURL: expURL, 1101 } 1102 s.SetUpTest(c) 1103 1104 urlRes, err = s.controller.IdentityProviderURL() 1105 c.Assert(err, jc.ErrorIsNil) 1106 c.Assert(urlRes.Result, gc.Equals, expURL) 1107 } 1108 1109 func (s *controllerSuite) newSummaryWatcherFacade(c *gc.C, id string) *apiserver.SrvModelSummaryWatcher { 1110 context := s.context 1111 context.ID_ = id 1112 watcher, err := apiserver.NewModelSummaryWatcher(context) 1113 c.Assert(err, jc.ErrorIsNil) 1114 return watcher 1115 } 1116 1117 func (s *controllerSuite) TestWatchAllModelSummariesByAdmin(c *gc.C) { 1118 // Default authorizer is an admin. 1119 result, err := s.controller.WatchAllModelSummaries() 1120 c.Assert(err, jc.ErrorIsNil) 1121 1122 watcherAPI := s.newSummaryWatcherFacade(c, result.WatcherID) 1123 1124 resultC := make(chan params.SummaryWatcherNextResults) 1125 go func() { 1126 result, err := watcherAPI.Next() 1127 c.Assert(err, jc.ErrorIsNil) 1128 resultC <- result 1129 }() 1130 1131 select { 1132 case result := <-resultC: 1133 // Expect to see the initial environment be reported. 1134 c.Assert(result, jc.DeepEquals, params.SummaryWatcherNextResults{ 1135 Models: []params.ModelAbstract{ 1136 { 1137 UUID: "deadbeef-0bad-400d-8000-4b1d0d06f00d", 1138 Controller: "", // TODO(thumper): add controller name next branch 1139 Name: "controller", 1140 Admins: []string{"test-admin"}, 1141 Cloud: "dummy", 1142 Region: "dummy-region", 1143 Status: "green", 1144 Messages: []params.ModelSummaryMessage{}, 1145 }}}) 1146 case <-time.After(testing.LongWait): 1147 c.Fatal("timed out") 1148 } 1149 } 1150 1151 func (s *controllerSuite) TestWatchAllModelSummariesByNonAdmin(c *gc.C) { 1152 anAuthoriser := apiservertesting.FakeAuthorizer{ 1153 Tag: names.NewLocalUserTag("bob"), 1154 } 1155 endPoint, err := controller.LatestAPI( 1156 facadetest.Context{ 1157 State_: s.State, 1158 StatePool_: s.StatePool, 1159 Resources_: s.resources, 1160 Auth_: anAuthoriser, 1161 }) 1162 c.Assert(err, jc.ErrorIsNil) 1163 1164 _, err = endPoint.WatchAllModelSummaries() 1165 c.Assert(err, gc.ErrorMatches, "permission denied") 1166 } 1167 1168 func (s *controllerSuite) makeBobsModel(c *gc.C) string { 1169 bob := s.Factory.MakeUser(c, &factory.UserParams{ 1170 Name: "bob", 1171 NoModelUser: true, 1172 }) 1173 st := s.Factory.MakeModel(c, &factory.ModelParams{ 1174 Owner: bob.UserTag(), 1175 Name: "bobs-model"}) 1176 uuid := st.ModelUUID() 1177 st.Close() 1178 s.WaitForModelWatchersIdle(c, uuid) 1179 return uuid 1180 } 1181 1182 func (s *controllerSuite) TestWatchModelSummariesByNonAdmin(c *gc.C) { 1183 s.makeBobsModel(c) 1184 1185 // Default authorizer is an admin. As a user, admin can't see 1186 // Bob's model. 1187 result, err := s.controller.WatchModelSummaries() 1188 c.Assert(err, jc.ErrorIsNil) 1189 1190 watcherAPI := s.newSummaryWatcherFacade(c, result.WatcherID) 1191 1192 resultC := make(chan params.SummaryWatcherNextResults) 1193 go func() { 1194 result, err := watcherAPI.Next() 1195 c.Assert(err, jc.ErrorIsNil) 1196 resultC <- result 1197 }() 1198 1199 select { 1200 case result := <-resultC: 1201 // Expect to see the initial environment be reported. 1202 c.Assert(result, jc.DeepEquals, params.SummaryWatcherNextResults{ 1203 Models: []params.ModelAbstract{ 1204 { 1205 UUID: "deadbeef-0bad-400d-8000-4b1d0d06f00d", 1206 Controller: "", // TODO(thumper): add controller name next branch 1207 Name: "controller", 1208 Admins: []string{"test-admin"}, 1209 Cloud: "dummy", 1210 Region: "dummy-region", 1211 Status: "green", 1212 Messages: []params.ModelSummaryMessage{}, 1213 }}}) 1214 case <-time.After(testing.LongWait): 1215 c.Fatal("timed out") 1216 } 1217 1218 } 1219 1220 type noopRegisterer struct { 1221 prometheus.Registerer 1222 } 1223 1224 func (noopRegisterer) Register(prometheus.Collector) error { 1225 return nil 1226 } 1227 1228 func (noopRegisterer) Unregister(prometheus.Collector) bool { 1229 return true 1230 }