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