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