github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/apiserver/uniter/storage.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package uniter 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/names" 9 10 "github.com/juju/juju/apiserver/common" 11 "github.com/juju/juju/apiserver/params" 12 "github.com/juju/juju/state" 13 "github.com/juju/juju/state/watcher" 14 ) 15 16 // StorageAPI provides access to the Storage API facade. 17 type StorageAPI struct { 18 st storageStateInterface 19 resources *common.Resources 20 accessUnit common.GetAuthFunc 21 } 22 23 // newStorageAPI creates a new server-side Storage API facade. 24 func newStorageAPI( 25 st storageStateInterface, 26 resources *common.Resources, 27 accessUnit common.GetAuthFunc, 28 ) (*StorageAPI, error) { 29 30 return &StorageAPI{ 31 st: st, 32 resources: resources, 33 accessUnit: accessUnit, 34 }, nil 35 } 36 37 // UnitStorageAttachments returns the IDs of storage attachments for a collection of units. 38 func (s *StorageAPI) UnitStorageAttachments(args params.Entities) (params.StorageAttachmentIdsResults, error) { 39 canAccess, err := s.accessUnit() 40 if err != nil { 41 return params.StorageAttachmentIdsResults{}, err 42 } 43 result := params.StorageAttachmentIdsResults{ 44 Results: make([]params.StorageAttachmentIdsResult, len(args.Entities)), 45 } 46 for i, entity := range args.Entities { 47 storageAttachmentIds, err := s.getOneUnitStorageAttachmentIds(canAccess, entity.Tag) 48 if err == nil { 49 result.Results[i].Result = params.StorageAttachmentIds{ 50 storageAttachmentIds, 51 } 52 } 53 result.Results[i].Error = common.ServerError(err) 54 } 55 return result, nil 56 } 57 58 func (s *StorageAPI) getOneUnitStorageAttachmentIds(canAccess common.AuthFunc, unitTag string) ([]params.StorageAttachmentId, error) { 59 tag, err := names.ParseUnitTag(unitTag) 60 if err != nil || !canAccess(tag) { 61 return nil, common.ErrPerm 62 } 63 stateStorageAttachments, err := s.st.UnitStorageAttachments(tag) 64 if errors.IsNotFound(err) { 65 return nil, common.ErrPerm 66 } else if err != nil { 67 return nil, err 68 } 69 result := make([]params.StorageAttachmentId, len(stateStorageAttachments)) 70 for i, stateStorageAttachment := range stateStorageAttachments { 71 result[i] = params.StorageAttachmentId{ 72 UnitTag: unitTag, 73 StorageTag: stateStorageAttachment.StorageInstance().String(), 74 } 75 } 76 return result, nil 77 } 78 79 // DestroyUnitStorageAttachments marks each storage attachment of the 80 // specified units as Dying. 81 func (s *StorageAPI) DestroyUnitStorageAttachments(args params.Entities) (params.ErrorResults, error) { 82 canAccess, err := s.accessUnit() 83 if err != nil { 84 return params.ErrorResults{}, err 85 } 86 result := params.ErrorResults{ 87 Results: make([]params.ErrorResult, len(args.Entities)), 88 } 89 one := func(tag string) error { 90 unitTag, err := names.ParseUnitTag(tag) 91 if err != nil { 92 return err 93 } 94 if !canAccess(unitTag) { 95 return common.ErrPerm 96 } 97 return s.st.DestroyUnitStorageAttachments(unitTag) 98 } 99 for i, entity := range args.Entities { 100 err := one(entity.Tag) 101 result.Results[i].Error = common.ServerError(err) 102 } 103 return result, nil 104 } 105 106 // StorageAttachments returns the storage attachments with the specified tags. 107 func (s *StorageAPI) StorageAttachments(args params.StorageAttachmentIds) (params.StorageAttachmentResults, error) { 108 canAccess, err := s.accessUnit() 109 if err != nil { 110 return params.StorageAttachmentResults{}, err 111 } 112 result := params.StorageAttachmentResults{ 113 Results: make([]params.StorageAttachmentResult, len(args.Ids)), 114 } 115 for i, id := range args.Ids { 116 storageAttachment, err := s.getOneStorageAttachment(canAccess, id) 117 if err == nil { 118 result.Results[i].Result = storageAttachment 119 } 120 result.Results[i].Error = common.ServerError(err) 121 } 122 return result, nil 123 } 124 125 // StorageAttachmentLife returns the lifecycle state of the storage attachments 126 // with the specified tags. 127 func (s *StorageAPI) StorageAttachmentLife(args params.StorageAttachmentIds) (params.LifeResults, error) { 128 canAccess, err := s.accessUnit() 129 if err != nil { 130 return params.LifeResults{}, err 131 } 132 result := params.LifeResults{ 133 Results: make([]params.LifeResult, len(args.Ids)), 134 } 135 for i, id := range args.Ids { 136 stateStorageAttachment, err := s.getOneStateStorageAttachment(canAccess, id) 137 if err == nil { 138 life := stateStorageAttachment.Life() 139 result.Results[i].Life = params.Life(life.String()) 140 } 141 result.Results[i].Error = common.ServerError(err) 142 } 143 return result, nil 144 } 145 146 func (s *StorageAPI) getOneStorageAttachment(canAccess common.AuthFunc, id params.StorageAttachmentId) (params.StorageAttachment, error) { 147 stateStorageAttachment, err := s.getOneStateStorageAttachment(canAccess, id) 148 if err != nil { 149 return params.StorageAttachment{}, err 150 } 151 return s.fromStateStorageAttachment(stateStorageAttachment) 152 } 153 154 func (s *StorageAPI) getOneStateStorageAttachment(canAccess common.AuthFunc, id params.StorageAttachmentId) (state.StorageAttachment, error) { 155 unitTag, err := names.ParseUnitTag(id.UnitTag) 156 if err != nil { 157 return nil, err 158 } 159 if !canAccess(unitTag) { 160 return nil, common.ErrPerm 161 } 162 storageTag, err := names.ParseStorageTag(id.StorageTag) 163 if err != nil { 164 return nil, err 165 } 166 return s.st.StorageAttachment(storageTag, unitTag) 167 } 168 169 func (s *StorageAPI) fromStateStorageAttachment(stateStorageAttachment state.StorageAttachment) (params.StorageAttachment, error) { 170 machineTag, err := s.st.UnitAssignedMachine(stateStorageAttachment.Unit()) 171 if err != nil { 172 return params.StorageAttachment{}, err 173 } 174 info, err := common.StorageAttachmentInfo(s.st, stateStorageAttachment, machineTag) 175 if err != nil { 176 return params.StorageAttachment{}, err 177 } 178 stateStorageInstance, err := s.st.StorageInstance(stateStorageAttachment.StorageInstance()) 179 if err != nil { 180 return params.StorageAttachment{}, err 181 } 182 return params.StorageAttachment{ 183 stateStorageAttachment.StorageInstance().String(), 184 stateStorageInstance.Owner().String(), 185 stateStorageAttachment.Unit().String(), 186 params.StorageKind(stateStorageInstance.Kind()), 187 info.Location, 188 params.Life(stateStorageAttachment.Life().String()), 189 }, nil 190 } 191 192 // WatchUnitStorageAttachments creates watchers for a collection of units, 193 // each of which can be used to watch for lifecycle changes to the corresponding 194 // unit's storage attachments. 195 func (s *StorageAPI) WatchUnitStorageAttachments(args params.Entities) (params.StringsWatchResults, error) { 196 canAccess, err := s.accessUnit() 197 if err != nil { 198 return params.StringsWatchResults{}, err 199 } 200 results := params.StringsWatchResults{ 201 Results: make([]params.StringsWatchResult, len(args.Entities)), 202 } 203 for i, entity := range args.Entities { 204 result, err := s.watchOneUnitStorageAttachments(entity.Tag, canAccess) 205 if err == nil { 206 results.Results[i] = result 207 } 208 results.Results[i].Error = common.ServerError(err) 209 } 210 return results, nil 211 } 212 213 func (s *StorageAPI) watchOneUnitStorageAttachments(tag string, canAccess func(names.Tag) bool) (params.StringsWatchResult, error) { 214 nothing := params.StringsWatchResult{} 215 unitTag, err := names.ParseUnitTag(tag) 216 if err != nil || !canAccess(unitTag) { 217 return nothing, common.ErrPerm 218 } 219 watch := s.st.WatchStorageAttachments(unitTag) 220 if changes, ok := <-watch.Changes(); ok { 221 return params.StringsWatchResult{ 222 StringsWatcherId: s.resources.Register(watch), 223 Changes: changes, 224 }, nil 225 } 226 return nothing, watcher.EnsureErr(watch) 227 } 228 229 // WatchStorageAttachments creates watchers for a collection of storage 230 // attachments, each of which can be used to watch changes to storage 231 // attachment info. 232 func (s *StorageAPI) WatchStorageAttachments(args params.StorageAttachmentIds) (params.NotifyWatchResults, error) { 233 canAccess, err := s.accessUnit() 234 if err != nil { 235 return params.NotifyWatchResults{}, err 236 } 237 results := params.NotifyWatchResults{ 238 Results: make([]params.NotifyWatchResult, len(args.Ids)), 239 } 240 for i, id := range args.Ids { 241 result, err := s.watchOneStorageAttachment(id, canAccess) 242 if err == nil { 243 results.Results[i] = result 244 } 245 results.Results[i].Error = common.ServerError(err) 246 } 247 return results, nil 248 } 249 250 func (s *StorageAPI) watchOneStorageAttachment(id params.StorageAttachmentId, canAccess func(names.Tag) bool) (params.NotifyWatchResult, error) { 251 // Watching a storage attachment is implemented as watching the 252 // underlying volume or filesystem attachment. The only thing 253 // we don't necessarily see in doing this is the lifecycle state 254 // changes, but these may be observed by using the 255 // WatchUnitStorageAttachments watcher. 256 nothing := params.NotifyWatchResult{} 257 unitTag, err := names.ParseUnitTag(id.UnitTag) 258 if err != nil || !canAccess(unitTag) { 259 return nothing, common.ErrPerm 260 } 261 storageTag, err := names.ParseStorageTag(id.StorageTag) 262 if err != nil { 263 return nothing, err 264 } 265 machineTag, err := s.st.UnitAssignedMachine(unitTag) 266 if err != nil { 267 return nothing, err 268 } 269 watch, err := common.WatchStorageAttachment(s.st, storageTag, machineTag, unitTag) 270 if err != nil { 271 return nothing, errors.Trace(err) 272 } 273 if _, ok := <-watch.Changes(); ok { 274 return params.NotifyWatchResult{ 275 NotifyWatcherId: s.resources.Register(watch), 276 }, nil 277 } 278 return nothing, watcher.EnsureErr(watch) 279 } 280 281 // RemoveStorageAttachments removes the specified storage 282 // attachments from state. 283 func (s *StorageAPI) RemoveStorageAttachments(args params.StorageAttachmentIds) (params.ErrorResults, error) { 284 canAccess, err := s.accessUnit() 285 if err != nil { 286 return params.ErrorResults{}, err 287 } 288 results := params.ErrorResults{ 289 Results: make([]params.ErrorResult, len(args.Ids)), 290 } 291 for i, id := range args.Ids { 292 err := s.removeOneStorageAttachment(id, canAccess) 293 if err != nil { 294 results.Results[i].Error = common.ServerError(err) 295 } 296 } 297 return results, nil 298 } 299 300 func (s *StorageAPI) removeOneStorageAttachment(id params.StorageAttachmentId, canAccess func(names.Tag) bool) error { 301 unitTag, err := names.ParseUnitTag(id.UnitTag) 302 if err != nil { 303 return err 304 } 305 if !canAccess(unitTag) { 306 return common.ErrPerm 307 } 308 storageTag, err := names.ParseStorageTag(id.StorageTag) 309 if err != nil { 310 return err 311 } 312 return s.st.RemoveStorageAttachment(storageTag, unitTag) 313 } 314 315 // AddUnitStorage validates and creates additional storage instances for units. 316 // Failures on an individual storage instance do not block remaining 317 // instances from being processed. 318 func (a *StorageAPI) AddUnitStorage( 319 args params.StoragesAddParams, 320 ) (params.ErrorResults, error) { 321 canAccess, err := a.accessUnit() 322 if err != nil { 323 return params.ErrorResults{}, err 324 } 325 if len(args.Storages) == 0 { 326 return params.ErrorResults{}, nil 327 } 328 329 serverErr := func(err error) params.ErrorResult { 330 return params.ErrorResult{common.ServerError(err)} 331 } 332 333 storageErr := func(err error, s, u string) params.ErrorResult { 334 return serverErr(errors.Annotatef(err, "adding storage %v for %v", s, u)) 335 } 336 337 result := make([]params.ErrorResult, len(args.Storages)) 338 for i, one := range args.Storages { 339 u, err := accessUnitTag(one.UnitTag, canAccess) 340 if err != nil { 341 result[i] = serverErr(err) 342 continue 343 } 344 345 cons, err := a.st.UnitStorageConstraints(u) 346 if err != nil { 347 result[i] = serverErr(err) 348 continue 349 } 350 351 oneCons, err := validConstraints(one, cons) 352 if err != nil { 353 result[i] = storageErr(err, one.StorageName, one.UnitTag) 354 continue 355 } 356 357 err = a.st.AddStorageForUnit(u, one.StorageName, oneCons) 358 if err != nil { 359 result[i] = storageErr(err, one.StorageName, one.UnitTag) 360 } 361 } 362 return params.ErrorResults{Results: result}, nil 363 } 364 365 func validConstraints( 366 p params.StorageAddParams, 367 cons map[string]state.StorageConstraints, 368 ) (state.StorageConstraints, error) { 369 emptyCons := state.StorageConstraints{} 370 371 result, ok := cons[p.StorageName] 372 if !ok { 373 return emptyCons, errors.NotFoundf("storage %q", p.StorageName) 374 } 375 376 onlyCount := params.StorageConstraints{Count: p.Constraints.Count} 377 if p.Constraints != onlyCount { 378 return emptyCons, errors.New("only count can be specified") 379 } 380 381 if p.Constraints.Count == nil || *p.Constraints.Count == 0 { 382 return emptyCons, errors.New("count must be specified") 383 } 384 385 result.Count = *p.Constraints.Count 386 return result, nil 387 } 388 389 func accessUnitTag(tag string, canAccess func(names.Tag) bool) (names.UnitTag, error) { 390 u, err := names.ParseUnitTag(tag) 391 if err != nil { 392 return names.UnitTag{}, errors.Annotatef(err, "parsing unit tag %v", tag) 393 } 394 if !canAccess(u) { 395 return names.UnitTag{}, common.ErrPerm 396 } 397 return u, nil 398 }