github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/core/description/storage.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 storages struct { 13 Version int `yaml:"version"` 14 Storages_ []*storage `yaml:"storages"` 15 } 16 17 type storage struct { 18 ID_ string `yaml:"id"` 19 Kind_ string `yaml:"kind"` 20 Owner_ string `yaml:"owner"` 21 Name_ string `yaml:"name"` 22 23 Attachments_ []string `yaml:"attachments"` 24 } 25 26 // StorageArgs is an argument struct used to add a storage to the Model. 27 type StorageArgs struct { 28 Tag names.StorageTag 29 Kind string 30 Owner names.Tag 31 Name string 32 Attachments []names.UnitTag 33 } 34 35 func newStorage(args StorageArgs) *storage { 36 s := &storage{ 37 ID_: args.Tag.Id(), 38 Kind_: args.Kind, 39 Name_: args.Name, 40 } 41 if args.Owner != nil { 42 s.Owner_ = args.Owner.String() 43 } 44 for _, unit := range args.Attachments { 45 s.Attachments_ = append(s.Attachments_, unit.Id()) 46 } 47 return s 48 } 49 50 // Tag implements Storage. 51 func (s *storage) Tag() names.StorageTag { 52 return names.NewStorageTag(s.ID_) 53 } 54 55 // Kind implements Storage. 56 func (s *storage) Kind() string { 57 return s.Kind_ 58 } 59 60 // Owner implements Storage. 61 func (s *storage) Owner() (names.Tag, error) { 62 if s.Owner_ == "" { 63 return nil, nil 64 } 65 tag, err := names.ParseTag(s.Owner_) 66 if err != nil { 67 return nil, errors.Trace(err) 68 } 69 return tag, nil 70 } 71 72 // Name implements Storage. 73 func (s *storage) Name() string { 74 return s.Name_ 75 } 76 77 // Attachments implements Storage. 78 func (s *storage) Attachments() []names.UnitTag { 79 var result []names.UnitTag 80 for _, unit := range s.Attachments_ { 81 result = append(result, names.NewUnitTag(unit)) 82 } 83 return result 84 } 85 86 // Validate implements Storage. 87 func (s *storage) Validate() error { 88 if s.ID_ == "" { 89 return errors.NotValidf("storage missing id") 90 } 91 if s.Owner_ == "" { 92 return errors.NotValidf("storage %q missing owner", s.ID_) 93 } 94 // Also check that the owner and attachments are valid. 95 if _, err := s.Owner(); err != nil { 96 return errors.Wrap(err, errors.NotValidf("storage %q invalid owner", s.ID_)) 97 } 98 return nil 99 } 100 101 func importStorages(source map[string]interface{}) ([]*storage, error) { 102 checker := versionedChecker("storages") 103 coerced, err := checker.Coerce(source, nil) 104 if err != nil { 105 return nil, errors.Annotatef(err, "storages version schema check failed") 106 } 107 valid := coerced.(map[string]interface{}) 108 109 version := int(valid["version"].(int64)) 110 importFunc, ok := storageDeserializationFuncs[version] 111 if !ok { 112 return nil, errors.NotValidf("version %d", version) 113 } 114 sourceList := valid["storages"].([]interface{}) 115 return importStorageList(sourceList, importFunc) 116 } 117 118 func importStorageList(sourceList []interface{}, importFunc storageDeserializationFunc) ([]*storage, error) { 119 result := make([]*storage, 0, len(sourceList)) 120 for i, value := range sourceList { 121 source, ok := value.(map[string]interface{}) 122 if !ok { 123 return nil, errors.Errorf("unexpected value for storage %d, %T", i, value) 124 } 125 storage, err := importFunc(source) 126 if err != nil { 127 return nil, errors.Annotatef(err, "storage %d", i) 128 } 129 result = append(result, storage) 130 } 131 return result, nil 132 } 133 134 type storageDeserializationFunc func(map[string]interface{}) (*storage, error) 135 136 var storageDeserializationFuncs = map[int]storageDeserializationFunc{ 137 1: importStorageV1, 138 } 139 140 func importStorageV1(source map[string]interface{}) (*storage, error) { 141 fields := schema.Fields{ 142 "id": schema.String(), 143 "kind": schema.String(), 144 "owner": schema.String(), 145 "name": schema.String(), 146 "attachments": schema.List(schema.String()), 147 } 148 149 // Normally a list would have defaults, but in this case storage 150 // should always have at least one attachment. 151 checker := schema.FieldMap(fields, nil) // no defaults 152 153 coerced, err := checker.Coerce(source, nil) 154 if err != nil { 155 return nil, errors.Annotatef(err, "storage v1 schema check failed") 156 } 157 valid := coerced.(map[string]interface{}) 158 // From here we know that the map returned from the schema coercion 159 // contains fields of the right type. 160 result := &storage{ 161 ID_: valid["id"].(string), 162 Kind_: valid["kind"].(string), 163 Owner_: valid["owner"].(string), 164 Name_: valid["name"].(string), 165 Attachments_: convertToStringSlice(valid["attachments"]), 166 } 167 168 return result, nil 169 }