~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/core/description/volume.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
        "gopkg.in/juju/names.v2"
 
10
)
 
11
 
 
12
type volumes struct {
 
13
        Version  int       `yaml:"version"`
 
14
        Volumes_ []*volume `yaml:"volumes"`
 
15
}
 
16
 
 
17
type volume struct {
 
18
        ID_          string `yaml:"id"`
 
19
        Binding_     string `yaml:"binding,omitempty"`
 
20
        StorageID_   string `yaml:"storage-id,omitempty"`
 
21
        Provisioned_ bool   `yaml:"provisioned"`
 
22
        Size_        uint64 `yaml:"size"`
 
23
        Pool_        string `yaml:"pool,omitempty"`
 
24
        HardwareID_  string `yaml:"hardware-id,omitempty"`
 
25
        VolumeID_    string `yaml:"volume-id,omitempty"`
 
26
        Persistent_  bool   `yaml:"persistent"`
 
27
 
 
28
        Status_        *status `yaml:"status"`
 
29
        StatusHistory_ `yaml:"status-history"`
 
30
 
 
31
        Attachments_ volumeAttachments `yaml:"attachments"`
 
32
}
 
33
 
 
34
type volumeAttachments struct {
 
35
        Version      int                 `yaml:"version"`
 
36
        Attachments_ []*volumeAttachment `yaml:"attachments"`
 
37
}
 
38
 
 
39
type volumeAttachment struct {
 
40
        MachineID_   string `yaml:"machine-id"`
 
41
        Provisioned_ bool   `yaml:"provisioned"`
 
42
        ReadOnly_    bool   `yaml:"read-only"`
 
43
        DeviceName_  string `yaml:"device-name"`
 
44
        DeviceLink_  string `yaml:"device-link"`
 
45
        BusAddress_  string `yaml:"bus-address"`
 
46
}
 
47
 
 
48
// VolumeArgs is an argument struct used to add a volume to the Model.
 
49
type VolumeArgs struct {
 
50
        Tag         names.VolumeTag
 
51
        Storage     names.StorageTag
 
52
        Binding     names.Tag
 
53
        Provisioned bool
 
54
        Size        uint64
 
55
        Pool        string
 
56
        HardwareID  string
 
57
        VolumeID    string
 
58
        Persistent  bool
 
59
}
 
60
 
 
61
func newVolume(args VolumeArgs) *volume {
 
62
        v := &volume{
 
63
                ID_:            args.Tag.Id(),
 
64
                StorageID_:     args.Storage.Id(),
 
65
                Provisioned_:   args.Provisioned,
 
66
                Size_:          args.Size,
 
67
                Pool_:          args.Pool,
 
68
                HardwareID_:    args.HardwareID,
 
69
                VolumeID_:      args.VolumeID,
 
70
                Persistent_:    args.Persistent,
 
71
                StatusHistory_: newStatusHistory(),
 
72
        }
 
73
        if args.Binding != nil {
 
74
                v.Binding_ = args.Binding.String()
 
75
        }
 
76
        v.setAttachments(nil)
 
77
        return v
 
78
}
 
79
 
 
80
// Tag implements Volume.
 
81
func (v *volume) Tag() names.VolumeTag {
 
82
        return names.NewVolumeTag(v.ID_)
 
83
}
 
84
 
 
85
// Storage implements Volume.
 
86
func (v *volume) Storage() names.StorageTag {
 
87
        if v.StorageID_ == "" {
 
88
                return names.StorageTag{}
 
89
        }
 
90
        return names.NewStorageTag(v.StorageID_)
 
91
}
 
92
 
 
93
// Binding implements Volume.
 
94
func (v *volume) Binding() (names.Tag, error) {
 
95
        if v.Binding_ == "" {
 
96
                return nil, nil
 
97
        }
 
98
        tag, err := names.ParseTag(v.Binding_)
 
99
        if err != nil {
 
100
                return nil, errors.Trace(err)
 
101
        }
 
102
        return tag, nil
 
103
}
 
104
 
 
105
// Provisioned implements Volume.
 
106
func (v *volume) Provisioned() bool {
 
107
        return v.Provisioned_
 
108
}
 
109
 
 
110
// Size implements Volume.
 
111
func (v *volume) Size() uint64 {
 
112
        return v.Size_
 
113
}
 
114
 
 
115
// Pool implements Volume.
 
116
func (v *volume) Pool() string {
 
117
        return v.Pool_
 
118
}
 
119
 
 
120
// HardwareID implements Volume.
 
121
func (v *volume) HardwareID() string {
 
122
        return v.HardwareID_
 
123
}
 
124
 
 
125
// VolumeID implements Volume.
 
126
func (v *volume) VolumeID() string {
 
127
        return v.VolumeID_
 
128
}
 
129
 
 
130
// Persistent implements Volume.
 
131
func (v *volume) Persistent() bool {
 
132
        return v.Persistent_
 
133
}
 
134
 
 
135
// Status implements Volume.
 
136
func (v *volume) Status() Status {
 
137
        // To avoid typed nils check nil here.
 
138
        if v.Status_ == nil {
 
139
                return nil
 
140
        }
 
141
        return v.Status_
 
142
}
 
143
 
 
144
// SetStatus implements Volume.
 
145
func (v *volume) SetStatus(args StatusArgs) {
 
146
        v.Status_ = newStatus(args)
 
147
}
 
148
 
 
149
func (v *volume) setAttachments(attachments []*volumeAttachment) {
 
150
        v.Attachments_ = volumeAttachments{
 
151
                Version:      1,
 
152
                Attachments_: attachments,
 
153
        }
 
154
}
 
155
 
 
156
// Attachments implements Volume.
 
157
func (v *volume) Attachments() []VolumeAttachment {
 
158
        var result []VolumeAttachment
 
159
        for _, attachment := range v.Attachments_.Attachments_ {
 
160
                result = append(result, attachment)
 
161
        }
 
162
        return result
 
163
}
 
164
 
 
165
// AddAttachment implements Volume.
 
166
func (v *volume) AddAttachment(args VolumeAttachmentArgs) VolumeAttachment {
 
167
        a := newVolumeAttachment(args)
 
168
        v.Attachments_.Attachments_ = append(v.Attachments_.Attachments_, a)
 
169
        return a
 
170
}
 
171
 
 
172
// Validate implements Volume.
 
173
func (v *volume) Validate() error {
 
174
        if v.ID_ == "" {
 
175
                return errors.NotValidf("volume missing id")
 
176
        }
 
177
        if v.Size_ == 0 {
 
178
                return errors.NotValidf("volume %q missing size", v.ID_)
 
179
        }
 
180
        if v.Status_ == nil {
 
181
                return errors.NotValidf("volume %q missing status", v.ID_)
 
182
        }
 
183
        return nil
 
184
}
 
185
 
 
186
func importVolumes(source map[string]interface{}) ([]*volume, error) {
 
187
        checker := versionedChecker("volumes")
 
188
        coerced, err := checker.Coerce(source, nil)
 
189
        if err != nil {
 
190
                return nil, errors.Annotatef(err, "volumes version schema check failed")
 
191
        }
 
192
        valid := coerced.(map[string]interface{})
 
193
 
 
194
        version := int(valid["version"].(int64))
 
195
        importFunc, ok := volumeDeserializationFuncs[version]
 
196
        if !ok {
 
197
                return nil, errors.NotValidf("version %d", version)
 
198
        }
 
199
        sourceList := valid["volumes"].([]interface{})
 
200
        return importVolumeList(sourceList, importFunc)
 
201
}
 
202
 
 
203
func importVolumeList(sourceList []interface{}, importFunc volumeDeserializationFunc) ([]*volume, error) {
 
204
        result := make([]*volume, 0, len(sourceList))
 
205
        for i, value := range sourceList {
 
206
                source, ok := value.(map[string]interface{})
 
207
                if !ok {
 
208
                        return nil, errors.Errorf("unexpected value for volume %d, %T", i, value)
 
209
                }
 
210
                volume, err := importFunc(source)
 
211
                if err != nil {
 
212
                        return nil, errors.Annotatef(err, "volume %d", i)
 
213
                }
 
214
                result = append(result, volume)
 
215
        }
 
216
        return result, nil
 
217
}
 
218
 
 
219
type volumeDeserializationFunc func(map[string]interface{}) (*volume, error)
 
220
 
 
221
var volumeDeserializationFuncs = map[int]volumeDeserializationFunc{
 
222
        1: importVolumeV1,
 
223
}
 
224
 
 
225
func importVolumeV1(source map[string]interface{}) (*volume, error) {
 
226
        fields := schema.Fields{
 
227
                "id":          schema.String(),
 
228
                "storage-id":  schema.String(),
 
229
                "binding":     schema.String(),
 
230
                "provisioned": schema.Bool(),
 
231
                "size":        schema.ForceUint(),
 
232
                "pool":        schema.String(),
 
233
                "hardware-id": schema.String(),
 
234
                "volume-id":   schema.String(),
 
235
                "persistent":  schema.Bool(),
 
236
                "status":      schema.StringMap(schema.Any()),
 
237
                "attachments": schema.StringMap(schema.Any()),
 
238
        }
 
239
 
 
240
        defaults := schema.Defaults{
 
241
                "storage-id":  "",
 
242
                "binding":     "",
 
243
                "pool":        "",
 
244
                "hardware-id": "",
 
245
                "volume-id":   "",
 
246
                "attachments": schema.Omit,
 
247
        }
 
248
        addStatusHistorySchema(fields)
 
249
        checker := schema.FieldMap(fields, defaults)
 
250
 
 
251
        coerced, err := checker.Coerce(source, nil)
 
252
        if err != nil {
 
253
                return nil, errors.Annotatef(err, "volume v1 schema check failed")
 
254
        }
 
255
        valid := coerced.(map[string]interface{})
 
256
        // From here we know that the map returned from the schema coercion
 
257
        // contains fields of the right type.
 
258
        result := &volume{
 
259
                ID_:            valid["id"].(string),
 
260
                StorageID_:     valid["storage-id"].(string),
 
261
                Binding_:       valid["binding"].(string),
 
262
                Provisioned_:   valid["provisioned"].(bool),
 
263
                Size_:          valid["size"].(uint64),
 
264
                Pool_:          valid["pool"].(string),
 
265
                HardwareID_:    valid["hardware-id"].(string),
 
266
                VolumeID_:      valid["volume-id"].(string),
 
267
                Persistent_:    valid["persistent"].(bool),
 
268
                StatusHistory_: newStatusHistory(),
 
269
        }
 
270
        if err := result.importStatusHistory(valid); err != nil {
 
271
                return nil, errors.Trace(err)
 
272
        }
 
273
 
 
274
        status, err := importStatus(valid["status"].(map[string]interface{}))
 
275
        if err != nil {
 
276
                return nil, errors.Trace(err)
 
277
        }
 
278
        result.Status_ = status
 
279
 
 
280
        attachments, err := importVolumeAttachments(valid["attachments"].(map[string]interface{}))
 
281
        if err != nil {
 
282
                return nil, errors.Trace(err)
 
283
        }
 
284
        result.setAttachments(attachments)
 
285
 
 
286
        return result, nil
 
287
}
 
288
 
 
289
// VolumeAttachmentArgs is an argument struct used to add information about the
 
290
// cloud instance to a Volume.
 
291
type VolumeAttachmentArgs struct {
 
292
        Machine     names.MachineTag
 
293
        Provisioned bool
 
294
        ReadOnly    bool
 
295
        DeviceName  string
 
296
        DeviceLink  string
 
297
        BusAddress  string
 
298
}
 
299
 
 
300
func newVolumeAttachment(args VolumeAttachmentArgs) *volumeAttachment {
 
301
        return &volumeAttachment{
 
302
                MachineID_:   args.Machine.Id(),
 
303
                Provisioned_: args.Provisioned,
 
304
                ReadOnly_:    args.ReadOnly,
 
305
                DeviceName_:  args.DeviceName,
 
306
                DeviceLink_:  args.DeviceLink,
 
307
                BusAddress_:  args.BusAddress,
 
308
        }
 
309
}
 
310
 
 
311
// Machine implements VolumeAttachment
 
312
func (a *volumeAttachment) Machine() names.MachineTag {
 
313
        return names.NewMachineTag(a.MachineID_)
 
314
}
 
315
 
 
316
// Provisioned implements VolumeAttachment
 
317
func (a *volumeAttachment) Provisioned() bool {
 
318
        return a.Provisioned_
 
319
}
 
320
 
 
321
// ReadOnly implements VolumeAttachment
 
322
func (a *volumeAttachment) ReadOnly() bool {
 
323
        return a.ReadOnly_
 
324
}
 
325
 
 
326
// DeviceName implements VolumeAttachment
 
327
func (a *volumeAttachment) DeviceName() string {
 
328
        return a.DeviceName_
 
329
}
 
330
 
 
331
// DeviceLink implements VolumeAttachment
 
332
func (a *volumeAttachment) DeviceLink() string {
 
333
        return a.DeviceLink_
 
334
}
 
335
 
 
336
// BusAddress implements VolumeAttachment
 
337
func (a *volumeAttachment) BusAddress() string {
 
338
        return a.BusAddress_
 
339
}
 
340
 
 
341
func importVolumeAttachments(source map[string]interface{}) ([]*volumeAttachment, error) {
 
342
        checker := versionedChecker("attachments")
 
343
        coerced, err := checker.Coerce(source, nil)
 
344
        if err != nil {
 
345
                return nil, errors.Annotatef(err, "volume attachments version schema check failed")
 
346
        }
 
347
        valid := coerced.(map[string]interface{})
 
348
 
 
349
        version := int(valid["version"].(int64))
 
350
        importFunc, ok := volumeAttachmentDeserializationFuncs[version]
 
351
        if !ok {
 
352
                return nil, errors.NotValidf("version %d", version)
 
353
        }
 
354
        sourceList := valid["attachments"].([]interface{})
 
355
        return importVolumeAttachmentList(sourceList, importFunc)
 
356
}
 
357
 
 
358
func importVolumeAttachmentList(sourceList []interface{}, importFunc volumeAttachmentDeserializationFunc) ([]*volumeAttachment, error) {
 
359
        result := make([]*volumeAttachment, 0, len(sourceList))
 
360
        for i, value := range sourceList {
 
361
                source, ok := value.(map[string]interface{})
 
362
                if !ok {
 
363
                        return nil, errors.Errorf("unexpected value for volumeAttachment %d, %T", i, value)
 
364
                }
 
365
                volumeAttachment, err := importFunc(source)
 
366
                if err != nil {
 
367
                        return nil, errors.Annotatef(err, "volumeAttachment %d", i)
 
368
                }
 
369
                result = append(result, volumeAttachment)
 
370
        }
 
371
        return result, nil
 
372
}
 
373
 
 
374
type volumeAttachmentDeserializationFunc func(map[string]interface{}) (*volumeAttachment, error)
 
375
 
 
376
var volumeAttachmentDeserializationFuncs = map[int]volumeAttachmentDeserializationFunc{
 
377
        1: importVolumeAttachmentV1,
 
378
}
 
379
 
 
380
func importVolumeAttachmentV1(source map[string]interface{}) (*volumeAttachment, error) {
 
381
        fields := schema.Fields{
 
382
                "machine-id":  schema.String(),
 
383
                "provisioned": schema.Bool(),
 
384
                "read-only":   schema.Bool(),
 
385
                "device-name": schema.String(),
 
386
                "device-link": schema.String(),
 
387
                "bus-address": schema.String(),
 
388
        }
 
389
        checker := schema.FieldMap(fields, nil) // no defaults
 
390
 
 
391
        coerced, err := checker.Coerce(source, nil)
 
392
        if err != nil {
 
393
                return nil, errors.Annotatef(err, "volumeAttachment v1 schema check failed")
 
394
        }
 
395
        valid := coerced.(map[string]interface{})
 
396
        // From here we know that the map returned from the schema coercion
 
397
        // contains fields of the right type.
 
398
 
 
399
        result := &volumeAttachment{
 
400
                MachineID_:   valid["machine-id"].(string),
 
401
                Provisioned_: valid["provisioned"].(bool),
 
402
                ReadOnly_:    valid["read-only"].(bool),
 
403
                DeviceName_:  valid["device-name"].(string),
 
404
                DeviceLink_:  valid["device-link"].(string),
 
405
                BusAddress_:  valid["bus-address"].(string),
 
406
        }
 
407
        return result, nil
 
408
}