~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/apiserver/controller/controller.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
        "sort"
 
10
 
 
11
        "github.com/juju/errors"
 
12
        "github.com/juju/loggo"
 
13
        "github.com/juju/utils/set"
 
14
        "gopkg.in/juju/names.v2"
 
15
 
 
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"
 
23
)
 
24
 
 
25
var logger = loggo.GetLogger("juju.apiserver.controller")
 
26
 
 
27
func init() {
 
28
        common.RegisterStandardFacade("Controller", 3, NewControllerAPI)
 
29
}
 
30
 
 
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)
 
42
}
 
43
 
 
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
 
49
 
 
50
        state      *state.State
 
51
        authorizer facade.Authorizer
 
52
        apiUser    names.UserTag
 
53
        resources  facade.Resources
 
54
}
 
55
 
 
56
var _ Controller = (*ControllerAPI)(nil)
 
57
 
 
58
// NewControllerAPI creates a new api server endpoint for managing
 
59
// environments.
 
60
func NewControllerAPI(
 
61
        st *state.State,
 
62
        resources facade.Resources,
 
63
        authorizer facade.Authorizer,
 
64
) (*ControllerAPI, error) {
 
65
        if !authorizer.AuthClient() {
 
66
                return nil, errors.Trace(common.ErrPerm)
 
67
        }
 
68
 
 
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)
 
73
        if err != nil {
 
74
                return nil, errors.Trace(err)
 
75
        }
 
76
        // The entire end point is only accessible to controller administrators.
 
77
        if !isAdmin {
 
78
                return nil, errors.Trace(common.ErrPerm)
 
79
        }
 
80
 
 
81
        environConfigGetter := stateenvirons.EnvironConfigGetter{st}
 
82
        return &ControllerAPI{
 
83
                ControllerConfigAPI: common.NewControllerConfig(st),
 
84
                CloudSpecAPI:        cloudspec.NewCloudSpec(environConfigGetter.CloudSpec, common.AuthFuncForTag(st.ModelTag())),
 
85
                state:               st,
 
86
                authorizer:          authorizer,
 
87
                apiUser:             apiUser,
 
88
                resources:           resources,
 
89
        }, nil
 
90
}
 
91
 
 
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{}
 
96
 
 
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)
 
103
        if err != nil {
 
104
                return result, errors.Trace(err)
 
105
        }
 
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)
 
111
                }
 
112
                visibleEnvironments.Add(env.UUID())
 
113
                result.UserModels = append(result.UserModels, params.UserModel{
 
114
                        Model: params.Model{
 
115
                                Name:     env.Name(),
 
116
                                UUID:     env.UUID(),
 
117
                                OwnerTag: env.Owner().String(),
 
118
                        },
 
119
                        LastConnection: &lastConn,
 
120
                })
 
121
        }
 
122
 
 
123
        allEnvs, err := s.state.AllModels()
 
124
        if err != nil {
 
125
                return result, errors.Trace(err)
 
126
        }
 
127
 
 
128
        for _, env := range allEnvs {
 
129
                if !visibleEnvironments.Contains(env.UUID()) {
 
130
                        result.UserModels = append(result.UserModels, params.UserModel{
 
131
                                Model: params.Model{
 
132
                                        Name:     env.Name(),
 
133
                                        UUID:     env.UUID(),
 
134
                                        OwnerTag: env.Owner().String(),
 
135
                                },
 
136
                                // No LastConnection as this user hasn't.
 
137
                        })
 
138
                }
 
139
        }
 
140
 
 
141
        // Sort the resulting sequence by environment name, then owner.
 
142
        sort.Sort(orderedUserModels(result.UserModels))
 
143
 
 
144
        return result, nil
 
145
}
 
146
 
 
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
 
150
// list.
 
151
func (s *ControllerAPI) ListBlockedModels() (params.ModelBlockInfoList, error) {
 
152
        results := params.ModelBlockInfoList{}
 
153
 
 
154
        blocks, err := s.state.AllBlocksForController()
 
155
        if err != nil {
 
156
                return results, errors.Trace(err)
 
157
        }
 
158
 
 
159
        envBlocks := make(map[string][]string)
 
160
        for _, block := range blocks {
 
161
                uuid := block.ModelUUID()
 
162
                types, ok := envBlocks[uuid]
 
163
                if !ok {
 
164
                        types = []string{block.Type().String()}
 
165
                } else {
 
166
                        types = append(types, block.Type().String())
 
167
                }
 
168
                envBlocks[uuid] = types
 
169
        }
 
170
 
 
171
        for uuid, blocks := range envBlocks {
 
172
                envInfo, err := s.state.GetModel(names.NewModelTag(uuid))
 
173
                if err != nil {
 
174
                        logger.Debugf("Unable to get name for model: %s", uuid)
 
175
                        continue
 
176
                }
 
177
                results.Models = append(results.Models, params.ModelBlockInfo{
 
178
                        UUID:     envInfo.UUID(),
 
179
                        Name:     envInfo.Name(),
 
180
                        OwnerTag: envInfo.Owner().String(),
 
181
                        Blocks:   blocks,
 
182
                })
 
183
        }
 
184
 
 
185
        // Sort the resulting sequence by environment name, then owner.
 
186
        sort.Sort(orderedBlockInfo(results.Models))
 
187
 
 
188
        return results, nil
 
189
}
 
190
 
 
191
// ModelConfig returns the environment config for the controller
 
192
// environment.  For information on the current environment, use
 
193
// client.ModelGet
 
194
func (s *ControllerAPI) ModelConfig() (params.ModelConfigResults, error) {
 
195
        result := params.ModelConfigResults{}
 
196
 
 
197
        controllerModel, err := s.state.ControllerModel()
 
198
        if err != nil {
 
199
                return result, errors.Trace(err)
 
200
        }
 
201
 
 
202
        cfg, err := controllerModel.Config()
 
203
        if err != nil {
 
204
                return result, errors.Trace(err)
 
205
        }
 
206
 
 
207
        result.Config = make(map[string]params.ConfigValue)
 
208
        for name, val := range cfg.AllAttrs() {
 
209
                result.Config[name] = params.ConfigValue{
 
210
                        Value: val,
 
211
                }
 
212
        }
 
213
        return result, nil
 
214
}
 
215
 
 
216
// RemoveBlocks removes all the blocks in the controller.
 
217
func (s *ControllerAPI) RemoveBlocks(args params.RemoveBlocksArgs) error {
 
218
        if !args.All {
 
219
                return errors.New("not supported")
 
220
        }
 
221
        return errors.Trace(s.state.RemoveAllBlocksForController())
 
222
}
 
223
 
 
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),
 
231
        }, nil
 
232
}
 
233
 
 
234
type orderedBlockInfo []params.ModelBlockInfo
 
235
 
 
236
func (o orderedBlockInfo) Len() int {
 
237
        return len(o)
 
238
}
 
239
 
 
240
func (o orderedBlockInfo) Less(i, j int) bool {
 
241
        if o[i].Name < o[j].Name {
 
242
                return true
 
243
        }
 
244
        if o[i].Name > o[j].Name {
 
245
                return false
 
246
        }
 
247
 
 
248
        if o[i].OwnerTag < o[j].OwnerTag {
 
249
                return true
 
250
        }
 
251
        if o[i].OwnerTag > o[j].OwnerTag {
 
252
                return false
 
253
        }
 
254
 
 
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.
 
258
        return false
 
259
}
 
260
 
 
261
// ModelStatus returns a summary of the environment.
 
262
func (c *ControllerAPI) ModelStatus(req params.Entities) (params.ModelStatusResults, error) {
 
263
        envs := req.Entities
 
264
        results := params.ModelStatusResults{}
 
265
        status := make([]params.ModelStatus, len(envs))
 
266
        for i, env := range envs {
 
267
                envStatus, err := c.environStatus(env.Tag)
 
268
                if err != nil {
 
269
                        return results, errors.Trace(err)
 
270
                }
 
271
                status[i] = envStatus
 
272
        }
 
273
        results.Results = status
 
274
        return results, nil
 
275
}
 
276
 
 
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,
 
281
) {
 
282
        out := params.InitiateModelMigrationResults{
 
283
                Results: make([]params.InitiateModelMigrationResult, len(reqArgs.Specs)),
 
284
        }
 
285
        for i, spec := range reqArgs.Specs {
 
286
                result := &out.Results[i]
 
287
                result.ModelTag = spec.ModelTag
 
288
                id, err := c.initiateOneModelMigration(spec)
 
289
                if err != nil {
 
290
                        result.Error = common.ServerError(err)
 
291
                } else {
 
292
                        result.MigrationId = id
 
293
                }
 
294
        }
 
295
        return out, nil
 
296
}
 
297
 
 
298
func (c *ControllerAPI) initiateOneModelMigration(spec params.ModelMigrationSpec) (string, error) {
 
299
        modelTag, err := names.ParseModelTag(spec.ModelTag)
 
300
        if err != nil {
 
301
                return "", errors.Annotate(err, "model tag")
 
302
        }
 
303
 
 
304
        // Ensure the model exists.
 
305
        if _, err := c.state.GetModel(modelTag); err != nil {
 
306
                return "", errors.Annotate(err, "unable to read model")
 
307
        }
 
308
 
 
309
        // Get State for model.
 
310
        hostedState, err := c.state.ForModel(modelTag)
 
311
        if err != nil {
 
312
                return "", errors.Trace(err)
 
313
        }
 
314
        defer hostedState.Close()
 
315
 
 
316
        // Start the migration.
 
317
        targetInfo := spec.TargetInfo
 
318
 
 
319
        controllerTag, err := names.ParseModelTag(targetInfo.ControllerTag)
 
320
        if err != nil {
 
321
                return "", errors.Annotate(err, "controller tag")
 
322
        }
 
323
        authTag, err := names.ParseUserTag(targetInfo.AuthTag)
 
324
        if err != nil {
 
325
                return "", errors.Annotate(err, "auth tag")
 
326
        }
 
327
 
 
328
        args := state.ModelMigrationSpec{
 
329
                InitiatedBy: c.apiUser,
 
330
                TargetInfo: migration.TargetInfo{
 
331
                        ControllerTag: controllerTag,
 
332
                        Addrs:         targetInfo.Addrs,
 
333
                        CACert:        targetInfo.CACert,
 
334
                        AuthTag:       authTag,
 
335
                        Password:      targetInfo.Password,
 
336
                },
 
337
        }
 
338
        mig, err := hostedState.CreateModelMigration(args)
 
339
        if err != nil {
 
340
                return "", errors.Trace(err)
 
341
        }
 
342
        return mig.Id(), nil
 
343
}
 
344
 
 
345
func (c *ControllerAPI) environStatus(tag string) (params.ModelStatus, error) {
 
346
        var status params.ModelStatus
 
347
        modelTag, err := names.ParseModelTag(tag)
 
348
        if err != nil {
 
349
                return status, errors.Trace(err)
 
350
        }
 
351
        st, err := c.state.ForModel(modelTag)
 
352
        if err != nil {
 
353
                return status, errors.Trace(err)
 
354
        }
 
355
        defer st.Close()
 
356
 
 
357
        machines, err := st.AllMachines()
 
358
        if err != nil {
 
359
                return status, errors.Trace(err)
 
360
        }
 
361
 
 
362
        var hostedMachines []*state.Machine
 
363
        for _, m := range machines {
 
364
                if !m.IsManager() {
 
365
                        hostedMachines = append(hostedMachines, m)
 
366
                }
 
367
        }
 
368
 
 
369
        services, err := st.AllApplications()
 
370
        if err != nil {
 
371
                return status, errors.Trace(err)
 
372
        }
 
373
 
 
374
        env, err := st.Model()
 
375
        if err != nil {
 
376
                return status, errors.Trace(err)
 
377
        }
 
378
        if err != nil {
 
379
                return status, errors.Trace(err)
 
380
        }
 
381
 
 
382
        return params.ModelStatus{
 
383
                ModelTag:           tag,
 
384
                OwnerTag:           env.Owner().String(),
 
385
                Life:               params.Life(env.Life().String()),
 
386
                HostedMachineCount: len(hostedMachines),
 
387
                ApplicationCount:   len(services),
 
388
        }, nil
 
389
}
 
390
 
 
391
func (o orderedBlockInfo) Swap(i, j int) {
 
392
        o[i], o[j] = o[j], o[i]
 
393
}
 
394
 
 
395
type orderedUserModels []params.UserModel
 
396
 
 
397
func (o orderedUserModels) Len() int {
 
398
        return len(o)
 
399
}
 
400
 
 
401
func (o orderedUserModels) Less(i, j int) bool {
 
402
        if o[i].Name < o[j].Name {
 
403
                return true
 
404
        }
 
405
        if o[i].Name > o[j].Name {
 
406
                return false
 
407
        }
 
408
 
 
409
        if o[i].OwnerTag < o[j].OwnerTag {
 
410
                return true
 
411
        }
 
412
        if o[i].OwnerTag > o[j].OwnerTag {
 
413
                return false
 
414
        }
 
415
 
 
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.
 
419
        return false
 
420
}
 
421
 
 
422
func (o orderedUserModels) Swap(i, j int) {
 
423
        o[i], o[j] = o[j], o[i]
 
424
}