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