1
// Copyright 2015 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
4
// The controller package defines an API end point for functions dealing
5
// with controllers as a whole.
11
"github.com/juju/errors"
12
"github.com/juju/loggo"
13
"github.com/juju/utils/set"
14
"gopkg.in/juju/names.v2"
16
"github.com/juju/juju/apiserver/common"
17
"github.com/juju/juju/apiserver/common/cloudspec"
18
"github.com/juju/juju/apiserver/facade"
19
"github.com/juju/juju/apiserver/params"
20
"github.com/juju/juju/core/migration"
21
"github.com/juju/juju/state"
22
"github.com/juju/juju/state/stateenvirons"
25
var logger = loggo.GetLogger("juju.apiserver.controller")
28
common.RegisterStandardFacade("Controller", 3, NewControllerAPI)
31
// Controller defines the methods on the controller API end point.
32
type Controller interface {
33
AllModels() (params.UserModelList, error)
34
DestroyController(args params.DestroyControllerArgs) error
35
ModelConfig() (params.ModelConfigResults, error)
36
ControllerConfig() (params.ControllerConfigResult, error)
37
ListBlockedModels() (params.ModelBlockInfoList, error)
38
RemoveBlocks(args params.RemoveBlocksArgs) error
39
WatchAllModels() (params.AllWatcherId, error)
40
ModelStatus(req params.Entities) (params.ModelStatusResults, error)
41
InitiateModelMigration(params.InitiateModelMigrationArgs) (params.InitiateModelMigrationResults, error)
44
// ControllerAPI implements the environment manager interface and is
45
// the concrete implementation of the api end point.
46
type ControllerAPI struct {
47
*common.ControllerConfigAPI
48
cloudspec.CloudSpecAPI
51
authorizer facade.Authorizer
53
resources facade.Resources
56
var _ Controller = (*ControllerAPI)(nil)
58
// NewControllerAPI creates a new api server endpoint for managing
60
func NewControllerAPI(
62
resources facade.Resources,
63
authorizer facade.Authorizer,
64
) (*ControllerAPI, error) {
65
if !authorizer.AuthClient() {
66
return nil, errors.Trace(common.ErrPerm)
69
// Since we know this is a user tag (because AuthClient is true),
70
// we just do the type assertion to the UserTag.
71
apiUser, _ := authorizer.GetAuthTag().(names.UserTag)
72
isAdmin, err := st.IsControllerAdministrator(apiUser)
74
return nil, errors.Trace(err)
76
// The entire end point is only accessible to controller administrators.
78
return nil, errors.Trace(common.ErrPerm)
81
environConfigGetter := stateenvirons.EnvironConfigGetter{st}
82
return &ControllerAPI{
83
ControllerConfigAPI: common.NewControllerConfig(st),
84
CloudSpecAPI: cloudspec.NewCloudSpec(environConfigGetter.CloudSpec, common.AuthFuncForTag(st.ModelTag())),
86
authorizer: authorizer,
92
// AllModels allows controller administrators to get the list of all the
93
// environments in the controller.
94
func (s *ControllerAPI) AllModels() (params.UserModelList, error) {
95
result := params.UserModelList{}
97
// Get all the environments that the authenticated user can see, and
98
// supplement that with the other environments that exist that the user
99
// cannot see. The reason we do this is to get the LastConnection time for
100
// the environments that the user is able to see, so we have consistent
101
// output when listing with or without --all when an admin user.
102
environments, err := s.state.ModelsForUser(s.apiUser)
104
return result, errors.Trace(err)
106
visibleEnvironments := set.NewStrings()
107
for _, env := range environments {
108
lastConn, err := env.LastConnection()
109
if err != nil && !state.IsNeverConnectedError(err) {
110
return result, errors.Trace(err)
112
visibleEnvironments.Add(env.UUID())
113
result.UserModels = append(result.UserModels, params.UserModel{
117
OwnerTag: env.Owner().String(),
119
LastConnection: &lastConn,
123
allEnvs, err := s.state.AllModels()
125
return result, errors.Trace(err)
128
for _, env := range allEnvs {
129
if !visibleEnvironments.Contains(env.UUID()) {
130
result.UserModels = append(result.UserModels, params.UserModel{
134
OwnerTag: env.Owner().String(),
136
// No LastConnection as this user hasn't.
141
// Sort the resulting sequence by environment name, then owner.
142
sort.Sort(orderedUserModels(result.UserModels))
147
// ListBlockedModels returns a list of all environments on the controller
148
// which have a block in place. The resulting slice is sorted by environment
149
// name, then owner. Callers must be controller administrators to retrieve the
151
func (s *ControllerAPI) ListBlockedModels() (params.ModelBlockInfoList, error) {
152
results := params.ModelBlockInfoList{}
154
blocks, err := s.state.AllBlocksForController()
156
return results, errors.Trace(err)
159
envBlocks := make(map[string][]string)
160
for _, block := range blocks {
161
uuid := block.ModelUUID()
162
types, ok := envBlocks[uuid]
164
types = []string{block.Type().String()}
166
types = append(types, block.Type().String())
168
envBlocks[uuid] = types
171
for uuid, blocks := range envBlocks {
172
envInfo, err := s.state.GetModel(names.NewModelTag(uuid))
174
logger.Debugf("Unable to get name for model: %s", uuid)
177
results.Models = append(results.Models, params.ModelBlockInfo{
178
UUID: envInfo.UUID(),
179
Name: envInfo.Name(),
180
OwnerTag: envInfo.Owner().String(),
185
// Sort the resulting sequence by environment name, then owner.
186
sort.Sort(orderedBlockInfo(results.Models))
191
// ModelConfig returns the environment config for the controller
192
// environment. For information on the current environment, use
194
func (s *ControllerAPI) ModelConfig() (params.ModelConfigResults, error) {
195
result := params.ModelConfigResults{}
197
controllerModel, err := s.state.ControllerModel()
199
return result, errors.Trace(err)
202
cfg, err := controllerModel.Config()
204
return result, errors.Trace(err)
207
result.Config = make(map[string]params.ConfigValue)
208
for name, val := range cfg.AllAttrs() {
209
result.Config[name] = params.ConfigValue{
216
// RemoveBlocks removes all the blocks in the controller.
217
func (s *ControllerAPI) RemoveBlocks(args params.RemoveBlocksArgs) error {
219
return errors.New("not supported")
221
return errors.Trace(s.state.RemoveAllBlocksForController())
224
// WatchAllModels starts watching events for all models in the
225
// controller. The returned AllWatcherId should be used with Next on the
226
// AllModelWatcher endpoint to receive deltas.
227
func (c *ControllerAPI) WatchAllModels() (params.AllWatcherId, error) {
228
w := c.state.WatchAllModels()
229
return params.AllWatcherId{
230
AllWatcherId: c.resources.Register(w),
234
type orderedBlockInfo []params.ModelBlockInfo
236
func (o orderedBlockInfo) Len() int {
240
func (o orderedBlockInfo) Less(i, j int) bool {
241
if o[i].Name < o[j].Name {
244
if o[i].Name > o[j].Name {
248
if o[i].OwnerTag < o[j].OwnerTag {
251
if o[i].OwnerTag > o[j].OwnerTag {
255
// Unreachable based on the rules of there not being duplicate
256
// environments of the same name for the same owner, but return false
257
// instead of panicing.
261
// ModelStatus returns a summary of the environment.
262
func (c *ControllerAPI) ModelStatus(req params.Entities) (params.ModelStatusResults, error) {
264
results := params.ModelStatusResults{}
265
status := make([]params.ModelStatus, len(envs))
266
for i, env := range envs {
267
envStatus, err := c.environStatus(env.Tag)
269
return results, errors.Trace(err)
271
status[i] = envStatus
273
results.Results = status
277
// InitiateModelMigration attempts to begin the migration of one or
278
// more models to other controllers.
279
func (c *ControllerAPI) InitiateModelMigration(reqArgs params.InitiateModelMigrationArgs) (
280
params.InitiateModelMigrationResults, error,
282
out := params.InitiateModelMigrationResults{
283
Results: make([]params.InitiateModelMigrationResult, len(reqArgs.Specs)),
285
for i, spec := range reqArgs.Specs {
286
result := &out.Results[i]
287
result.ModelTag = spec.ModelTag
288
id, err := c.initiateOneModelMigration(spec)
290
result.Error = common.ServerError(err)
292
result.MigrationId = id
298
func (c *ControllerAPI) initiateOneModelMigration(spec params.ModelMigrationSpec) (string, error) {
299
modelTag, err := names.ParseModelTag(spec.ModelTag)
301
return "", errors.Annotate(err, "model tag")
304
// Ensure the model exists.
305
if _, err := c.state.GetModel(modelTag); err != nil {
306
return "", errors.Annotate(err, "unable to read model")
309
// Get State for model.
310
hostedState, err := c.state.ForModel(modelTag)
312
return "", errors.Trace(err)
314
defer hostedState.Close()
316
// Start the migration.
317
targetInfo := spec.TargetInfo
319
controllerTag, err := names.ParseModelTag(targetInfo.ControllerTag)
321
return "", errors.Annotate(err, "controller tag")
323
authTag, err := names.ParseUserTag(targetInfo.AuthTag)
325
return "", errors.Annotate(err, "auth tag")
328
args := state.ModelMigrationSpec{
329
InitiatedBy: c.apiUser,
330
TargetInfo: migration.TargetInfo{
331
ControllerTag: controllerTag,
332
Addrs: targetInfo.Addrs,
333
CACert: targetInfo.CACert,
335
Password: targetInfo.Password,
338
mig, err := hostedState.CreateModelMigration(args)
340
return "", errors.Trace(err)
345
func (c *ControllerAPI) environStatus(tag string) (params.ModelStatus, error) {
346
var status params.ModelStatus
347
modelTag, err := names.ParseModelTag(tag)
349
return status, errors.Trace(err)
351
st, err := c.state.ForModel(modelTag)
353
return status, errors.Trace(err)
357
machines, err := st.AllMachines()
359
return status, errors.Trace(err)
362
var hostedMachines []*state.Machine
363
for _, m := range machines {
365
hostedMachines = append(hostedMachines, m)
369
services, err := st.AllApplications()
371
return status, errors.Trace(err)
374
env, err := st.Model()
376
return status, errors.Trace(err)
379
return status, errors.Trace(err)
382
return params.ModelStatus{
384
OwnerTag: env.Owner().String(),
385
Life: params.Life(env.Life().String()),
386
HostedMachineCount: len(hostedMachines),
387
ApplicationCount: len(services),
391
func (o orderedBlockInfo) Swap(i, j int) {
392
o[i], o[j] = o[j], o[i]
395
type orderedUserModels []params.UserModel
397
func (o orderedUserModels) Len() int {
401
func (o orderedUserModels) Less(i, j int) bool {
402
if o[i].Name < o[j].Name {
405
if o[i].Name > o[j].Name {
409
if o[i].OwnerTag < o[j].OwnerTag {
412
if o[i].OwnerTag > o[j].OwnerTag {
416
// Unreachable based on the rules of there not being duplicate
417
// environments of the same name for the same owner, but return false
418
// instead of panicing.
422
func (o orderedUserModels) Swap(i, j int) {
423
o[i], o[j] = o[j], o[i]