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

« back to all changes in this revision

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