~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/core/description/machine.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 2016 Canonical Ltd.
 
2
// Licensed under the AGPLv3, see LICENCE file for details.
 
3
 
 
4
package description
 
5
 
 
6
import (
 
7
        "github.com/juju/errors"
 
8
        "github.com/juju/schema"
 
9
        "github.com/juju/version"
 
10
        "gopkg.in/juju/names.v2"
 
11
)
 
12
 
 
13
type machines struct {
 
14
        Version   int        `yaml:"version"`
 
15
        Machines_ []*machine `yaml:"machines"`
 
16
}
 
17
 
 
18
type machine struct {
 
19
        Id_            string         `yaml:"id"`
 
20
        Nonce_         string         `yaml:"nonce"`
 
21
        PasswordHash_  string         `yaml:"password-hash"`
 
22
        Placement_     string         `yaml:"placement,omitempty"`
 
23
        Instance_      *cloudInstance `yaml:"instance,omitempty"`
 
24
        Series_        string         `yaml:"series"`
 
25
        ContainerType_ string         `yaml:"container-type,omitempty"`
 
26
 
 
27
        Status_        *status `yaml:"status"`
 
28
        StatusHistory_ `yaml:"status-history"`
 
29
 
 
30
        ProviderAddresses_ []*address `yaml:"provider-addresses,omitempty"`
 
31
        MachineAddresses_  []*address `yaml:"machine-addresses,omitempty"`
 
32
 
 
33
        PreferredPublicAddress_  *address `yaml:"preferred-public-address,omitempty"`
 
34
        PreferredPrivateAddress_ *address `yaml:"preferred-private-address,omitempty"`
 
35
 
 
36
        Tools_ *agentTools `yaml:"tools"`
 
37
        Jobs_  []string    `yaml:"jobs"`
 
38
 
 
39
        SupportedContainers_ *[]string `yaml:"supported-containers,omitempty"`
 
40
 
 
41
        Containers_ []*machine `yaml:"containers"`
 
42
 
 
43
        OpenedPorts_ *versionedOpenedPorts `yaml:"opened-ports,omitempty"`
 
44
 
 
45
        Annotations_ `yaml:"annotations,omitempty"`
 
46
 
 
47
        Constraints_ *constraints `yaml:"constraints,omitempty"`
 
48
 
 
49
        BlockDevices_ blockdevices `yaml:"block-devices,omitempty"`
 
50
}
 
51
 
 
52
// MachineArgs is an argument struct used to add a machine to the Model.
 
53
type MachineArgs struct {
 
54
        Id            names.MachineTag
 
55
        Nonce         string
 
56
        PasswordHash  string
 
57
        Placement     string
 
58
        Series        string
 
59
        ContainerType string
 
60
        Jobs          []string
 
61
        // A null value means that we don't yet know which containers
 
62
        // are supported. An empty slice means 'no containers are supported'.
 
63
        SupportedContainers *[]string
 
64
}
 
65
 
 
66
func newMachine(args MachineArgs) *machine {
 
67
        var jobs []string
 
68
        if count := len(args.Jobs); count > 0 {
 
69
                jobs = make([]string, count)
 
70
                copy(jobs, args.Jobs)
 
71
        }
 
72
        m := &machine{
 
73
                Id_:            args.Id.Id(),
 
74
                Nonce_:         args.Nonce,
 
75
                PasswordHash_:  args.PasswordHash,
 
76
                Placement_:     args.Placement,
 
77
                Series_:        args.Series,
 
78
                ContainerType_: args.ContainerType,
 
79
                Jobs_:          jobs,
 
80
                StatusHistory_: newStatusHistory(),
 
81
        }
 
82
        if args.SupportedContainers != nil {
 
83
                supported := make([]string, len(*args.SupportedContainers))
 
84
                copy(supported, *args.SupportedContainers)
 
85
                m.SupportedContainers_ = &supported
 
86
        }
 
87
        m.setBlockDevices(nil)
 
88
        return m
 
89
}
 
90
 
 
91
// Id implements Machine.
 
92
func (m *machine) Id() string {
 
93
        return m.Id_
 
94
}
 
95
 
 
96
// Tag implements Machine.
 
97
func (m *machine) Tag() names.MachineTag {
 
98
        return names.NewMachineTag(m.Id_)
 
99
}
 
100
 
 
101
// Nonce implements Machine.
 
102
func (m *machine) Nonce() string {
 
103
        return m.Nonce_
 
104
}
 
105
 
 
106
// PasswordHash implements Machine.
 
107
func (m *machine) PasswordHash() string {
 
108
        return m.PasswordHash_
 
109
}
 
110
 
 
111
// Placement implements Machine.
 
112
func (m *machine) Placement() string {
 
113
        return m.Placement_
 
114
}
 
115
 
 
116
// Instance implements Machine.
 
117
func (m *machine) Instance() CloudInstance {
 
118
        // To avoid typed nils check nil here.
 
119
        if m.Instance_ == nil {
 
120
                return nil
 
121
        }
 
122
        return m.Instance_
 
123
}
 
124
 
 
125
// SetInstance implements Machine.
 
126
func (m *machine) SetInstance(args CloudInstanceArgs) {
 
127
        m.Instance_ = newCloudInstance(args)
 
128
}
 
129
 
 
130
// Series implements Machine.
 
131
func (m *machine) Series() string {
 
132
        return m.Series_
 
133
}
 
134
 
 
135
// ContainerType implements Machine.
 
136
func (m *machine) ContainerType() string {
 
137
        return m.ContainerType_
 
138
}
 
139
 
 
140
// Status implements Machine.
 
141
func (m *machine) Status() Status {
 
142
        // To avoid typed nils check nil here.
 
143
        if m.Status_ == nil {
 
144
                return nil
 
145
        }
 
146
        return m.Status_
 
147
}
 
148
 
 
149
// SetStatus implements Machine.
 
150
func (m *machine) SetStatus(args StatusArgs) {
 
151
        m.Status_ = newStatus(args)
 
152
}
 
153
 
 
154
// ProviderAddresses implements Machine.
 
155
func (m *machine) ProviderAddresses() []Address {
 
156
        var result []Address
 
157
        for _, addr := range m.ProviderAddresses_ {
 
158
                result = append(result, addr)
 
159
        }
 
160
        return result
 
161
}
 
162
 
 
163
// MachineAddresses implements Machine.
 
164
func (m *machine) MachineAddresses() []Address {
 
165
        var result []Address
 
166
        for _, addr := range m.MachineAddresses_ {
 
167
                result = append(result, addr)
 
168
        }
 
169
        return result
 
170
}
 
171
 
 
172
// SetAddresses implements Machine.
 
173
func (m *machine) SetAddresses(margs []AddressArgs, pargs []AddressArgs) {
 
174
        m.MachineAddresses_ = nil
 
175
        m.ProviderAddresses_ = nil
 
176
        for _, args := range margs {
 
177
                if args.Value != "" {
 
178
                        m.MachineAddresses_ = append(m.MachineAddresses_, newAddress(args))
 
179
                }
 
180
        }
 
181
        for _, args := range pargs {
 
182
                if args.Value != "" {
 
183
                        m.ProviderAddresses_ = append(m.ProviderAddresses_, newAddress(args))
 
184
                }
 
185
        }
 
186
}
 
187
 
 
188
// PreferredPublicAddress implements Machine.
 
189
func (m *machine) PreferredPublicAddress() Address {
 
190
        // To avoid typed nils check nil here.
 
191
        if m.PreferredPublicAddress_ == nil {
 
192
                return nil
 
193
        }
 
194
        return m.PreferredPublicAddress_
 
195
}
 
196
 
 
197
// PreferredPrivateAddress implements Machine.
 
198
func (m *machine) PreferredPrivateAddress() Address {
 
199
        // To avoid typed nils check nil here.
 
200
        if m.PreferredPrivateAddress_ == nil {
 
201
                return nil
 
202
        }
 
203
        return m.PreferredPrivateAddress_
 
204
}
 
205
 
 
206
// SetPreferredAddresses implements Machine.
 
207
func (m *machine) SetPreferredAddresses(public AddressArgs, private AddressArgs) {
 
208
        if public.Value != "" {
 
209
                m.PreferredPublicAddress_ = newAddress(public)
 
210
        }
 
211
        if private.Value != "" {
 
212
                m.PreferredPrivateAddress_ = newAddress(private)
 
213
        }
 
214
}
 
215
 
 
216
// Tools implements Machine.
 
217
func (m *machine) Tools() AgentTools {
 
218
        // To avoid a typed nil, check before returning.
 
219
        if m.Tools_ == nil {
 
220
                return nil
 
221
        }
 
222
        return m.Tools_
 
223
}
 
224
 
 
225
// SetTools implements Machine.
 
226
func (m *machine) SetTools(args AgentToolsArgs) {
 
227
        m.Tools_ = newAgentTools(args)
 
228
}
 
229
 
 
230
// Jobs implements Machine.
 
231
func (m *machine) Jobs() []string {
 
232
        return m.Jobs_
 
233
}
 
234
 
 
235
// SupportedContainers implements Machine.
 
236
func (m *machine) SupportedContainers() ([]string, bool) {
 
237
        if m.SupportedContainers_ == nil {
 
238
                return nil, false
 
239
        }
 
240
        return *m.SupportedContainers_, true
 
241
}
 
242
 
 
243
// Containers implements Machine.
 
244
func (m *machine) Containers() []Machine {
 
245
        var result []Machine
 
246
        for _, container := range m.Containers_ {
 
247
                result = append(result, container)
 
248
        }
 
249
        return result
 
250
}
 
251
 
 
252
// BlockDevices implements Machine.
 
253
func (m *machine) BlockDevices() []BlockDevice {
 
254
        var result []BlockDevice
 
255
        for _, device := range m.BlockDevices_.BlockDevices_ {
 
256
                result = append(result, device)
 
257
        }
 
258
        return result
 
259
}
 
260
 
 
261
// AddBlockDevice implements Machine.
 
262
func (m *machine) AddBlockDevice(args BlockDeviceArgs) BlockDevice {
 
263
        return m.BlockDevices_.add(args)
 
264
}
 
265
 
 
266
func (m *machine) setBlockDevices(devices []*blockdevice) {
 
267
        m.BlockDevices_ = blockdevices{
 
268
                Version:       1,
 
269
                BlockDevices_: devices,
 
270
        }
 
271
}
 
272
 
 
273
// AddContainer implements Machine.
 
274
func (m *machine) AddContainer(args MachineArgs) Machine {
 
275
        container := newMachine(args)
 
276
        m.Containers_ = append(m.Containers_, container)
 
277
        return container
 
278
}
 
279
 
 
280
// OpenedPorts implements Machine.
 
281
func (m *machine) OpenedPorts() []OpenedPorts {
 
282
        if m.OpenedPorts_ == nil {
 
283
                return nil
 
284
        }
 
285
        var result []OpenedPorts
 
286
        for _, ports := range m.OpenedPorts_.OpenedPorts_ {
 
287
                result = append(result, ports)
 
288
        }
 
289
        return result
 
290
}
 
291
 
 
292
// AddOpenedPorts implements Machine.
 
293
func (m *machine) AddOpenedPorts(args OpenedPortsArgs) OpenedPorts {
 
294
        if m.OpenedPorts_ == nil {
 
295
                m.OpenedPorts_ = &versionedOpenedPorts{Version: 1}
 
296
        }
 
297
        ports := newOpenedPorts(args)
 
298
        m.OpenedPorts_.OpenedPorts_ = append(m.OpenedPorts_.OpenedPorts_, ports)
 
299
        return ports
 
300
}
 
301
 
 
302
func (m *machine) setOpenedPorts(portsList []*openedPorts) {
 
303
        m.OpenedPorts_ = &versionedOpenedPorts{
 
304
                Version:      1,
 
305
                OpenedPorts_: portsList,
 
306
        }
 
307
}
 
308
 
 
309
// Constraints implements HasConstraints.
 
310
func (m *machine) Constraints() Constraints {
 
311
        if m.Constraints_ == nil {
 
312
                return nil
 
313
        }
 
314
        return m.Constraints_
 
315
}
 
316
 
 
317
// SetConstraints implements HasConstraints.
 
318
func (m *machine) SetConstraints(args ConstraintsArgs) {
 
319
        m.Constraints_ = newConstraints(args)
 
320
}
 
321
 
 
322
// Validate implements Machine.
 
323
func (m *machine) Validate() error {
 
324
        if m.Id_ == "" {
 
325
                return errors.NotValidf("machine missing id")
 
326
        }
 
327
        if m.Status_ == nil {
 
328
                return errors.NotValidf("machine %q missing status", m.Id_)
 
329
        }
 
330
        // Since all exports should be done when machines are stable,
 
331
        // there should always be tools and cloud instance.
 
332
        if m.Tools_ == nil {
 
333
                return errors.NotValidf("machine %q missing tools", m.Id_)
 
334
        }
 
335
        if m.Instance_ == nil {
 
336
                return errors.NotValidf("machine %q missing instance", m.Id_)
 
337
        }
 
338
        for _, container := range m.Containers_ {
 
339
                if err := container.Validate(); err != nil {
 
340
                        return errors.Trace(err)
 
341
                }
 
342
        }
 
343
 
 
344
        return nil
 
345
}
 
346
 
 
347
func importMachines(source map[string]interface{}) ([]*machine, error) {
 
348
        checker := versionedChecker("machines")
 
349
        coerced, err := checker.Coerce(source, nil)
 
350
        if err != nil {
 
351
                return nil, errors.Annotatef(err, "machines version schema check failed")
 
352
        }
 
353
        valid := coerced.(map[string]interface{})
 
354
 
 
355
        version := int(valid["version"].(int64))
 
356
        importFunc, ok := machineDeserializationFuncs[version]
 
357
        if !ok {
 
358
                return nil, errors.NotValidf("version %d", version)
 
359
        }
 
360
        sourceList := valid["machines"].([]interface{})
 
361
        return importMachineList(sourceList, importFunc)
 
362
}
 
363
 
 
364
func importMachineList(sourceList []interface{}, importFunc machineDeserializationFunc) ([]*machine, error) {
 
365
        result := make([]*machine, 0, len(sourceList))
 
366
        for i, value := range sourceList {
 
367
                source, ok := value.(map[string]interface{})
 
368
                if !ok {
 
369
                        return nil, errors.Errorf("unexpected value for machine %d, %T", i, value)
 
370
                }
 
371
                machine, err := importFunc(source)
 
372
                if err != nil {
 
373
                        return nil, errors.Annotatef(err, "machine %d", i)
 
374
                }
 
375
                result = append(result, machine)
 
376
        }
 
377
        return result, nil
 
378
}
 
379
 
 
380
type machineDeserializationFunc func(map[string]interface{}) (*machine, error)
 
381
 
 
382
var machineDeserializationFuncs = map[int]machineDeserializationFunc{
 
383
        1: importMachineV1,
 
384
}
 
385
 
 
386
func importMachineV1(source map[string]interface{}) (*machine, error) {
 
387
        fields := schema.Fields{
 
388
                "id":                   schema.String(),
 
389
                "nonce":                schema.String(),
 
390
                "password-hash":        schema.String(),
 
391
                "placement":            schema.String(),
 
392
                "instance":             schema.StringMap(schema.Any()),
 
393
                "series":               schema.String(),
 
394
                "container-type":       schema.String(),
 
395
                "jobs":                 schema.List(schema.String()),
 
396
                "status":               schema.StringMap(schema.Any()),
 
397
                "supported-containers": schema.List(schema.String()),
 
398
                "tools":                schema.StringMap(schema.Any()),
 
399
                "containers":           schema.List(schema.StringMap(schema.Any())),
 
400
                "opened-ports":         schema.StringMap(schema.Any()),
 
401
 
 
402
                "provider-addresses":        schema.List(schema.StringMap(schema.Any())),
 
403
                "machine-addresses":         schema.List(schema.StringMap(schema.Any())),
 
404
                "preferred-public-address":  schema.StringMap(schema.Any()),
 
405
                "preferred-private-address": schema.StringMap(schema.Any()),
 
406
 
 
407
                "block-devices": schema.StringMap(schema.Any()),
 
408
        }
 
409
 
 
410
        defaults := schema.Defaults{
 
411
                "placement":      "",
 
412
                "container-type": "",
 
413
                // Even though we are expecting instance data for every machine,
 
414
                // it isn't strictly necessary, so we allow it to not exist here.
 
415
                "instance":                  schema.Omit,
 
416
                "supported-containers":      schema.Omit,
 
417
                "opened-ports":              schema.Omit,
 
418
                "block-devices":             schema.Omit,
 
419
                "provider-addresses":        schema.Omit,
 
420
                "machine-addresses":         schema.Omit,
 
421
                "preferred-public-address":  schema.Omit,
 
422
                "preferred-private-address": schema.Omit,
 
423
        }
 
424
        addAnnotationSchema(fields, defaults)
 
425
        addConstraintsSchema(fields, defaults)
 
426
        addStatusHistorySchema(fields)
 
427
        checker := schema.FieldMap(fields, defaults)
 
428
 
 
429
        coerced, err := checker.Coerce(source, nil)
 
430
        if err != nil {
 
431
                return nil, errors.Annotatef(err, "machine v1 schema check failed")
 
432
        }
 
433
        valid := coerced.(map[string]interface{})
 
434
        // From here we know that the map returned from the schema coercion
 
435
        // contains fields of the right type.
 
436
        result := &machine{
 
437
                Id_:            valid["id"].(string),
 
438
                Nonce_:         valid["nonce"].(string),
 
439
                PasswordHash_:  valid["password-hash"].(string),
 
440
                Placement_:     valid["placement"].(string),
 
441
                Series_:        valid["series"].(string),
 
442
                ContainerType_: valid["container-type"].(string),
 
443
                StatusHistory_: newStatusHistory(),
 
444
                Jobs_:          convertToStringSlice(valid["jobs"]),
 
445
        }
 
446
        result.importAnnotations(valid)
 
447
        if err := result.importStatusHistory(valid); err != nil {
 
448
                return nil, errors.Trace(err)
 
449
        }
 
450
 
 
451
        if constraintsMap, ok := valid["constraints"]; ok {
 
452
                constraints, err := importConstraints(constraintsMap.(map[string]interface{}))
 
453
                if err != nil {
 
454
                        return nil, errors.Trace(err)
 
455
                }
 
456
                result.Constraints_ = constraints
 
457
        }
 
458
 
 
459
        if supported, ok := valid["supported-containers"]; ok {
 
460
                supportedList := supported.([]interface{})
 
461
                s := make([]string, len(supportedList))
 
462
                for i, containerType := range supportedList {
 
463
                        s[i] = containerType.(string)
 
464
                }
 
465
                result.SupportedContainers_ = &s
 
466
        }
 
467
 
 
468
        if instanceMap, ok := valid["instance"]; ok {
 
469
                instance, err := importCloudInstance(instanceMap.(map[string]interface{}))
 
470
                if err != nil {
 
471
                        return nil, errors.Trace(err)
 
472
                }
 
473
                result.Instance_ = instance
 
474
        }
 
475
 
 
476
        if blockDeviceMap, ok := valid["block-devices"]; ok {
 
477
                devices, err := importBlockDevices(blockDeviceMap.(map[string]interface{}))
 
478
                if err != nil {
 
479
                        return nil, errors.Trace(err)
 
480
                }
 
481
                result.setBlockDevices(devices)
 
482
        } else {
 
483
                result.setBlockDevices(nil)
 
484
        }
 
485
 
 
486
        // Tools and status are required, so we expect them to be there.
 
487
        tools, err := importAgentTools(valid["tools"].(map[string]interface{}))
 
488
        if err != nil {
 
489
                return nil, errors.Trace(err)
 
490
        }
 
491
        result.Tools_ = tools
 
492
 
 
493
        status, err := importStatus(valid["status"].(map[string]interface{}))
 
494
        if err != nil {
 
495
                return nil, errors.Trace(err)
 
496
        }
 
497
        result.Status_ = status
 
498
 
 
499
        if addresses, ok := valid["provider-addresses"]; ok {
 
500
                providerAddresses, err := importAddresses(addresses.([]interface{}))
 
501
                if err != nil {
 
502
                        return nil, errors.Trace(err)
 
503
                }
 
504
                result.ProviderAddresses_ = providerAddresses
 
505
        }
 
506
 
 
507
        if addresses, ok := valid["machine-addresses"]; ok {
 
508
                machineAddresses, err := importAddresses(addresses.([]interface{}))
 
509
                if err != nil {
 
510
                        return nil, errors.Trace(err)
 
511
                }
 
512
                result.MachineAddresses_ = machineAddresses
 
513
        }
 
514
 
 
515
        if address, ok := valid["preferred-public-address"]; ok {
 
516
                publicAddress, err := importAddress(address.(map[string]interface{}))
 
517
                if err != nil {
 
518
                        return nil, errors.Trace(err)
 
519
                }
 
520
                result.PreferredPublicAddress_ = publicAddress
 
521
        }
 
522
 
 
523
        if address, ok := valid["preferred-private-address"]; ok {
 
524
                privateAddress, err := importAddress(address.(map[string]interface{}))
 
525
                if err != nil {
 
526
                        return nil, errors.Trace(err)
 
527
                }
 
528
                result.PreferredPrivateAddress_ = privateAddress
 
529
        }
 
530
 
 
531
        machineList := valid["containers"].([]interface{})
 
532
        machines, err := importMachineList(machineList, importMachineV1)
 
533
        if err != nil {
 
534
                return nil, errors.Annotatef(err, "containers")
 
535
        }
 
536
        result.Containers_ = machines
 
537
 
 
538
        if portsMap, ok := valid["opened-ports"]; ok {
 
539
                portsList, err := importOpenedPorts(portsMap.(map[string]interface{}))
 
540
                if err != nil {
 
541
                        return nil, errors.Trace(err)
 
542
                }
 
543
                result.setOpenedPorts(portsList)
 
544
        }
 
545
 
 
546
        return result, nil
 
547
 
 
548
}
 
549
 
 
550
// CloudInstanceArgs is an argument struct used to add information about the
 
551
// cloud instance to a Machine.
 
552
type CloudInstanceArgs struct {
 
553
        InstanceId       string
 
554
        Status           string
 
555
        Architecture     string
 
556
        Memory           uint64
 
557
        RootDisk         uint64
 
558
        CpuCores         uint64
 
559
        CpuPower         uint64
 
560
        Tags             []string
 
561
        AvailabilityZone string
 
562
}
 
563
 
 
564
func newCloudInstance(args CloudInstanceArgs) *cloudInstance {
 
565
        tags := make([]string, len(args.Tags))
 
566
        copy(tags, args.Tags)
 
567
        return &cloudInstance{
 
568
                Version:           1,
 
569
                InstanceId_:       args.InstanceId,
 
570
                Status_:           args.Status,
 
571
                Architecture_:     args.Architecture,
 
572
                Memory_:           args.Memory,
 
573
                RootDisk_:         args.RootDisk,
 
574
                CpuCores_:         args.CpuCores,
 
575
                CpuPower_:         args.CpuPower,
 
576
                Tags_:             tags,
 
577
                AvailabilityZone_: args.AvailabilityZone,
 
578
        }
 
579
}
 
580
 
 
581
type cloudInstance struct {
 
582
        Version int `yaml:"version"`
 
583
 
 
584
        InstanceId_ string `yaml:"instance-id"`
 
585
        Status_     string `yaml:"status"`
 
586
        // For all the optional values, empty values make no sense, and
 
587
        // it would be better to have them not set rather than set with
 
588
        // a nonsense value.
 
589
        Architecture_     string   `yaml:"architecture,omitempty"`
 
590
        Memory_           uint64   `yaml:"memory,omitempty"`
 
591
        RootDisk_         uint64   `yaml:"root-disk,omitempty"`
 
592
        CpuCores_         uint64   `yaml:"cpu-cores,omitempty"`
 
593
        CpuPower_         uint64   `yaml:"cpu-power,omitempty"`
 
594
        Tags_             []string `yaml:"tags,omitempty"`
 
595
        AvailabilityZone_ string   `yaml:"availability-zone,omitempty"`
 
596
}
 
597
 
 
598
// InstanceId implements CloudInstance.
 
599
func (c *cloudInstance) InstanceId() string {
 
600
        return c.InstanceId_
 
601
}
 
602
 
 
603
// Status implements CloudInstance.
 
604
func (c *cloudInstance) Status() string {
 
605
        return c.Status_
 
606
}
 
607
 
 
608
// Architecture implements CloudInstance.
 
609
func (c *cloudInstance) Architecture() string {
 
610
        return c.Architecture_
 
611
}
 
612
 
 
613
// Memory implements CloudInstance.
 
614
func (c *cloudInstance) Memory() uint64 {
 
615
        return c.Memory_
 
616
}
 
617
 
 
618
// RootDisk implements CloudInstance.
 
619
func (c *cloudInstance) RootDisk() uint64 {
 
620
        return c.RootDisk_
 
621
}
 
622
 
 
623
// CpuCores implements CloudInstance.
 
624
func (c *cloudInstance) CpuCores() uint64 {
 
625
        return c.CpuCores_
 
626
}
 
627
 
 
628
// CpuPower implements CloudInstance.
 
629
func (c *cloudInstance) CpuPower() uint64 {
 
630
        return c.CpuPower_
 
631
}
 
632
 
 
633
// Tags implements CloudInstance.
 
634
func (c *cloudInstance) Tags() []string {
 
635
        tags := make([]string, len(c.Tags_))
 
636
        copy(tags, c.Tags_)
 
637
        return tags
 
638
}
 
639
 
 
640
// AvailabilityZone implements CloudInstance.
 
641
func (c *cloudInstance) AvailabilityZone() string {
 
642
        return c.AvailabilityZone_
 
643
}
 
644
 
 
645
func importCloudInstance(source map[string]interface{}) (*cloudInstance, error) {
 
646
        version, err := getVersion(source)
 
647
        if err != nil {
 
648
                return nil, errors.Annotate(err, "cloudInstance version schema check failed")
 
649
        }
 
650
 
 
651
        importFunc, ok := cloudInstanceDeserializationFuncs[version]
 
652
        if !ok {
 
653
                return nil, errors.NotValidf("version %d", version)
 
654
        }
 
655
 
 
656
        return importFunc(source)
 
657
}
 
658
 
 
659
type cloudInstanceDeserializationFunc func(map[string]interface{}) (*cloudInstance, error)
 
660
 
 
661
var cloudInstanceDeserializationFuncs = map[int]cloudInstanceDeserializationFunc{
 
662
        1: importCloudInstanceV1,
 
663
}
 
664
 
 
665
func importCloudInstanceV1(source map[string]interface{}) (*cloudInstance, error) {
 
666
        fields := schema.Fields{
 
667
                "instance-id":       schema.String(),
 
668
                "status":            schema.String(),
 
669
                "architecture":      schema.String(),
 
670
                "memory":            schema.Uint(),
 
671
                "root-disk":         schema.Uint(),
 
672
                "cpu-cores":         schema.Uint(),
 
673
                "cpu-power":         schema.Uint(),
 
674
                "tags":              schema.List(schema.String()),
 
675
                "availability-zone": schema.String(),
 
676
        }
 
677
        // Some values don't have to be there.
 
678
        defaults := schema.Defaults{
 
679
                "architecture":      "",
 
680
                "memory":            uint64(0),
 
681
                "root-disk":         uint64(0),
 
682
                "cpu-cores":         uint64(0),
 
683
                "cpu-power":         uint64(0),
 
684
                "tags":              schema.Omit,
 
685
                "availability-zone": "",
 
686
        }
 
687
        checker := schema.FieldMap(fields, defaults)
 
688
 
 
689
        coerced, err := checker.Coerce(source, nil)
 
690
        if err != nil {
 
691
                return nil, errors.Annotatef(err, "cloudInstance v1 schema check failed")
 
692
        }
 
693
        valid := coerced.(map[string]interface{})
 
694
        // From here we know that the map returned from the schema coercion
 
695
        // contains fields of the right type.
 
696
 
 
697
        return &cloudInstance{
 
698
                Version:           1,
 
699
                InstanceId_:       valid["instance-id"].(string),
 
700
                Status_:           valid["status"].(string),
 
701
                Architecture_:     valid["architecture"].(string),
 
702
                Memory_:           valid["memory"].(uint64),
 
703
                RootDisk_:         valid["root-disk"].(uint64),
 
704
                CpuCores_:         valid["cpu-cores"].(uint64),
 
705
                CpuPower_:         valid["cpu-power"].(uint64),
 
706
                Tags_:             convertToStringSlice(valid["tags"]),
 
707
                AvailabilityZone_: valid["availability-zone"].(string),
 
708
        }, nil
 
709
}
 
710
 
 
711
// AgentToolsArgs is an argument struct used to add information about the
 
712
// tools the agent is using to a Machine.
 
713
type AgentToolsArgs struct {
 
714
        Version version.Binary
 
715
        URL     string
 
716
        SHA256  string
 
717
        Size    int64
 
718
}
 
719
 
 
720
func newAgentTools(args AgentToolsArgs) *agentTools {
 
721
        return &agentTools{
 
722
                Version_:      1,
 
723
                ToolsVersion_: args.Version,
 
724
                URL_:          args.URL,
 
725
                SHA256_:       args.SHA256,
 
726
                Size_:         args.Size,
 
727
        }
 
728
}
 
729
 
 
730
// Keeping the agentTools with the machine code, because we hope
 
731
// that one day we will succeed in merging the unit agents with the
 
732
// machine agents.
 
733
type agentTools struct {
 
734
        Version_      int            `yaml:"version"`
 
735
        ToolsVersion_ version.Binary `yaml:"tools-version"`
 
736
        URL_          string         `yaml:"url"`
 
737
        SHA256_       string         `yaml:"sha256"`
 
738
        Size_         int64          `yaml:"size"`
 
739
}
 
740
 
 
741
// Version implements AgentTools.
 
742
func (a *agentTools) Version() version.Binary {
 
743
        return a.ToolsVersion_
 
744
}
 
745
 
 
746
// URL implements AgentTools.
 
747
func (a *agentTools) URL() string {
 
748
        return a.URL_
 
749
}
 
750
 
 
751
// SHA256 implements AgentTools.
 
752
func (a *agentTools) SHA256() string {
 
753
        return a.SHA256_
 
754
}
 
755
 
 
756
// Size implements AgentTools.
 
757
func (a *agentTools) Size() int64 {
 
758
        return a.Size_
 
759
}
 
760
 
 
761
func importAgentTools(source map[string]interface{}) (*agentTools, error) {
 
762
        version, err := getVersion(source)
 
763
        if err != nil {
 
764
                return nil, errors.Annotate(err, "agentTools version schema check failed")
 
765
        }
 
766
 
 
767
        importFunc, ok := agentToolsDeserializationFuncs[version]
 
768
        if !ok {
 
769
                return nil, errors.NotValidf("version %d", version)
 
770
        }
 
771
 
 
772
        return importFunc(source)
 
773
}
 
774
 
 
775
type agentToolsDeserializationFunc func(map[string]interface{}) (*agentTools, error)
 
776
 
 
777
var agentToolsDeserializationFuncs = map[int]agentToolsDeserializationFunc{
 
778
        1: importAgentToolsV1,
 
779
}
 
780
 
 
781
func importAgentToolsV1(source map[string]interface{}) (*agentTools, error) {
 
782
        fields := schema.Fields{
 
783
                "tools-version": schema.String(),
 
784
                "url":           schema.String(),
 
785
                "sha256":        schema.String(),
 
786
                "size":          schema.Int(),
 
787
        }
 
788
        checker := schema.FieldMap(fields, nil) // no defaults
 
789
 
 
790
        coerced, err := checker.Coerce(source, nil)
 
791
        if err != nil {
 
792
                return nil, errors.Annotatef(err, "agentTools v1 schema check failed")
 
793
        }
 
794
        valid := coerced.(map[string]interface{})
 
795
        // From here we know that the map returned from the schema coercion
 
796
        // contains fields of the right type.
 
797
 
 
798
        verString := valid["tools-version"].(string)
 
799
        toolsVersion, err := version.ParseBinary(verString)
 
800
        if err != nil {
 
801
                return nil, errors.Annotatef(err, "agentTools tools-version")
 
802
        }
 
803
 
 
804
        return &agentTools{
 
805
                Version_:      1,
 
806
                ToolsVersion_: toolsVersion,
 
807
                URL_:          valid["url"].(string),
 
808
                SHA256_:       valid["sha256"].(string),
 
809
                Size_:         valid["size"].(int64),
 
810
        }, nil
 
811
}