~juju-qa/ubuntu/xenial/juju/xenial-2.0-beta3

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/state/migration_export.go

  • Committer: Martin Packman
  • Date: 2016-03-30 19:31:08 UTC
  • mfrom: (1.1.41)
  • Revision ID: martin.packman@canonical.com-20160330193108-h9iz3ak334uk0z5r
Merge new upstream source 2.0~beta3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2016 Canonical Ltd.
 
2
// Licensed under the AGPLv3, see LICENCE file for details.
 
3
 
 
4
package state
 
5
 
 
6
import (
 
7
        "strings"
 
8
        "time"
 
9
 
 
10
        "github.com/juju/errors"
 
11
        "github.com/juju/loggo"
 
12
        "github.com/juju/names"
 
13
        "github.com/juju/utils/set"
 
14
        "gopkg.in/mgo.v2/bson"
 
15
 
 
16
        "github.com/juju/juju/core/description"
 
17
)
 
18
 
 
19
// Export the current model for the State.
 
20
func (st *State) Export() (description.Model, error) {
 
21
        dbModel, err := st.Model()
 
22
        if err != nil {
 
23
                return nil, errors.Trace(err)
 
24
        }
 
25
 
 
26
        export := exporter{
 
27
                st:      st,
 
28
                dbModel: dbModel,
 
29
                logger:  loggo.GetLogger("juju.state.export-model"),
 
30
        }
 
31
        if err := export.readAllStatuses(); err != nil {
 
32
                return nil, errors.Annotate(err, "reading statuses")
 
33
        }
 
34
        if err := export.readAllStatusHistory(); err != nil {
 
35
                return nil, errors.Trace(err)
 
36
        }
 
37
        if err := export.readAllSettings(); err != nil {
 
38
                return nil, errors.Trace(err)
 
39
        }
 
40
        if err := export.readAllAnnotations(); err != nil {
 
41
                return nil, errors.Trace(err)
 
42
        }
 
43
        if err := export.readAllConstraints(); err != nil {
 
44
                return nil, errors.Trace(err)
 
45
        }
 
46
 
 
47
        envConfig, found := export.settings[modelGlobalKey]
 
48
        if !found {
 
49
                return nil, errors.New("missing environ config")
 
50
        }
 
51
 
 
52
        blocks, err := export.readBlocks()
 
53
        if err != nil {
 
54
                return nil, errors.Trace(err)
 
55
        }
 
56
 
 
57
        args := description.ModelArgs{
 
58
                Owner:              dbModel.Owner(),
 
59
                Config:             envConfig.Settings,
 
60
                LatestToolsVersion: dbModel.LatestToolsVersion(),
 
61
                Blocks:             blocks,
 
62
        }
 
63
        export.model = description.NewModel(args)
 
64
        modelKey := dbModel.globalKey()
 
65
        export.model.SetAnnotations(export.getAnnotations(modelKey))
 
66
        if err := export.sequences(); err != nil {
 
67
                return nil, errors.Trace(err)
 
68
        }
 
69
        constraintsArgs, err := export.constraintsArgs(modelKey)
 
70
        if err != nil {
 
71
                return nil, errors.Trace(err)
 
72
        }
 
73
        export.model.SetConstraints(constraintsArgs)
 
74
 
 
75
        if err := export.modelUsers(); err != nil {
 
76
                return nil, errors.Trace(err)
 
77
        }
 
78
        if err := export.machines(); err != nil {
 
79
                return nil, errors.Trace(err)
 
80
        }
 
81
        if err := export.services(); err != nil {
 
82
                return nil, errors.Trace(err)
 
83
        }
 
84
        if err := export.relations(); err != nil {
 
85
                return nil, errors.Trace(err)
 
86
        }
 
87
 
 
88
        if err := export.model.Validate(); err != nil {
 
89
                return nil, errors.Trace(err)
 
90
        }
 
91
 
 
92
        export.logExtras()
 
93
 
 
94
        return export.model, nil
 
95
}
 
96
 
 
97
type exporter struct {
 
98
        st      *State
 
99
        dbModel *Model
 
100
        model   description.Model
 
101
        logger  loggo.Logger
 
102
 
 
103
        annotations   map[string]annotatorDoc
 
104
        constraints   map[string]bson.M
 
105
        settings      map[string]settingsDoc
 
106
        status        map[string]bson.M
 
107
        statusHistory map[string][]historicalStatusDoc
 
108
        // Map of service name to units. Populated as part
 
109
        // of the services export.
 
110
        units map[string][]*Unit
 
111
}
 
112
 
 
113
func (e *exporter) sequences() error {
 
114
        sequences, closer := e.st.getCollection(sequenceC)
 
115
        defer closer()
 
116
 
 
117
        var docs []sequenceDoc
 
118
        if err := sequences.Find(nil).All(&docs); err != nil {
 
119
                return errors.Trace(err)
 
120
        }
 
121
 
 
122
        for _, doc := range docs {
 
123
                e.model.SetSequence(doc.Name, doc.Counter)
 
124
        }
 
125
        return nil
 
126
}
 
127
 
 
128
func (e *exporter) readBlocks() (map[string]string, error) {
 
129
        blocks, closer := e.st.getCollection(blocksC)
 
130
        defer closer()
 
131
 
 
132
        var docs []blockDoc
 
133
        if err := blocks.Find(nil).All(&docs); err != nil {
 
134
                return nil, errors.Trace(err)
 
135
        }
 
136
 
 
137
        result := make(map[string]string)
 
138
        for _, doc := range docs {
 
139
                // We don't care about the id, uuid, or tag.
 
140
                // The uuid and tag both refer to the model uuid, and the
 
141
                // id is opaque - even though it is sequence generated.
 
142
                result[doc.Type.MigrationValue()] = doc.Message
 
143
        }
 
144
        return result, nil
 
145
}
 
146
 
 
147
func (e *exporter) modelUsers() error {
 
148
        users, err := e.dbModel.Users()
 
149
        if err != nil {
 
150
                return errors.Trace(err)
 
151
        }
 
152
        lastConnections, err := e.readLastConnectionTimes()
 
153
        if err != nil {
 
154
                return errors.Trace(err)
 
155
        }
 
156
 
 
157
        for _, user := range users {
 
158
                lastConn := lastConnections[strings.ToLower(user.UserName())]
 
159
                arg := description.UserArgs{
 
160
                        Name:           user.UserTag(),
 
161
                        DisplayName:    user.DisplayName(),
 
162
                        CreatedBy:      names.NewUserTag(user.CreatedBy()),
 
163
                        DateCreated:    user.DateCreated(),
 
164
                        LastConnection: lastConn,
 
165
                        ReadOnly:       user.ReadOnly(),
 
166
                }
 
167
                e.model.AddUser(arg)
 
168
        }
 
169
        return nil
 
170
}
 
171
 
 
172
func (e *exporter) machines() error {
 
173
        machines, err := e.st.AllMachines()
 
174
        if err != nil {
 
175
                return errors.Trace(err)
 
176
        }
 
177
        e.logger.Debugf("found %d machines", len(machines))
 
178
 
 
179
        instanceDataCollection, closer := e.st.getCollection(instanceDataC)
 
180
        defer closer()
 
181
 
 
182
        var instData []instanceData
 
183
        instances := make(map[string]instanceData)
 
184
        if err := instanceDataCollection.Find(nil).All(&instData); err != nil {
 
185
                return errors.Annotate(err, "instance data")
 
186
        }
 
187
        e.logger.Debugf("found %d instanceData", len(instData))
 
188
        for _, data := range instData {
 
189
                instances[data.MachineId] = data
 
190
        }
 
191
 
 
192
        // Read all the open ports documents.
 
193
        openedPorts, closer := e.st.getCollection(openedPortsC)
 
194
        defer closer()
 
195
        var portsData []portsDoc
 
196
        if err := openedPorts.Find(nil).All(&portsData); err != nil {
 
197
                return errors.Annotate(err, "opened ports")
 
198
        }
 
199
        e.logger.Debugf("found %d openedPorts docs", len(portsData))
 
200
 
 
201
        // We are iterating through a flat list of machines, but the migration
 
202
        // model stores the nesting. The AllMachines method assures us that the
 
203
        // machines are returned in an order so the parent will always before
 
204
        // any children.
 
205
        machineMap := make(map[string]description.Machine)
 
206
 
 
207
        for _, machine := range machines {
 
208
                e.logger.Debugf("export machine %s", machine.Id())
 
209
 
 
210
                var exParent description.Machine
 
211
                if parentId := ParentId(machine.Id()); parentId != "" {
 
212
                        var found bool
 
213
                        exParent, found = machineMap[parentId]
 
214
                        if !found {
 
215
                                return errors.Errorf("machine %s missing parent", machine.Id())
 
216
                        }
 
217
                }
 
218
 
 
219
                exMachine, err := e.newMachine(exParent, machine, instances, portsData)
 
220
                if err != nil {
 
221
                        return errors.Trace(err)
 
222
                }
 
223
                machineMap[machine.Id()] = exMachine
 
224
        }
 
225
 
 
226
        return nil
 
227
}
 
228
 
 
229
func (e *exporter) newMachine(exParent description.Machine, machine *Machine, instances map[string]instanceData, portsData []portsDoc) (description.Machine, error) {
 
230
        args := description.MachineArgs{
 
231
                Id:            machine.MachineTag(),
 
232
                Nonce:         machine.doc.Nonce,
 
233
                PasswordHash:  machine.doc.PasswordHash,
 
234
                Placement:     machine.doc.Placement,
 
235
                Series:        machine.doc.Series,
 
236
                ContainerType: machine.doc.ContainerType,
 
237
        }
 
238
 
 
239
        if supported, ok := machine.SupportedContainers(); ok {
 
240
                containers := make([]string, len(supported))
 
241
                for i, containerType := range supported {
 
242
                        containers[i] = string(containerType)
 
243
                }
 
244
                args.SupportedContainers = &containers
 
245
        }
 
246
 
 
247
        for _, job := range machine.Jobs() {
 
248
                args.Jobs = append(args.Jobs, job.MigrationValue())
 
249
        }
 
250
 
 
251
        // A null value means that we don't yet know which containers
 
252
        // are supported. An empty slice means 'no containers are supported'.
 
253
        var exMachine description.Machine
 
254
        if exParent == nil {
 
255
                exMachine = e.model.AddMachine(args)
 
256
        } else {
 
257
                exMachine = exParent.AddContainer(args)
 
258
        }
 
259
        exMachine.SetAddresses(
 
260
                e.newAddressArgsSlice(machine.doc.MachineAddresses),
 
261
                e.newAddressArgsSlice(machine.doc.Addresses))
 
262
        exMachine.SetPreferredAddresses(
 
263
                e.newAddressArgs(machine.doc.PreferredPublicAddress),
 
264
                e.newAddressArgs(machine.doc.PreferredPrivateAddress))
 
265
 
 
266
        // We fully expect the machine to have tools set, and that there is
 
267
        // some instance data.
 
268
        instData, found := instances[machine.doc.Id]
 
269
        if !found {
 
270
                return nil, errors.NotValidf("missing instance data for machine %s", machine.Id())
 
271
        }
 
272
        exMachine.SetInstance(e.newCloudInstanceArgs(instData))
 
273
 
 
274
        // Find the current machine status.
 
275
        globalKey := machine.globalKey()
 
276
        statusArgs, err := e.statusArgs(globalKey)
 
277
        if err != nil {
 
278
                return nil, errors.Annotatef(err, "status for machine %s", machine.Id())
 
279
        }
 
280
        exMachine.SetStatus(statusArgs)
 
281
        exMachine.SetStatusHistory(e.statusHistoryArgs(globalKey))
 
282
 
 
283
        tools, err := machine.AgentTools()
 
284
        if err != nil {
 
285
                // This means the tools aren't set, but they should be.
 
286
                return nil, errors.Trace(err)
 
287
        }
 
288
 
 
289
        exMachine.SetTools(description.AgentToolsArgs{
 
290
                Version: tools.Version,
 
291
                URL:     tools.URL,
 
292
                SHA256:  tools.SHA256,
 
293
                Size:    tools.Size,
 
294
        })
 
295
 
 
296
        for _, args := range e.networkPortsArgsForMachine(machine.Id(), portsData) {
 
297
                exMachine.AddNetworkPorts(args)
 
298
        }
 
299
 
 
300
        exMachine.SetAnnotations(e.getAnnotations(globalKey))
 
301
 
 
302
        constraintsArgs, err := e.constraintsArgs(globalKey)
 
303
        if err != nil {
 
304
                return nil, errors.Trace(err)
 
305
        }
 
306
        exMachine.SetConstraints(constraintsArgs)
 
307
 
 
308
        return exMachine, nil
 
309
}
 
310
 
 
311
func (e *exporter) networkPortsArgsForMachine(machineId string, portsData []portsDoc) []description.NetworkPortsArgs {
 
312
        var result []description.NetworkPortsArgs
 
313
        for _, doc := range portsData {
 
314
                // Don't bother including a network if there are no ports open on it.
 
315
                if doc.MachineID == machineId && len(doc.Ports) > 0 {
 
316
                        args := description.NetworkPortsArgs{NetworkName: doc.NetworkName}
 
317
                        for _, p := range doc.Ports {
 
318
                                args.OpenPorts = append(args.OpenPorts, description.PortRangeArgs{
 
319
                                        UnitName: p.UnitName,
 
320
                                        FromPort: p.FromPort,
 
321
                                        ToPort:   p.ToPort,
 
322
                                        Protocol: p.Protocol,
 
323
                                })
 
324
                        }
 
325
                        result = append(result, args)
 
326
                }
 
327
        }
 
328
        return result
 
329
}
 
330
 
 
331
func (e *exporter) newAddressArgsSlice(a []address) []description.AddressArgs {
 
332
        result := []description.AddressArgs{}
 
333
        for _, addr := range a {
 
334
                result = append(result, e.newAddressArgs(addr))
 
335
        }
 
336
        return result
 
337
}
 
338
 
 
339
func (e *exporter) newAddressArgs(a address) description.AddressArgs {
 
340
        return description.AddressArgs{
 
341
                Value:       a.Value,
 
342
                Type:        a.AddressType,
 
343
                NetworkName: a.NetworkName,
 
344
                Scope:       a.Scope,
 
345
                Origin:      a.Origin,
 
346
        }
 
347
}
 
348
 
 
349
func (e *exporter) newCloudInstanceArgs(data instanceData) description.CloudInstanceArgs {
 
350
        inst := description.CloudInstanceArgs{
 
351
                InstanceId: string(data.InstanceId),
 
352
                Status:     data.Status,
 
353
        }
 
354
        if data.Arch != nil {
 
355
                inst.Architecture = *data.Arch
 
356
        }
 
357
        if data.Mem != nil {
 
358
                inst.Memory = *data.Mem
 
359
        }
 
360
        if data.RootDisk != nil {
 
361
                inst.RootDisk = *data.RootDisk
 
362
        }
 
363
        if data.CpuCores != nil {
 
364
                inst.CpuCores = *data.CpuCores
 
365
        }
 
366
        if data.CpuPower != nil {
 
367
                inst.CpuPower = *data.CpuPower
 
368
        }
 
369
        if data.Tags != nil {
 
370
                inst.Tags = *data.Tags
 
371
        }
 
372
        if data.AvailZone != nil {
 
373
                inst.AvailabilityZone = *data.AvailZone
 
374
        }
 
375
        return inst
 
376
}
 
377
 
 
378
func (e *exporter) services() error {
 
379
        services, err := e.st.AllServices()
 
380
        if err != nil {
 
381
                return errors.Trace(err)
 
382
        }
 
383
        e.logger.Debugf("found %d services", len(services))
 
384
 
 
385
        refcounts, err := e.readAllSettingsRefCounts()
 
386
        if err != nil {
 
387
                return errors.Trace(err)
 
388
        }
 
389
 
 
390
        e.units, err = e.readAllUnits()
 
391
        if err != nil {
 
392
                return errors.Trace(err)
 
393
        }
 
394
 
 
395
        meterStatus, err := e.readAllMeterStatus()
 
396
        if err != nil {
 
397
                return errors.Trace(err)
 
398
        }
 
399
 
 
400
        leaders := e.readServiceLeaders()
 
401
 
 
402
        for _, service := range services {
 
403
                serviceUnits := e.units[service.Name()]
 
404
                leader := leaders[service.Name()]
 
405
                if err := e.addService(service, refcounts, serviceUnits, meterStatus, leader); err != nil {
 
406
                        return errors.Trace(err)
 
407
                }
 
408
        }
 
409
        return nil
 
410
}
 
411
 
 
412
func (e *exporter) readServiceLeaders() map[string]string {
 
413
        result := make(map[string]string)
 
414
        for key, value := range e.st.leadershipClient.Leases() {
 
415
                result[key] = value.Holder
 
416
        }
 
417
        return result
 
418
}
 
419
 
 
420
func (e *exporter) addService(service *Service, refcounts map[string]int, units []*Unit, meterStatus map[string]*meterStatusDoc, leader string) error {
 
421
        settingsKey := service.settingsKey()
 
422
        leadershipKey := leadershipSettingsKey(service.Name())
 
423
 
 
424
        serviceSettingsDoc, found := e.settings[settingsKey]
 
425
        if !found {
 
426
                return errors.Errorf("missing settings for service %q", service.Name())
 
427
        }
 
428
        refCount, found := refcounts[settingsKey]
 
429
        if !found {
 
430
                return errors.Errorf("missing settings refcount for service %q", service.Name())
 
431
        }
 
432
        leadershipSettingsDoc, found := e.settings[leadershipKey]
 
433
        if !found {
 
434
                return errors.Errorf("missing leadership settings for service %q", service.Name())
 
435
        }
 
436
 
 
437
        args := description.ServiceArgs{
 
438
                Tag:                  service.ServiceTag(),
 
439
                Series:               service.doc.Series,
 
440
                Subordinate:          service.doc.Subordinate,
 
441
                CharmURL:             service.doc.CharmURL.String(),
 
442
                CharmModifiedVersion: service.doc.CharmModifiedVersion,
 
443
                ForceCharm:           service.doc.ForceCharm,
 
444
                Exposed:              service.doc.Exposed,
 
445
                MinUnits:             service.doc.MinUnits,
 
446
                Settings:             serviceSettingsDoc.Settings,
 
447
                SettingsRefCount:     refCount,
 
448
                Leader:               leader,
 
449
                LeadershipSettings:   leadershipSettingsDoc.Settings,
 
450
                MetricsCredentials:   service.doc.MetricCredentials,
 
451
        }
 
452
        exService := e.model.AddService(args)
 
453
        // Find the current service status.
 
454
        globalKey := service.globalKey()
 
455
        statusArgs, err := e.statusArgs(globalKey)
 
456
        if err != nil {
 
457
                return errors.Annotatef(err, "status for service %s", service.Name())
 
458
        }
 
459
        exService.SetStatus(statusArgs)
 
460
        exService.SetStatusHistory(e.statusHistoryArgs(globalKey))
 
461
        exService.SetAnnotations(e.getAnnotations(globalKey))
 
462
 
 
463
        constraintsArgs, err := e.constraintsArgs(globalKey)
 
464
        if err != nil {
 
465
                return errors.Trace(err)
 
466
        }
 
467
        exService.SetConstraints(constraintsArgs)
 
468
 
 
469
        for _, unit := range units {
 
470
                agentKey := unit.globalAgentKey()
 
471
                unitMeterStatus, found := meterStatus[agentKey]
 
472
                if !found {
 
473
                        return errors.Errorf("missing meter status for unit %s", unit.Name())
 
474
                }
 
475
 
 
476
                args := description.UnitArgs{
 
477
                        Tag:             unit.UnitTag(),
 
478
                        Machine:         names.NewMachineTag(unit.doc.MachineId),
 
479
                        PasswordHash:    unit.doc.PasswordHash,
 
480
                        MeterStatusCode: unitMeterStatus.Code,
 
481
                        MeterStatusInfo: unitMeterStatus.Info,
 
482
                }
 
483
                if principalName, isSubordinate := unit.PrincipalName(); isSubordinate {
 
484
                        args.Principal = names.NewUnitTag(principalName)
 
485
                }
 
486
                if subs := unit.SubordinateNames(); len(subs) > 0 {
 
487
                        for _, subName := range subs {
 
488
                                args.Subordinates = append(args.Subordinates, names.NewUnitTag(subName))
 
489
                        }
 
490
                }
 
491
                exUnit := exService.AddUnit(args)
 
492
                // workload uses globalKey, agent uses globalAgentKey.
 
493
                globalKey := unit.globalKey()
 
494
                statusArgs, err := e.statusArgs(globalKey)
 
495
                if err != nil {
 
496
                        return errors.Annotatef(err, "workload status for unit %s", unit.Name())
 
497
                }
 
498
                exUnit.SetWorkloadStatus(statusArgs)
 
499
                exUnit.SetWorkloadStatusHistory(e.statusHistoryArgs(globalKey))
 
500
                statusArgs, err = e.statusArgs(agentKey)
 
501
                if err != nil {
 
502
                        return errors.Annotatef(err, "agent status for unit %s", unit.Name())
 
503
                }
 
504
                exUnit.SetAgentStatus(statusArgs)
 
505
                exUnit.SetAgentStatusHistory(e.statusHistoryArgs(agentKey))
 
506
 
 
507
                tools, err := unit.AgentTools()
 
508
                if err != nil {
 
509
                        // This means the tools aren't set, but they should be.
 
510
                        return errors.Trace(err)
 
511
                }
 
512
                exUnit.SetTools(description.AgentToolsArgs{
 
513
                        Version: tools.Version,
 
514
                        URL:     tools.URL,
 
515
                        SHA256:  tools.SHA256,
 
516
                        Size:    tools.Size,
 
517
                })
 
518
                exUnit.SetAnnotations(e.getAnnotations(globalKey))
 
519
 
 
520
                constraintsArgs, err := e.constraintsArgs(agentKey)
 
521
                if err != nil {
 
522
                        return errors.Trace(err)
 
523
                }
 
524
                exUnit.SetConstraints(constraintsArgs)
 
525
        }
 
526
 
 
527
        return nil
 
528
}
 
529
 
 
530
func (e *exporter) relations() error {
 
531
        rels, err := e.st.AllRelations()
 
532
        if err != nil {
 
533
                return errors.Trace(err)
 
534
        }
 
535
        e.logger.Debugf("read %d relations", len(rels))
 
536
 
 
537
        relationScopes, err := e.readAllRelationScopes()
 
538
        if err != nil {
 
539
                return errors.Trace(err)
 
540
        }
 
541
 
 
542
        for _, relation := range rels {
 
543
                exRelation := e.model.AddRelation(description.RelationArgs{
 
544
                        Id:  relation.Id(),
 
545
                        Key: relation.String(),
 
546
                })
 
547
                for _, ep := range relation.Endpoints() {
 
548
                        exEndPoint := exRelation.AddEndpoint(description.EndpointArgs{
 
549
                                ServiceName: ep.ServiceName,
 
550
                                Name:        ep.Name,
 
551
                                Role:        string(ep.Role),
 
552
                                Interface:   ep.Interface,
 
553
                                Optional:    ep.Optional,
 
554
                                Limit:       ep.Limit,
 
555
                                Scope:       string(ep.Scope),
 
556
                        })
 
557
                        // We expect a relationScope and settings for each of the
 
558
                        // units of the specified service.
 
559
                        units := e.units[ep.ServiceName]
 
560
                        for _, unit := range units {
 
561
                                ru, err := relation.Unit(unit)
 
562
                                if err != nil {
 
563
                                        return errors.Trace(err)
 
564
                                }
 
565
                                key := ru.key()
 
566
                                if !relationScopes.Contains(key) {
 
567
                                        return errors.Errorf("missing relation scope for %s and %s", relation, unit.Name())
 
568
                                }
 
569
                                settingsDoc, found := e.settings[key]
 
570
                                if !found {
 
571
                                        return errors.Errorf("missing relation settings for %s and %s", relation, unit.Name())
 
572
                                }
 
573
                                exEndPoint.SetUnitSettings(unit.Name(), settingsDoc.Settings)
 
574
                        }
 
575
                }
 
576
        }
 
577
        return nil
 
578
}
 
579
 
 
580
func (e *exporter) readAllRelationScopes() (set.Strings, error) {
 
581
        relationScopes, closer := e.st.getCollection(relationScopesC)
 
582
        defer closer()
 
583
 
 
584
        docs := []relationScopeDoc{}
 
585
        err := relationScopes.Find(nil).All(&docs)
 
586
        if err != nil {
 
587
                return nil, errors.Annotate(err, "cannot get all relation scopes")
 
588
        }
 
589
        e.logger.Debugf("found %d relationScope docs", len(docs))
 
590
 
 
591
        result := set.NewStrings()
 
592
        for _, doc := range docs {
 
593
                result.Add(doc.Key)
 
594
        }
 
595
        return result, nil
 
596
}
 
597
 
 
598
func (e *exporter) readAllUnits() (map[string][]*Unit, error) {
 
599
        unitsCollection, closer := e.st.getCollection(unitsC)
 
600
        defer closer()
 
601
 
 
602
        docs := []unitDoc{}
 
603
        err := unitsCollection.Find(nil).All(&docs)
 
604
        if err != nil {
 
605
                return nil, errors.Annotate(err, "cannot get all units")
 
606
        }
 
607
        e.logger.Debugf("found %d unit docs", len(docs))
 
608
        result := make(map[string][]*Unit)
 
609
        for _, doc := range docs {
 
610
                units := result[doc.Service]
 
611
                result[doc.Service] = append(units, newUnit(e.st, &doc))
 
612
        }
 
613
        return result, nil
 
614
}
 
615
 
 
616
func (e *exporter) readAllMeterStatus() (map[string]*meterStatusDoc, error) {
 
617
        meterStatuses, closer := e.st.getCollection(meterStatusC)
 
618
        defer closer()
 
619
 
 
620
        docs := []meterStatusDoc{}
 
621
        err := meterStatuses.Find(nil).All(&docs)
 
622
        if err != nil {
 
623
                return nil, errors.Annotate(err, "cannot get all meter status docs")
 
624
        }
 
625
        e.logger.Debugf("found %d meter status docs", len(docs))
 
626
        result := make(map[string]*meterStatusDoc)
 
627
        for _, doc := range docs {
 
628
                result[e.st.localID(doc.DocID)] = &doc
 
629
        }
 
630
        return result, nil
 
631
}
 
632
 
 
633
func (e *exporter) readLastConnectionTimes() (map[string]time.Time, error) {
 
634
        lastConnections, closer := e.st.getCollection(modelUserLastConnectionC)
 
635
        defer closer()
 
636
 
 
637
        var docs []modelUserLastConnectionDoc
 
638
        if err := lastConnections.Find(nil).All(&docs); err != nil {
 
639
                return nil, errors.Trace(err)
 
640
        }
 
641
 
 
642
        result := make(map[string]time.Time)
 
643
        for _, doc := range docs {
 
644
                result[doc.UserName] = doc.LastConnection.UTC()
 
645
        }
 
646
        return result, nil
 
647
}
 
648
 
 
649
func (e *exporter) readAllAnnotations() error {
 
650
        annotations, closer := e.st.getCollection(annotationsC)
 
651
        defer closer()
 
652
 
 
653
        var docs []annotatorDoc
 
654
        if err := annotations.Find(nil).All(&docs); err != nil {
 
655
                return errors.Trace(err)
 
656
        }
 
657
        e.logger.Debugf("read %d annotations docs", len(docs))
 
658
 
 
659
        e.annotations = make(map[string]annotatorDoc)
 
660
        for _, doc := range docs {
 
661
                e.annotations[doc.GlobalKey] = doc
 
662
        }
 
663
        return nil
 
664
}
 
665
 
 
666
func (e *exporter) readAllConstraints() error {
 
667
        constraintsCollection, closer := e.st.getCollection(constraintsC)
 
668
        defer closer()
 
669
 
 
670
        // Since the constraintsDoc doesn't include any global key or _id
 
671
        // fields, we can't just deserialize the entire collection into a slice
 
672
        // of docs, so we get them all out with bson maps.
 
673
        var docs []bson.M
 
674
        err := constraintsCollection.Find(nil).All(&docs)
 
675
        if err != nil {
 
676
                return errors.Annotate(err, "failed to read constraints collection")
 
677
        }
 
678
 
 
679
        e.logger.Debugf("read %d constraints docs", len(docs))
 
680
        e.constraints = make(map[string]bson.M)
 
681
        for _, doc := range docs {
 
682
                docId, ok := doc["_id"].(string)
 
683
                if !ok {
 
684
                        return errors.Errorf("expected string, got %s (%T)", doc["_id"], doc["_id"])
 
685
                }
 
686
                id := e.st.localID(docId)
 
687
                e.constraints[id] = doc
 
688
                e.logger.Debugf("doc[%q] = %#v", id, doc)
 
689
        }
 
690
        return nil
 
691
}
 
692
 
 
693
// getAnnotations doesn't really care if there are any there or not
 
694
// for the key, but if they were there, they are removed so we can
 
695
// check at the end of the export for anything we have forgotten.
 
696
func (e *exporter) getAnnotations(key string) map[string]string {
 
697
        result, found := e.annotations[key]
 
698
        if found {
 
699
                delete(e.annotations, key)
 
700
        }
 
701
        return result.Annotations
 
702
}
 
703
 
 
704
func (e *exporter) readAllSettings() error {
 
705
        settings, closer := e.st.getCollection(settingsC)
 
706
        defer closer()
 
707
 
 
708
        var docs []settingsDoc
 
709
        if err := settings.Find(nil).All(&docs); err != nil {
 
710
                return errors.Trace(err)
 
711
        }
 
712
 
 
713
        e.settings = make(map[string]settingsDoc)
 
714
        for _, doc := range docs {
 
715
                key := e.st.localID(doc.DocID)
 
716
                e.settings[key] = doc
 
717
        }
 
718
        return nil
 
719
}
 
720
 
 
721
func (e *exporter) readAllStatuses() error {
 
722
        statuses, closer := e.st.getCollection(statusesC)
 
723
        defer closer()
 
724
 
 
725
        var docs []bson.M
 
726
        err := statuses.Find(nil).All(&docs)
 
727
        if err != nil {
 
728
                return errors.Annotate(err, "failed to read status collection")
 
729
        }
 
730
 
 
731
        e.logger.Debugf("read %d status documents", len(docs))
 
732
        e.status = make(map[string]bson.M)
 
733
        for _, doc := range docs {
 
734
                docId, ok := doc["_id"].(string)
 
735
                if !ok {
 
736
                        return errors.Errorf("expected string, got %s (%T)", doc["_id"], doc["_id"])
 
737
                }
 
738
                id := e.st.localID(docId)
 
739
                e.status[id] = doc
 
740
        }
 
741
 
 
742
        return nil
 
743
}
 
744
 
 
745
func (e *exporter) readAllStatusHistory() error {
 
746
        statuses, closer := e.st.getCollection(statusesHistoryC)
 
747
        defer closer()
 
748
 
 
749
        count := 0
 
750
        e.statusHistory = make(map[string][]historicalStatusDoc)
 
751
        var doc historicalStatusDoc
 
752
        iter := statuses.Find(nil).Sort("-updated").Iter()
 
753
        defer iter.Close()
 
754
        for iter.Next(&doc) {
 
755
                history := e.statusHistory[doc.GlobalKey]
 
756
                e.statusHistory[doc.GlobalKey] = append(history, doc)
 
757
                count++
 
758
        }
 
759
 
 
760
        if err := iter.Err(); err != nil {
 
761
                return errors.Annotate(err, "failed to read status history collection")
 
762
        }
 
763
 
 
764
        e.logger.Debugf("read %d status history documents", count)
 
765
 
 
766
        return nil
 
767
}
 
768
 
 
769
func (e *exporter) statusArgs(globalKey string) (description.StatusArgs, error) {
 
770
        result := description.StatusArgs{}
 
771
        statusDoc, found := e.status[globalKey]
 
772
        if !found {
 
773
                return result, errors.NotFoundf("status data for %s", globalKey)
 
774
        }
 
775
 
 
776
        status, ok := statusDoc["status"].(string)
 
777
        if !ok {
 
778
                return result, errors.Errorf("expected string for status, got %T", statusDoc["status"])
 
779
        }
 
780
        info, ok := statusDoc["statusinfo"].(string)
 
781
        if !ok {
 
782
                return result, errors.Errorf("expected string for statusinfo, got %T", statusDoc["statusinfo"])
 
783
        }
 
784
        // data is an embedded map and comes out as a bson.M
 
785
        // A bson.M is map[string]interface{}, so we can type cast it.
 
786
        data, ok := statusDoc["statusdata"].(bson.M)
 
787
        if !ok {
 
788
                return result, errors.Errorf("expected map for data, got %T", statusDoc["statusdata"])
 
789
        }
 
790
        dataMap := map[string]interface{}(data)
 
791
        updated, ok := statusDoc["updated"].(int64)
 
792
        if !ok {
 
793
                return result, errors.Errorf("expected int64 for updated, got %T", statusDoc["updated"])
 
794
        }
 
795
 
 
796
        result.Value = status
 
797
        result.Message = info
 
798
        result.Data = dataMap
 
799
        result.Updated = time.Unix(0, updated)
 
800
        return result, nil
 
801
}
 
802
 
 
803
func (e *exporter) statusHistoryArgs(globalKey string) []description.StatusArgs {
 
804
        history := e.statusHistory[globalKey]
 
805
        result := make([]description.StatusArgs, len(history))
 
806
        e.logger.Debugf("found %d status history docs for %s", len(history), globalKey)
 
807
        for i, doc := range history {
 
808
                result[i] = description.StatusArgs{
 
809
                        Value:   string(doc.Status),
 
810
                        Message: doc.StatusInfo,
 
811
                        Data:    doc.StatusData,
 
812
                        Updated: time.Unix(0, doc.Updated),
 
813
                }
 
814
        }
 
815
 
 
816
        return result
 
817
}
 
818
 
 
819
func (e *exporter) constraintsArgs(globalKey string) (description.ConstraintsArgs, error) {
 
820
        doc, found := e.constraints[globalKey]
 
821
        if !found {
 
822
                // No constraints for this key.
 
823
                e.logger.Debugf("no constraints found for key %q", globalKey)
 
824
                return description.ConstraintsArgs{}, nil
 
825
        }
 
826
        // We capture any type error using a closure to avoid having to return
 
827
        // multiple values from the optional functions. This does mean that we will
 
828
        // only report on the last one, but that is fine as there shouldn't be any.
 
829
        var optionalErr error
 
830
        optionalString := func(name string) string {
 
831
                switch value := doc[name].(type) {
 
832
                case nil:
 
833
                case string:
 
834
                        return value
 
835
                default:
 
836
                        optionalErr = errors.Errorf("expected uint64 for %s, got %T", name, value)
 
837
                }
 
838
                return ""
 
839
        }
 
840
        optionalInt := func(name string) uint64 {
 
841
                switch value := doc[name].(type) {
 
842
                case nil:
 
843
                case uint64:
 
844
                        return value
 
845
                case int64:
 
846
                        return uint64(value)
 
847
                default:
 
848
                        optionalErr = errors.Errorf("expected uint64 for %s, got %T", name, value)
 
849
                }
 
850
                return 0
 
851
        }
 
852
        optionalStringSlice := func(name string) []string {
 
853
                switch value := doc[name].(type) {
 
854
                case nil:
 
855
                case []string:
 
856
                        return value
 
857
                default:
 
858
                        optionalErr = errors.Errorf("expected []string] for %s, got %T", name, value)
 
859
                }
 
860
                return nil
 
861
        }
 
862
        result := description.ConstraintsArgs{
 
863
                Architecture: optionalString("arch"),
 
864
                Container:    optionalString("container"),
 
865
                CpuCores:     optionalInt("cpucores"),
 
866
                CpuPower:     optionalInt("cpupower"),
 
867
                InstanceType: optionalString("instancetype"),
 
868
                Memory:       optionalInt("mem"),
 
869
                RootDisk:     optionalInt("rootdisk"),
 
870
                Spaces:       optionalStringSlice("spaces"),
 
871
                Tags:         optionalStringSlice("tags"),
 
872
        }
 
873
        if optionalErr != nil {
 
874
                return description.ConstraintsArgs{}, errors.Trace(optionalErr)
 
875
        }
 
876
        return result, nil
 
877
}
 
878
 
 
879
func (e *exporter) readAllSettingsRefCounts() (map[string]int, error) {
 
880
        refCounts, closer := e.st.getCollection(settingsrefsC)
 
881
        defer closer()
 
882
 
 
883
        var docs []bson.M
 
884
        err := refCounts.Find(nil).All(&docs)
 
885
        if err != nil {
 
886
                return nil, errors.Annotate(err, "failed to read settings refcount collection")
 
887
        }
 
888
 
 
889
        e.logger.Debugf("read %d settings refcount documents", len(docs))
 
890
        result := make(map[string]int)
 
891
        for _, doc := range docs {
 
892
                docId, ok := doc["_id"].(string)
 
893
                if !ok {
 
894
                        return nil, errors.Errorf("expected string, got %s (%T)", doc["_id"], doc["_id"])
 
895
                }
 
896
                id := e.st.localID(docId)
 
897
                count, ok := doc["refcount"].(int)
 
898
                if !ok {
 
899
                        return nil, errors.Errorf("expected int, got %s (%T)", doc["refcount"], doc["refcount"])
 
900
                }
 
901
                result[id] = count
 
902
        }
 
903
 
 
904
        return result, nil
 
905
}
 
906
 
 
907
func (e *exporter) logExtras() {
 
908
        // As annotations are saved into the model, they are removed from the
 
909
        // exporter's map. If there are any left at the end, we are missing
 
910
        // things. Not an error just now, just a warning that we have missed
 
911
        // something. Could potentially be an error at a later date when
 
912
        // migrations are complete (but probably not).
 
913
        for key, doc := range e.annotations {
 
914
                e.logger.Warningf("unexported annotation for %s, %s", doc.Tag, key)
 
915
        }
 
916
}