github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/apiserver/controller/controller.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 // The controller package defines an API end point for functions dealing 5 // with controllers as a whole. 6 package controller 7 8 import ( 9 "encoding/json" 10 "sort" 11 12 "github.com/juju/errors" 13 "github.com/juju/loggo" 14 "github.com/juju/txn" 15 "github.com/juju/utils/set" 16 "gopkg.in/juju/names.v2" 17 "gopkg.in/macaroon.v1" 18 19 "github.com/juju/juju/api" 20 "github.com/juju/juju/api/migrationtarget" 21 "github.com/juju/juju/apiserver/common" 22 "github.com/juju/juju/apiserver/common/cloudspec" 23 "github.com/juju/juju/apiserver/facade" 24 "github.com/juju/juju/apiserver/params" 25 coremigration "github.com/juju/juju/core/migration" 26 "github.com/juju/juju/migration" 27 "github.com/juju/juju/permission" 28 "github.com/juju/juju/state" 29 "github.com/juju/juju/state/stateenvirons" 30 ) 31 32 var logger = loggo.GetLogger("juju.apiserver.controller") 33 34 func init() { 35 common.RegisterStandardFacade("Controller", 3, NewControllerAPI) 36 } 37 38 // Controller defines the methods on the controller API end point. 39 type Controller interface { 40 AllModels() (params.UserModelList, error) 41 DestroyController(args params.DestroyControllerArgs) error 42 ModelConfig() (params.ModelConfigResults, error) 43 HostedModelConfigs() (params.HostedModelConfigsResults, error) 44 GetControllerAccess(params.Entities) (params.UserAccessResults, error) 45 ControllerConfig() (params.ControllerConfigResult, error) 46 ListBlockedModels() (params.ModelBlockInfoList, error) 47 RemoveBlocks(args params.RemoveBlocksArgs) error 48 WatchAllModels() (params.AllWatcherId, error) 49 ModelStatus(params.Entities) (params.ModelStatusResults, error) 50 InitiateMigration(params.InitiateMigrationArgs) (params.InitiateMigrationResults, error) 51 ModifyControllerAccess(params.ModifyControllerAccessRequest) (params.ErrorResults, error) 52 } 53 54 // ControllerAPI implements the environment manager interface and is 55 // the concrete implementation of the api end point. 56 type ControllerAPI struct { 57 *common.ControllerConfigAPI 58 cloudspec.CloudSpecAPI 59 60 state *state.State 61 authorizer facade.Authorizer 62 apiUser names.UserTag 63 resources facade.Resources 64 } 65 66 var _ Controller = (*ControllerAPI)(nil) 67 68 // NewControllerAPI creates a new api server endpoint for managing 69 // environments. 70 func NewControllerAPI( 71 st *state.State, 72 resources facade.Resources, 73 authorizer facade.Authorizer, 74 ) (*ControllerAPI, error) { 75 if !authorizer.AuthClient() { 76 return nil, errors.Trace(common.ErrPerm) 77 } 78 79 // Since we know this is a user tag (because AuthClient is true), 80 // we just do the type assertion to the UserTag. 81 apiUser, _ := authorizer.GetAuthTag().(names.UserTag) 82 83 environConfigGetter := stateenvirons.EnvironConfigGetter{st} 84 return &ControllerAPI{ 85 ControllerConfigAPI: common.NewControllerConfig(st), 86 CloudSpecAPI: cloudspec.NewCloudSpec(environConfigGetter.CloudSpec, common.AuthFuncForTag(st.ModelTag())), 87 state: st, 88 authorizer: authorizer, 89 apiUser: apiUser, 90 resources: resources, 91 }, nil 92 } 93 94 func (s *ControllerAPI) checkHasAdmin() error { 95 isAdmin, err := s.authorizer.HasPermission(permission.SuperuserAccess, s.state.ControllerTag()) 96 if err != nil { 97 return errors.Trace(err) 98 } 99 if !isAdmin { 100 return common.ServerError(common.ErrPerm) 101 } 102 return nil 103 } 104 105 // AllModels allows controller administrators to get the list of all the 106 // environments in the controller. 107 func (s *ControllerAPI) AllModels() (params.UserModelList, error) { 108 result := params.UserModelList{} 109 if err := s.checkHasAdmin(); err != nil { 110 return result, errors.Trace(err) 111 } 112 113 // Get all the environments that the authenticated user can see, and 114 // supplement that with the other environments that exist that the user 115 // cannot see. The reason we do this is to get the LastConnection time for 116 // the environments that the user is able to see, so we have consistent 117 // output when listing with or without --all when an admin user. 118 environments, err := s.state.ModelsForUser(s.apiUser) 119 if err != nil { 120 return result, errors.Trace(err) 121 } 122 visibleEnvironments := set.NewStrings() 123 for _, env := range environments { 124 lastConn, err := env.LastConnection() 125 if err != nil && !state.IsNeverConnectedError(err) { 126 return result, errors.Trace(err) 127 } 128 visibleEnvironments.Add(env.UUID()) 129 result.UserModels = append(result.UserModels, params.UserModel{ 130 Model: params.Model{ 131 Name: env.Name(), 132 UUID: env.UUID(), 133 OwnerTag: env.Owner().String(), 134 }, 135 LastConnection: &lastConn, 136 }) 137 } 138 139 allEnvs, err := s.state.AllModels() 140 if err != nil { 141 return result, errors.Trace(err) 142 } 143 144 for _, env := range allEnvs { 145 if !visibleEnvironments.Contains(env.UUID()) { 146 result.UserModels = append(result.UserModels, params.UserModel{ 147 Model: params.Model{ 148 Name: env.Name(), 149 UUID: env.UUID(), 150 OwnerTag: env.Owner().String(), 151 }, 152 // No LastConnection as this user hasn't. 153 }) 154 } 155 } 156 157 // Sort the resulting sequence by environment name, then owner. 158 sort.Sort(orderedUserModels(result.UserModels)) 159 160 return result, nil 161 } 162 163 // ListBlockedModels returns a list of all environments on the controller 164 // which have a block in place. The resulting slice is sorted by environment 165 // name, then owner. Callers must be controller administrators to retrieve the 166 // list. 167 func (s *ControllerAPI) ListBlockedModels() (params.ModelBlockInfoList, error) { 168 results := params.ModelBlockInfoList{} 169 if err := s.checkHasAdmin(); err != nil { 170 return results, errors.Trace(err) 171 } 172 blocks, err := s.state.AllBlocksForController() 173 if err != nil { 174 return results, errors.Trace(err) 175 } 176 177 envBlocks := make(map[string][]string) 178 for _, block := range blocks { 179 uuid := block.ModelUUID() 180 types, ok := envBlocks[uuid] 181 if !ok { 182 types = []string{block.Type().String()} 183 } else { 184 types = append(types, block.Type().String()) 185 } 186 envBlocks[uuid] = types 187 } 188 189 for uuid, blocks := range envBlocks { 190 envInfo, err := s.state.GetModel(names.NewModelTag(uuid)) 191 if err != nil { 192 logger.Debugf("Unable to get name for model: %s", uuid) 193 continue 194 } 195 results.Models = append(results.Models, params.ModelBlockInfo{ 196 UUID: envInfo.UUID(), 197 Name: envInfo.Name(), 198 OwnerTag: envInfo.Owner().String(), 199 Blocks: blocks, 200 }) 201 } 202 203 // Sort the resulting sequence by environment name, then owner. 204 sort.Sort(orderedBlockInfo(results.Models)) 205 206 return results, nil 207 } 208 209 // ModelConfig returns the environment config for the controller 210 // environment. For information on the current environment, use 211 // client.ModelGet 212 func (s *ControllerAPI) ModelConfig() (params.ModelConfigResults, error) { 213 result := params.ModelConfigResults{} 214 if err := s.checkHasAdmin(); err != nil { 215 return result, errors.Trace(err) 216 } 217 218 controllerModel, err := s.state.ControllerModel() 219 if err != nil { 220 return result, errors.Trace(err) 221 } 222 223 cfg, err := controllerModel.Config() 224 if err != nil { 225 return result, errors.Trace(err) 226 } 227 228 result.Config = make(map[string]params.ConfigValue) 229 for name, val := range cfg.AllAttrs() { 230 result.Config[name] = params.ConfigValue{ 231 Value: val, 232 } 233 } 234 return result, nil 235 } 236 237 // HostedModelConfigs returns all the information that the client needs in 238 // order to connect directly with the host model's provider and destroy it 239 // directly. 240 func (s *ControllerAPI) HostedModelConfigs() (params.HostedModelConfigsResults, error) { 241 result := params.HostedModelConfigsResults{} 242 if err := s.checkHasAdmin(); err != nil { 243 return result, errors.Trace(err) 244 } 245 246 controllerModel, err := s.state.ControllerModel() 247 if err != nil { 248 return result, errors.Trace(err) 249 } 250 251 allModels, err := s.state.AllModels() 252 if err != nil { 253 return result, errors.Trace(err) 254 } 255 256 for _, model := range allModels { 257 if model.UUID() != controllerModel.UUID() { 258 config := params.HostedModelConfig{ 259 Name: model.Name(), 260 OwnerTag: model.Owner().String(), 261 } 262 modelConf, err := model.Config() 263 if err != nil { 264 config.Error = common.ServerError(err) 265 } else { 266 config.Config = modelConf.AllAttrs() 267 } 268 cloudSpec := s.GetCloudSpec(model.ModelTag()) 269 if config.Error == nil { 270 config.CloudSpec = cloudSpec.Result 271 config.Error = cloudSpec.Error 272 } 273 result.Models = append(result.Models, config) 274 } 275 } 276 277 return result, nil 278 } 279 280 // RemoveBlocks removes all the blocks in the controller. 281 func (s *ControllerAPI) RemoveBlocks(args params.RemoveBlocksArgs) error { 282 if err := s.checkHasAdmin(); err != nil { 283 return errors.Trace(err) 284 } 285 286 if !args.All { 287 return errors.New("not supported") 288 } 289 return errors.Trace(s.state.RemoveAllBlocksForController()) 290 } 291 292 // WatchAllModels starts watching events for all models in the 293 // controller. The returned AllWatcherId should be used with Next on the 294 // AllModelWatcher endpoint to receive deltas. 295 func (c *ControllerAPI) WatchAllModels() (params.AllWatcherId, error) { 296 if err := c.checkHasAdmin(); err != nil { 297 return params.AllWatcherId{}, errors.Trace(err) 298 } 299 w := c.state.WatchAllModels() 300 return params.AllWatcherId{ 301 AllWatcherId: c.resources.Register(w), 302 }, nil 303 } 304 305 type orderedBlockInfo []params.ModelBlockInfo 306 307 func (o orderedBlockInfo) Len() int { 308 return len(o) 309 } 310 311 func (o orderedBlockInfo) Less(i, j int) bool { 312 if o[i].Name < o[j].Name { 313 return true 314 } 315 if o[i].Name > o[j].Name { 316 return false 317 } 318 319 if o[i].OwnerTag < o[j].OwnerTag { 320 return true 321 } 322 if o[i].OwnerTag > o[j].OwnerTag { 323 return false 324 } 325 326 // Unreachable based on the rules of there not being duplicate 327 // environments of the same name for the same owner, but return false 328 // instead of panicing. 329 return false 330 } 331 332 // ModelStatus returns a summary of the environment. 333 func (c *ControllerAPI) ModelStatus(req params.Entities) (params.ModelStatusResults, error) { 334 models := req.Entities 335 results := params.ModelStatusResults{} 336 if err := c.checkHasAdmin(); err != nil { 337 return results, errors.Trace(err) 338 } 339 340 status := make([]params.ModelStatus, len(models)) 341 for i, model := range models { 342 modelStatus, err := c.modelStatus(model.Tag) 343 if err != nil { 344 return results, errors.Trace(err) 345 } 346 status[i] = modelStatus 347 } 348 results.Results = status 349 return results, nil 350 } 351 352 // GetControllerAccess returns the level of access the specifed users 353 // have on the controller. 354 func (c *ControllerAPI) GetControllerAccess(req params.Entities) (params.UserAccessResults, error) { 355 results := params.UserAccessResults{} 356 isAdmin, err := c.authorizer.HasPermission(permission.SuperuserAccess, c.state.ControllerTag()) 357 if err != nil { 358 return results, errors.Trace(err) 359 } 360 361 users := req.Entities 362 results.Results = make([]params.UserAccessResult, len(users)) 363 for i, user := range users { 364 userTag, err := names.ParseUserTag(user.Tag) 365 if err != nil { 366 results.Results[i].Error = common.ServerError(err) 367 continue 368 } 369 if !isAdmin && !c.authorizer.AuthOwner(userTag) { 370 results.Results[i].Error = common.ServerError(common.ErrPerm) 371 continue 372 } 373 accessInfo, err := c.state.UserAccess(userTag, c.state.ControllerTag()) 374 if err != nil { 375 results.Results[i].Error = common.ServerError(err) 376 continue 377 } 378 results.Results[i].Result = ¶ms.UserAccess{ 379 Access: string(accessInfo.Access), 380 UserTag: userTag.String()} 381 } 382 return results, nil 383 } 384 385 // InitiateMigration attempts to begin the migration of one or 386 // more models to other controllers. 387 func (c *ControllerAPI) InitiateMigration(reqArgs params.InitiateMigrationArgs) ( 388 params.InitiateMigrationResults, error, 389 ) { 390 out := params.InitiateMigrationResults{ 391 Results: make([]params.InitiateMigrationResult, len(reqArgs.Specs)), 392 } 393 if err := c.checkHasAdmin(); err != nil { 394 return out, errors.Trace(err) 395 } 396 397 for i, spec := range reqArgs.Specs { 398 result := &out.Results[i] 399 result.ModelTag = spec.ModelTag 400 id, err := c.initiateOneMigration(spec) 401 if err != nil { 402 result.Error = common.ServerError(err) 403 } else { 404 result.MigrationId = id 405 } 406 } 407 return out, nil 408 } 409 410 func (c *ControllerAPI) initiateOneMigration(spec params.MigrationSpec) (string, error) { 411 modelTag, err := names.ParseModelTag(spec.ModelTag) 412 if err != nil { 413 return "", errors.Annotate(err, "model tag") 414 } 415 416 // Ensure the model exists. 417 if _, err := c.state.GetModel(modelTag); err != nil { 418 return "", errors.Annotate(err, "unable to read model") 419 } 420 421 hostedState, err := c.state.ForModel(modelTag) 422 if err != nil { 423 return "", errors.Trace(err) 424 } 425 defer hostedState.Close() 426 427 // Construct target info. 428 specTarget := spec.TargetInfo 429 controllerTag, err := names.ParseControllerTag(specTarget.ControllerTag) 430 if err != nil { 431 return "", errors.Annotate(err, "controller tag") 432 } 433 authTag, err := names.ParseUserTag(specTarget.AuthTag) 434 if err != nil { 435 return "", errors.Annotate(err, "auth tag") 436 } 437 var macs []macaroon.Slice 438 if specTarget.Macaroons != "" { 439 if err := json.Unmarshal([]byte(specTarget.Macaroons), &macs); err != nil { 440 return "", errors.Annotate(err, "invalid macaroons") 441 } 442 } 443 targetInfo := coremigration.TargetInfo{ 444 ControllerTag: controllerTag, 445 Addrs: specTarget.Addrs, 446 CACert: specTarget.CACert, 447 AuthTag: authTag, 448 Password: specTarget.Password, 449 Macaroons: macs, 450 } 451 452 // Check if the migration is likely to succeed. 453 if !(spec.ExternalControl && spec.SkipInitialPrechecks) { 454 if err := runMigrationPrechecks(hostedState, targetInfo); err != nil { 455 return "", errors.Trace(err) 456 } 457 } 458 459 // Trigger the migration. 460 mig, err := hostedState.CreateMigration(state.MigrationSpec{ 461 InitiatedBy: c.apiUser, 462 TargetInfo: targetInfo, 463 ExternalControl: spec.ExternalControl, 464 }) 465 if err != nil { 466 return "", errors.Trace(err) 467 } 468 return mig.Id(), nil 469 } 470 471 func (c *ControllerAPI) modelStatus(tag string) (params.ModelStatus, error) { 472 var status params.ModelStatus 473 modelTag, err := names.ParseModelTag(tag) 474 if err != nil { 475 return status, errors.Trace(err) 476 } 477 st, err := c.state.ForModel(modelTag) 478 if err != nil { 479 return status, errors.Trace(err) 480 } 481 defer st.Close() 482 483 machines, err := st.AllMachines() 484 if err != nil { 485 return status, errors.Trace(err) 486 } 487 488 var hostedMachines []*state.Machine 489 for _, m := range machines { 490 if !m.IsManager() { 491 hostedMachines = append(hostedMachines, m) 492 } 493 } 494 495 applications, err := st.AllApplications() 496 if err != nil { 497 return status, errors.Trace(err) 498 } 499 500 model, err := st.Model() 501 if err != nil { 502 return status, errors.Trace(err) 503 } 504 if err != nil { 505 return status, errors.Trace(err) 506 } 507 508 modelMachines, err := common.ModelMachineInfo(common.NewModelManagerBackend(st)) 509 if err != nil { 510 return status, errors.Trace(err) 511 } 512 513 return params.ModelStatus{ 514 ModelTag: tag, 515 OwnerTag: model.Owner().String(), 516 Life: params.Life(model.Life().String()), 517 HostedMachineCount: len(hostedMachines), 518 ApplicationCount: len(applications), 519 Machines: modelMachines, 520 }, nil 521 } 522 523 // ModifyControllerAccess changes the model access granted to users. 524 func (c *ControllerAPI) ModifyControllerAccess(args params.ModifyControllerAccessRequest) (params.ErrorResults, error) { 525 result := params.ErrorResults{ 526 Results: make([]params.ErrorResult, len(args.Changes)), 527 } 528 if len(args.Changes) == 0 { 529 return result, nil 530 } 531 532 hasPermission, err := c.authorizer.HasPermission(permission.SuperuserAccess, c.state.ControllerTag()) 533 if err != nil { 534 return result, errors.Trace(err) 535 } 536 537 for i, arg := range args.Changes { 538 if !hasPermission { 539 result.Results[i].Error = common.ServerError(common.ErrPerm) 540 continue 541 } 542 543 controllerAccess := permission.Access(arg.Access) 544 if err := permission.ValidateControllerAccess(controllerAccess); err != nil { 545 result.Results[i].Error = common.ServerError(err) 546 continue 547 } 548 549 targetUserTag, err := names.ParseUserTag(arg.UserTag) 550 if err != nil { 551 result.Results[i].Error = common.ServerError(errors.Annotate(err, "could not modify controller access")) 552 continue 553 } 554 555 result.Results[i].Error = common.ServerError( 556 ChangeControllerAccess(c.state, c.apiUser, targetUserTag, arg.Action, controllerAccess)) 557 } 558 return result, nil 559 } 560 561 var runMigrationPrechecks = func(st *state.State, targetInfo coremigration.TargetInfo) error { 562 // Check model and source controller. 563 if err := migration.SourcePrecheck(migration.PrecheckShim(st)); err != nil { 564 return errors.Annotate(err, "source prechecks failed") 565 } 566 567 // Check target controller. 568 conn, err := api.Open(targetToAPIInfo(targetInfo), migration.ControllerDialOpts()) 569 if err != nil { 570 return errors.Annotate(err, "connect to target controller") 571 } 572 defer conn.Close() 573 modelInfo, err := makeModelInfo(st) 574 if err != nil { 575 return errors.Trace(err) 576 } 577 err = migrationtarget.NewClient(conn).Prechecks(modelInfo) 578 return errors.Annotate(err, "target prechecks failed") 579 } 580 581 func makeModelInfo(st *state.State) (coremigration.ModelInfo, error) { 582 var empty coremigration.ModelInfo 583 584 model, err := st.Model() 585 if err != nil { 586 return empty, errors.Trace(err) 587 } 588 conf, err := st.ModelConfig() 589 if err != nil { 590 return empty, errors.Trace(err) 591 } 592 agentVersion, _ := conf.AgentVersion() 593 return coremigration.ModelInfo{ 594 UUID: model.UUID(), 595 Name: model.Name(), 596 Owner: model.Owner(), 597 AgentVersion: agentVersion, 598 }, nil 599 } 600 601 func targetToAPIInfo(ti coremigration.TargetInfo) *api.Info { 602 return &api.Info{ 603 Addrs: ti.Addrs, 604 CACert: ti.CACert, 605 Tag: ti.AuthTag, 606 Password: ti.Password, 607 Macaroons: ti.Macaroons, 608 } 609 } 610 611 func grantControllerAccess(accessor *state.State, targetUserTag, apiUser names.UserTag, access permission.Access) error { 612 _, err := accessor.AddControllerUser(state.UserAccessSpec{User: targetUserTag, CreatedBy: apiUser, Access: access}) 613 if errors.IsAlreadyExists(err) { 614 controllerTag := accessor.ControllerTag() 615 controllerUser, err := accessor.UserAccess(targetUserTag, controllerTag) 616 if errors.IsNotFound(err) { 617 // Conflicts with prior check, must be inconsistent state. 618 err = txn.ErrExcessiveContention 619 } 620 if err != nil { 621 return errors.Annotate(err, "could not look up controller access for user") 622 } 623 624 // Only set access if greater access is being granted. 625 if controllerUser.Access.EqualOrGreaterControllerAccessThan(access) { 626 return errors.Errorf("user already has %q access or greater", access) 627 } 628 if _, err = accessor.SetUserAccess(controllerUser.UserTag, controllerUser.Object, access); err != nil { 629 return errors.Annotate(err, "could not set controller access for user") 630 } 631 return nil 632 633 } 634 if err != nil { 635 return errors.Trace(err) 636 } 637 return nil 638 } 639 640 func revokeControllerAccess(accessor *state.State, targetUserTag, apiUser names.UserTag, access permission.Access) error { 641 controllerTag := accessor.ControllerTag() 642 switch access { 643 case permission.LoginAccess: 644 // Revoking login access removes all access. 645 err := accessor.RemoveUserAccess(targetUserTag, controllerTag) 646 return errors.Annotate(err, "could not revoke controller access") 647 case permission.AddModelAccess: 648 // Revoking add-model access sets login. 649 controllerUser, err := accessor.UserAccess(targetUserTag, controllerTag) 650 if err != nil { 651 return errors.Annotate(err, "could not look up controller access for user") 652 } 653 _, err = accessor.SetUserAccess(controllerUser.UserTag, controllerUser.Object, permission.LoginAccess) 654 return errors.Annotate(err, "could not set controller access to read-only") 655 case permission.SuperuserAccess: 656 // Revoking superuser sets add-model. 657 controllerUser, err := accessor.UserAccess(targetUserTag, controllerTag) 658 if err != nil { 659 return errors.Annotate(err, "could not look up controller access for user") 660 } 661 _, err = accessor.SetUserAccess(controllerUser.UserTag, controllerUser.Object, permission.AddModelAccess) 662 return errors.Annotate(err, "could not set controller access to add-model") 663 664 default: 665 return errors.Errorf("don't know how to revoke %q access", access) 666 } 667 668 } 669 670 // ChangeControllerAccess performs the requested access grant or revoke action for the 671 // specified user on the controller. 672 func ChangeControllerAccess(accessor *state.State, apiUser, targetUserTag names.UserTag, action params.ControllerAction, access permission.Access) error { 673 switch action { 674 case params.GrantControllerAccess: 675 err := grantControllerAccess(accessor, targetUserTag, apiUser, access) 676 if err != nil { 677 return errors.Annotate(err, "could not grant controller access") 678 } 679 return nil 680 case params.RevokeControllerAccess: 681 return revokeControllerAccess(accessor, targetUserTag, apiUser, access) 682 default: 683 return errors.Errorf("unknown action %q", action) 684 } 685 } 686 687 func (o orderedBlockInfo) Swap(i, j int) { 688 o[i], o[j] = o[j], o[i] 689 } 690 691 type orderedUserModels []params.UserModel 692 693 func (o orderedUserModels) Len() int { 694 return len(o) 695 } 696 697 func (o orderedUserModels) Less(i, j int) bool { 698 if o[i].Name < o[j].Name { 699 return true 700 } 701 if o[i].Name > o[j].Name { 702 return false 703 } 704 705 if o[i].OwnerTag < o[j].OwnerTag { 706 return true 707 } 708 if o[i].OwnerTag > o[j].OwnerTag { 709 return false 710 } 711 712 // Unreachable based on the rules of there not being duplicate 713 // environments of the same name for the same owner, but return false 714 // instead of panicing. 715 return false 716 } 717 718 func (o orderedUserModels) Swap(i, j int) { 719 o[i], o[j] = o[j], o[i] 720 }