github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/core/description/filesystem.go (about) 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 filesystems struct { 13 Version int `yaml:"version"` 14 Filesystems_ []*filesystem `yaml:"filesystems"` 15 } 16 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"` 22 23 Provisioned_ bool `yaml:"provisioned"` 24 Size_ uint64 `yaml:"size"` 25 Pool_ string `yaml:"pool,omitempty"` 26 FilesystemID_ string `yaml:"filesystem-id,omitempty"` 27 28 Status_ *status `yaml:"status"` 29 StatusHistory_ `yaml:"status-history"` 30 31 Attachments_ filesystemAttachments `yaml:"attachments"` 32 } 33 34 type filesystemAttachments struct { 35 Version int `yaml:"version"` 36 Attachments_ []*filesystemAttachment `yaml:"attachments"` 37 } 38 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"` 44 } 45 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 51 Binding names.Tag 52 Provisioned bool 53 Size uint64 54 Pool string 55 FilesystemID string 56 } 57 58 func newFilesystem(args FilesystemArgs) *filesystem { 59 f := &filesystem{ 60 ID_: args.Tag.Id(), 61 StorageID_: args.Storage.Id(), 62 VolumeID_: args.Volume.Id(), 63 Provisioned_: args.Provisioned, 64 Size_: args.Size, 65 Pool_: args.Pool, 66 FilesystemID_: args.FilesystemID, 67 StatusHistory_: newStatusHistory(), 68 } 69 if args.Binding != nil { 70 f.Binding_ = args.Binding.String() 71 } 72 f.setAttachments(nil) 73 return f 74 } 75 76 // Tag implements Filesystem. 77 func (f *filesystem) Tag() names.FilesystemTag { 78 return names.NewFilesystemTag(f.ID_) 79 } 80 81 // Volume implements Filesystem. 82 func (f *filesystem) Volume() names.VolumeTag { 83 if f.VolumeID_ == "" { 84 return names.VolumeTag{} 85 } 86 return names.NewVolumeTag(f.VolumeID_) 87 } 88 89 // Storage implements Filesystem. 90 func (f *filesystem) Storage() names.StorageTag { 91 if f.StorageID_ == "" { 92 return names.StorageTag{} 93 } 94 return names.NewStorageTag(f.StorageID_) 95 } 96 97 // Binding implements Filesystem. 98 func (f *filesystem) Binding() (names.Tag, error) { 99 if f.Binding_ == "" { 100 return nil, nil 101 } 102 tag, err := names.ParseTag(f.Binding_) 103 if err != nil { 104 return nil, errors.Trace(err) 105 } 106 return tag, nil 107 } 108 109 // Provisioned implements Filesystem. 110 func (f *filesystem) Provisioned() bool { 111 return f.Provisioned_ 112 } 113 114 // Size implements Filesystem. 115 func (f *filesystem) Size() uint64 { 116 return f.Size_ 117 } 118 119 // Pool implements Filesystem. 120 func (f *filesystem) Pool() string { 121 return f.Pool_ 122 } 123 124 // FilesystemID implements Filesystem. 125 func (f *filesystem) FilesystemID() string { 126 return f.FilesystemID_ 127 } 128 129 // Status implements Filesystem. 130 func (f *filesystem) Status() Status { 131 // To avoid typed nils check nil here. 132 if f.Status_ == nil { 133 return nil 134 } 135 return f.Status_ 136 } 137 138 // SetStatus implements Filesystem. 139 func (f *filesystem) SetStatus(args StatusArgs) { 140 f.Status_ = newStatus(args) 141 } 142 143 func (f *filesystem) setAttachments(attachments []*filesystemAttachment) { 144 f.Attachments_ = filesystemAttachments{ 145 Version: 1, 146 Attachments_: attachments, 147 } 148 } 149 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) 155 } 156 return result 157 } 158 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) 163 return a 164 } 165 166 // Validate implements Filesystem. 167 func (f *filesystem) Validate() error { 168 if f.ID_ == "" { 169 return errors.NotValidf("filesystem missing id") 170 } 171 if f.Size_ == 0 { 172 return errors.NotValidf("filesystem %q missing size", f.ID_) 173 } 174 if f.Status_ == nil { 175 return errors.NotValidf("filesystem %q missing status", f.ID_) 176 } 177 if _, err := f.Binding(); err != nil { 178 return errors.Wrap(err, errors.NotValidf("filesystem %q binding", f.ID_)) 179 } 180 return nil 181 } 182 183 func importFilesystems(source map[string]interface{}) ([]*filesystem, error) { 184 checker := versionedChecker("filesystems") 185 coerced, err := checker.Coerce(source, nil) 186 if err != nil { 187 return nil, errors.Annotatef(err, "filesystems version schema check failed") 188 } 189 valid := coerced.(map[string]interface{}) 190 191 version := int(valid["version"].(int64)) 192 importFunc, ok := filesystemDeserializationFuncs[version] 193 if !ok { 194 return nil, errors.NotValidf("version %d", version) 195 } 196 sourceList := valid["filesystems"].([]interface{}) 197 return importFilesystemList(sourceList, importFunc) 198 } 199 200 func importFilesystemList(sourceList []interface{}, importFunc filesystemDeserializationFunc) ([]*filesystem, error) { 201 result := make([]*filesystem, 0, len(sourceList)) 202 for i, value := range sourceList { 203 source, ok := value.(map[string]interface{}) 204 if !ok { 205 return nil, errors.Errorf("unexpected value for filesystem %d, %T", i, value) 206 } 207 filesystem, err := importFunc(source) 208 if err != nil { 209 return nil, errors.Annotatef(err, "filesystem %d", i) 210 } 211 result = append(result, filesystem) 212 } 213 return result, nil 214 } 215 216 type filesystemDeserializationFunc func(map[string]interface{}) (*filesystem, error) 217 218 var filesystemDeserializationFuncs = map[int]filesystemDeserializationFunc{ 219 1: importFilesystemV1, 220 } 221 222 func importFilesystemV1(source map[string]interface{}) (*filesystem, error) { 223 fields := schema.Fields{ 224 "id": schema.String(), 225 "storage-id": schema.String(), 226 "volume-id": schema.String(), 227 "binding": schema.String(), 228 "provisioned": schema.Bool(), 229 "size": schema.ForceUint(), 230 "pool": schema.String(), 231 "filesystem-id": schema.String(), 232 "status": schema.StringMap(schema.Any()), 233 "attachments": schema.StringMap(schema.Any()), 234 } 235 236 defaults := schema.Defaults{ 237 "storage-id": "", 238 "volume-id": "", 239 "binding": "", 240 "pool": "", 241 "filesystem-id": "", 242 "attachments": schema.Omit, 243 } 244 addStatusHistorySchema(fields) 245 checker := schema.FieldMap(fields, defaults) 246 247 coerced, err := checker.Coerce(source, nil) 248 if err != nil { 249 return nil, errors.Annotatef(err, "filesystem v1 schema check failed") 250 } 251 valid := coerced.(map[string]interface{}) 252 // From here we know that the map returned from the schema coercion 253 // contains fields of the right type. 254 result := &filesystem{ 255 ID_: valid["id"].(string), 256 StorageID_: valid["storage-id"].(string), 257 VolumeID_: valid["volume-id"].(string), 258 Binding_: valid["binding"].(string), 259 Provisioned_: valid["provisioned"].(bool), 260 Size_: valid["size"].(uint64), 261 Pool_: valid["pool"].(string), 262 FilesystemID_: valid["filesystem-id"].(string), 263 StatusHistory_: newStatusHistory(), 264 } 265 if err := result.importStatusHistory(valid); err != nil { 266 return nil, errors.Trace(err) 267 } 268 269 status, err := importStatus(valid["status"].(map[string]interface{})) 270 if err != nil { 271 return nil, errors.Trace(err) 272 } 273 result.Status_ = status 274 275 attachments, err := importFilesystemAttachments(valid["attachments"].(map[string]interface{})) 276 if err != nil { 277 return nil, errors.Trace(err) 278 } 279 result.setAttachments(attachments) 280 281 return result, nil 282 } 283 284 // FilesystemAttachmentArgs is an argument struct used to add information about the 285 // cloud instance to a Filesystem. 286 type FilesystemAttachmentArgs struct { 287 Machine names.MachineTag 288 Provisioned bool 289 ReadOnly bool 290 MountPoint string 291 } 292 293 func newFilesystemAttachment(args FilesystemAttachmentArgs) *filesystemAttachment { 294 return &filesystemAttachment{ 295 MachineID_: args.Machine.Id(), 296 Provisioned_: args.Provisioned, 297 ReadOnly_: args.ReadOnly, 298 MountPoint_: args.MountPoint, 299 } 300 } 301 302 // Machine implements FilesystemAttachment 303 func (a *filesystemAttachment) Machine() names.MachineTag { 304 return names.NewMachineTag(a.MachineID_) 305 } 306 307 // Provisioned implements FilesystemAttachment 308 func (a *filesystemAttachment) Provisioned() bool { 309 return a.Provisioned_ 310 } 311 312 // ReadOnly implements FilesystemAttachment 313 func (a *filesystemAttachment) ReadOnly() bool { 314 return a.ReadOnly_ 315 } 316 317 // MountPoint implements FilesystemAttachment 318 func (a *filesystemAttachment) MountPoint() string { 319 return a.MountPoint_ 320 } 321 322 func importFilesystemAttachments(source map[string]interface{}) ([]*filesystemAttachment, error) { 323 checker := versionedChecker("attachments") 324 coerced, err := checker.Coerce(source, nil) 325 if err != nil { 326 return nil, errors.Annotatef(err, "filesystem attachments version schema check failed") 327 } 328 valid := coerced.(map[string]interface{}) 329 330 version := int(valid["version"].(int64)) 331 importFunc, ok := filesystemAttachmentDeserializationFuncs[version] 332 if !ok { 333 return nil, errors.NotValidf("version %d", version) 334 } 335 sourceList := valid["attachments"].([]interface{}) 336 return importFilesystemAttachmentList(sourceList, importFunc) 337 } 338 339 func importFilesystemAttachmentList(sourceList []interface{}, importFunc filesystemAttachmentDeserializationFunc) ([]*filesystemAttachment, error) { 340 result := make([]*filesystemAttachment, 0, len(sourceList)) 341 for i, value := range sourceList { 342 source, ok := value.(map[string]interface{}) 343 if !ok { 344 return nil, errors.Errorf("unexpected value for filesystemAttachment %d, %T", i, value) 345 } 346 filesystemAttachment, err := importFunc(source) 347 if err != nil { 348 return nil, errors.Annotatef(err, "filesystemAttachment %d", i) 349 } 350 result = append(result, filesystemAttachment) 351 } 352 return result, nil 353 } 354 355 type filesystemAttachmentDeserializationFunc func(map[string]interface{}) (*filesystemAttachment, error) 356 357 var filesystemAttachmentDeserializationFuncs = map[int]filesystemAttachmentDeserializationFunc{ 358 1: importFilesystemAttachmentV1, 359 } 360 361 func importFilesystemAttachmentV1(source map[string]interface{}) (*filesystemAttachment, error) { 362 fields := schema.Fields{ 363 "machine-id": schema.String(), 364 "provisioned": schema.Bool(), 365 "read-only": schema.Bool(), 366 "mount-point": schema.String(), 367 } 368 defaults := schema.Defaults{ 369 "mount-point": "", 370 } 371 checker := schema.FieldMap(fields, defaults) 372 373 coerced, err := checker.Coerce(source, nil) 374 if err != nil { 375 return nil, errors.Annotatef(err, "filesystemAttachment v1 schema check failed") 376 } 377 valid := coerced.(map[string]interface{}) 378 // From here we know that the map returned from the schema coercion 379 // contains fields of the right type. 380 381 result := &filesystemAttachment{ 382 MachineID_: valid["machine-id"].(string), 383 Provisioned_: valid["provisioned"].(bool), 384 ReadOnly_: valid["read-only"].(bool), 385 MountPoint_: valid["mount-point"].(string), 386 } 387 return result, nil 388 }