1
// Copyright 2016 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
7
"github.com/juju/errors"
8
"github.com/juju/schema"
9
"gopkg.in/juju/names.v2"
12
type filesystems struct {
13
Version int `yaml:"version"`
14
Filesystems_ []*filesystem `yaml:"filesystems"`
17
type filesystem struct {
18
ID_ string `yaml:"id"`
19
StorageID_ string `yaml:"storage-id,omitempty"`
20
VolumeID_ string `yaml:"volume-id,omitempty"`
21
Binding_ string `yaml:"binding,omitempty"`
23
Provisioned_ bool `yaml:"provisioned"`
24
Size_ uint64 `yaml:"size"`
25
Pool_ string `yaml:"pool,omitempty"`
26
FilesystemID_ string `yaml:"filesystem-id,omitempty"`
28
Status_ *status `yaml:"status"`
29
StatusHistory_ `yaml:"status-history"`
31
Attachments_ filesystemAttachments `yaml:"attachments"`
34
type filesystemAttachments struct {
35
Version int `yaml:"version"`
36
Attachments_ []*filesystemAttachment `yaml:"attachments"`
39
type filesystemAttachment struct {
40
MachineID_ string `yaml:"machine-id"`
41
Provisioned_ bool `yaml:"provisioned"`
42
MountPoint_ string `yaml:"mount-point,omitempty"`
43
ReadOnly_ bool `yaml:"read-only"`
46
// FilesystemArgs is an argument struct used to add a filesystem to the Model.
47
type FilesystemArgs struct {
48
Tag names.FilesystemTag
49
Storage names.StorageTag
50
Volume names.VolumeTag
58
func newFilesystem(args FilesystemArgs) *filesystem {
61
StorageID_: args.Storage.Id(),
62
VolumeID_: args.Volume.Id(),
63
Provisioned_: args.Provisioned,
66
FilesystemID_: args.FilesystemID,
67
StatusHistory_: newStatusHistory(),
69
if args.Binding != nil {
70
f.Binding_ = args.Binding.String()
76
// Tag implements Filesystem.
77
func (f *filesystem) Tag() names.FilesystemTag {
78
return names.NewFilesystemTag(f.ID_)
81
// Volume implements Filesystem.
82
func (f *filesystem) Volume() names.VolumeTag {
83
if f.VolumeID_ == "" {
84
return names.VolumeTag{}
86
return names.NewVolumeTag(f.VolumeID_)
89
// Storage implements Filesystem.
90
func (f *filesystem) Storage() names.StorageTag {
91
if f.StorageID_ == "" {
92
return names.StorageTag{}
94
return names.NewStorageTag(f.StorageID_)
97
// Binding implements Filesystem.
98
func (f *filesystem) Binding() (names.Tag, error) {
102
tag, err := names.ParseTag(f.Binding_)
104
return nil, errors.Trace(err)
109
// Provisioned implements Filesystem.
110
func (f *filesystem) Provisioned() bool {
111
return f.Provisioned_
114
// Size implements Filesystem.
115
func (f *filesystem) Size() uint64 {
119
// Pool implements Filesystem.
120
func (f *filesystem) Pool() string {
124
// FilesystemID implements Filesystem.
125
func (f *filesystem) FilesystemID() string {
126
return f.FilesystemID_
129
// Status implements Filesystem.
130
func (f *filesystem) Status() Status {
131
// To avoid typed nils check nil here.
132
if f.Status_ == nil {
138
// SetStatus implements Filesystem.
139
func (f *filesystem) SetStatus(args StatusArgs) {
140
f.Status_ = newStatus(args)
143
func (f *filesystem) setAttachments(attachments []*filesystemAttachment) {
144
f.Attachments_ = filesystemAttachments{
146
Attachments_: attachments,
150
// Attachments implements Filesystem.
151
func (f *filesystem) Attachments() []FilesystemAttachment {
152
var result []FilesystemAttachment
153
for _, attachment := range f.Attachments_.Attachments_ {
154
result = append(result, attachment)
159
// AddAttachment implements Filesystem.
160
func (f *filesystem) AddAttachment(args FilesystemAttachmentArgs) FilesystemAttachment {
161
a := newFilesystemAttachment(args)
162
f.Attachments_.Attachments_ = append(f.Attachments_.Attachments_, a)
166
// Validate implements Filesystem.
167
func (f *filesystem) Validate() error {
169
return errors.NotValidf("filesystem missing id")
172
return errors.NotValidf("filesystem %q missing size", f.ID_)
174
if f.Status_ == nil {
175
return errors.NotValidf("filesystem %q missing status", f.ID_)
180
func importFilesystems(source map[string]interface{}) ([]*filesystem, error) {
181
checker := versionedChecker("filesystems")
182
coerced, err := checker.Coerce(source, nil)
184
return nil, errors.Annotatef(err, "filesystems version schema check failed")
186
valid := coerced.(map[string]interface{})
188
version := int(valid["version"].(int64))
189
importFunc, ok := filesystemDeserializationFuncs[version]
191
return nil, errors.NotValidf("version %d", version)
193
sourceList := valid["filesystems"].([]interface{})
194
return importFilesystemList(sourceList, importFunc)
197
func importFilesystemList(sourceList []interface{}, importFunc filesystemDeserializationFunc) ([]*filesystem, error) {
198
result := make([]*filesystem, 0, len(sourceList))
199
for i, value := range sourceList {
200
source, ok := value.(map[string]interface{})
202
return nil, errors.Errorf("unexpected value for filesystem %d, %T", i, value)
204
filesystem, err := importFunc(source)
206
return nil, errors.Annotatef(err, "filesystem %d", i)
208
result = append(result, filesystem)
213
type filesystemDeserializationFunc func(map[string]interface{}) (*filesystem, error)
215
var filesystemDeserializationFuncs = map[int]filesystemDeserializationFunc{
216
1: importFilesystemV1,
219
func importFilesystemV1(source map[string]interface{}) (*filesystem, error) {
220
fields := schema.Fields{
221
"id": schema.String(),
222
"storage-id": schema.String(),
223
"volume-id": schema.String(),
224
"binding": schema.String(),
225
"provisioned": schema.Bool(),
226
"size": schema.ForceUint(),
227
"pool": schema.String(),
228
"filesystem-id": schema.String(),
229
"status": schema.StringMap(schema.Any()),
230
"attachments": schema.StringMap(schema.Any()),
233
defaults := schema.Defaults{
239
"attachments": schema.Omit,
241
addStatusHistorySchema(fields)
242
checker := schema.FieldMap(fields, defaults)
244
coerced, err := checker.Coerce(source, nil)
246
return nil, errors.Annotatef(err, "filesystem v1 schema check failed")
248
valid := coerced.(map[string]interface{})
249
// From here we know that the map returned from the schema coercion
250
// contains fields of the right type.
251
result := &filesystem{
252
ID_: valid["id"].(string),
253
StorageID_: valid["storage-id"].(string),
254
VolumeID_: valid["volume-id"].(string),
255
Binding_: valid["binding"].(string),
256
Provisioned_: valid["provisioned"].(bool),
257
Size_: valid["size"].(uint64),
258
Pool_: valid["pool"].(string),
259
FilesystemID_: valid["filesystem-id"].(string),
260
StatusHistory_: newStatusHistory(),
262
if err := result.importStatusHistory(valid); err != nil {
263
return nil, errors.Trace(err)
266
status, err := importStatus(valid["status"].(map[string]interface{}))
268
return nil, errors.Trace(err)
270
result.Status_ = status
272
attachments, err := importFilesystemAttachments(valid["attachments"].(map[string]interface{}))
274
return nil, errors.Trace(err)
276
result.setAttachments(attachments)
281
// FilesystemAttachmentArgs is an argument struct used to add information about the
282
// cloud instance to a Filesystem.
283
type FilesystemAttachmentArgs struct {
284
Machine names.MachineTag
290
func newFilesystemAttachment(args FilesystemAttachmentArgs) *filesystemAttachment {
291
return &filesystemAttachment{
292
MachineID_: args.Machine.Id(),
293
Provisioned_: args.Provisioned,
294
ReadOnly_: args.ReadOnly,
295
MountPoint_: args.MountPoint,
299
// Machine implements FilesystemAttachment
300
func (a *filesystemAttachment) Machine() names.MachineTag {
301
return names.NewMachineTag(a.MachineID_)
304
// Provisioned implements FilesystemAttachment
305
func (a *filesystemAttachment) Provisioned() bool {
306
return a.Provisioned_
309
// ReadOnly implements FilesystemAttachment
310
func (a *filesystemAttachment) ReadOnly() bool {
314
// MountPoint implements FilesystemAttachment
315
func (a *filesystemAttachment) MountPoint() string {
319
func importFilesystemAttachments(source map[string]interface{}) ([]*filesystemAttachment, error) {
320
checker := versionedChecker("attachments")
321
coerced, err := checker.Coerce(source, nil)
323
return nil, errors.Annotatef(err, "filesystem attachments version schema check failed")
325
valid := coerced.(map[string]interface{})
327
version := int(valid["version"].(int64))
328
importFunc, ok := filesystemAttachmentDeserializationFuncs[version]
330
return nil, errors.NotValidf("version %d", version)
332
sourceList := valid["attachments"].([]interface{})
333
return importFilesystemAttachmentList(sourceList, importFunc)
336
func importFilesystemAttachmentList(sourceList []interface{}, importFunc filesystemAttachmentDeserializationFunc) ([]*filesystemAttachment, error) {
337
result := make([]*filesystemAttachment, 0, len(sourceList))
338
for i, value := range sourceList {
339
source, ok := value.(map[string]interface{})
341
return nil, errors.Errorf("unexpected value for filesystemAttachment %d, %T", i, value)
343
filesystemAttachment, err := importFunc(source)
345
return nil, errors.Annotatef(err, "filesystemAttachment %d", i)
347
result = append(result, filesystemAttachment)
352
type filesystemAttachmentDeserializationFunc func(map[string]interface{}) (*filesystemAttachment, error)
354
var filesystemAttachmentDeserializationFuncs = map[int]filesystemAttachmentDeserializationFunc{
355
1: importFilesystemAttachmentV1,
358
func importFilesystemAttachmentV1(source map[string]interface{}) (*filesystemAttachment, error) {
359
fields := schema.Fields{
360
"machine-id": schema.String(),
361
"provisioned": schema.Bool(),
362
"read-only": schema.Bool(),
363
"mount-point": schema.String(),
365
defaults := schema.Defaults{
368
checker := schema.FieldMap(fields, defaults)
370
coerced, err := checker.Coerce(source, nil)
372
return nil, errors.Annotatef(err, "filesystemAttachment v1 schema check failed")
374
valid := coerced.(map[string]interface{})
375
// From here we know that the map returned from the schema coercion
376
// contains fields of the right type.
378
result := &filesystemAttachment{
379
MachineID_: valid["machine-id"].(string),
380
Provisioned_: valid["provisioned"].(bool),
381
ReadOnly_: valid["read-only"].(bool),
382
MountPoint_: valid["mount-point"].(string),