github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/agent/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 backend backend 21 storage storageAccess 22 resources facade.Resources 23 accessUnit common.GetAuthFunc 24 } 25 26 // newStorageAPI creates a new server-side Storage API facade. 27 func newStorageAPI( 28 backend backend, 29 storage storageAccess, 30 resources facade.Resources, 31 accessUnit common.GetAuthFunc, 32 ) (*StorageAPI, error) { 33 34 return &StorageAPI{ 35 backend: backend, 36 storage: storage, 37 resources: resources, 38 accessUnit: accessUnit, 39 }, nil 40 } 41 42 // UnitStorageAttachments returns the IDs of storage attachments for a collection of units. 43 func (s *StorageAPI) UnitStorageAttachments(args params.Entities) (params.StorageAttachmentIdsResults, error) { 44 canAccess, err := s.accessUnit() 45 if err != nil { 46 return params.StorageAttachmentIdsResults{}, err 47 } 48 result := params.StorageAttachmentIdsResults{ 49 Results: make([]params.StorageAttachmentIdsResult, len(args.Entities)), 50 } 51 for i, entity := range args.Entities { 52 storageAttachmentIds, err := s.getOneUnitStorageAttachmentIds(canAccess, entity.Tag) 53 if err == nil { 54 result.Results[i].Result = params.StorageAttachmentIds{ 55 storageAttachmentIds, 56 } 57 } 58 result.Results[i].Error = common.ServerError(err) 59 } 60 return result, nil 61 } 62 63 func (s *StorageAPI) getOneUnitStorageAttachmentIds(canAccess common.AuthFunc, unitTag string) ([]params.StorageAttachmentId, error) { 64 tag, err := names.ParseUnitTag(unitTag) 65 if err != nil || !canAccess(tag) { 66 return nil, common.ErrPerm 67 } 68 stateStorageAttachments, err := s.storage.UnitStorageAttachments(tag) 69 if errors.IsNotFound(err) { 70 return nil, common.ErrPerm 71 } else if err != nil { 72 return nil, err 73 } 74 result := make([]params.StorageAttachmentId, len(stateStorageAttachments)) 75 for i, stateStorageAttachment := range stateStorageAttachments { 76 result[i] = params.StorageAttachmentId{ 77 UnitTag: unitTag, 78 StorageTag: stateStorageAttachment.StorageInstance().String(), 79 } 80 } 81 return result, nil 82 } 83 84 // DestroyUnitStorageAttachments marks each storage attachment of the 85 // specified units as Dying. 86 func (s *StorageAPI) DestroyUnitStorageAttachments(args params.Entities) (params.ErrorResults, error) { 87 canAccess, err := s.accessUnit() 88 if err != nil { 89 return params.ErrorResults{}, err 90 } 91 result := params.ErrorResults{ 92 Results: make([]params.ErrorResult, len(args.Entities)), 93 } 94 one := func(tag string) error { 95 unitTag, err := names.ParseUnitTag(tag) 96 if err != nil { 97 return err 98 } 99 if !canAccess(unitTag) { 100 return common.ErrPerm 101 } 102 return s.storage.DestroyUnitStorageAttachments(unitTag) 103 } 104 for i, entity := range args.Entities { 105 err := one(entity.Tag) 106 result.Results[i].Error = common.ServerError(err) 107 } 108 return result, nil 109 } 110 111 // StorageAttachments returns the storage attachments with the specified tags. 112 func (s *StorageAPI) StorageAttachments(args params.StorageAttachmentIds) (params.StorageAttachmentResults, error) { 113 canAccess, err := s.accessUnit() 114 if err != nil { 115 return params.StorageAttachmentResults{}, err 116 } 117 result := params.StorageAttachmentResults{ 118 Results: make([]params.StorageAttachmentResult, len(args.Ids)), 119 } 120 for i, id := range args.Ids { 121 storageAttachment, err := s.getOneStorageAttachment(canAccess, id) 122 if err == nil { 123 result.Results[i].Result = storageAttachment 124 } 125 result.Results[i].Error = common.ServerError(err) 126 } 127 return result, nil 128 } 129 130 // StorageAttachmentLife returns the lifecycle state of the storage attachments 131 // with the specified tags. 132 func (s *StorageAPI) StorageAttachmentLife(args params.StorageAttachmentIds) (params.LifeResults, error) { 133 canAccess, err := s.accessUnit() 134 if err != nil { 135 return params.LifeResults{}, err 136 } 137 result := params.LifeResults{ 138 Results: make([]params.LifeResult, len(args.Ids)), 139 } 140 for i, id := range args.Ids { 141 stateStorageAttachment, err := s.getOneStateStorageAttachment(canAccess, id) 142 if err == nil { 143 life := stateStorageAttachment.Life() 144 result.Results[i].Life = params.Life(life.String()) 145 } 146 result.Results[i].Error = common.ServerError(err) 147 } 148 return result, nil 149 } 150 151 func (s *StorageAPI) getOneStorageAttachment(canAccess common.AuthFunc, id params.StorageAttachmentId) (params.StorageAttachment, error) { 152 stateStorageAttachment, err := s.getOneStateStorageAttachment(canAccess, id) 153 if err != nil { 154 return params.StorageAttachment{}, err 155 } 156 return s.fromStateStorageAttachment(stateStorageAttachment) 157 } 158 159 func (s *StorageAPI) getOneStateStorageAttachment(canAccess common.AuthFunc, id params.StorageAttachmentId) (state.StorageAttachment, error) { 160 unitTag, err := names.ParseUnitTag(id.UnitTag) 161 if err != nil { 162 return nil, err 163 } 164 if !canAccess(unitTag) { 165 return nil, common.ErrPerm 166 } 167 storageTag, err := names.ParseStorageTag(id.StorageTag) 168 if err != nil { 169 return nil, err 170 } 171 return s.storage.StorageAttachment(storageTag, unitTag) 172 } 173 174 func (s *StorageAPI) fromStateStorageAttachment(stateStorageAttachment state.StorageAttachment) (params.StorageAttachment, error) { 175 var hostTag names.Tag 176 hostTag = stateStorageAttachment.Unit() 177 u, err := s.backend.Unit(hostTag.Id()) 178 if err != nil { 179 return params.StorageAttachment{}, err 180 } 181 if u.ShouldBeAssigned() { 182 hostTag, err = unitAssignedMachine(s.backend, stateStorageAttachment.Unit()) 183 if err != nil { 184 return params.StorageAttachment{}, err 185 } 186 } 187 188 info, err := storagecommon.StorageAttachmentInfo( 189 s.storage, s.storage.VolumeAccess(), s.storage.FilesystemAccess(), stateStorageAttachment, hostTag) 190 if err != nil { 191 return params.StorageAttachment{}, err 192 } 193 stateStorageInstance, err := s.storage.StorageInstance(stateStorageAttachment.StorageInstance()) 194 if err != nil { 195 return params.StorageAttachment{}, err 196 } 197 var ownerTag string 198 if owner, ok := stateStorageInstance.Owner(); ok { 199 ownerTag = owner.String() 200 } 201 return params.StorageAttachment{ 202 stateStorageAttachment.StorageInstance().String(), 203 ownerTag, 204 stateStorageAttachment.Unit().String(), 205 params.StorageKind(stateStorageInstance.Kind()), 206 info.Location, 207 params.Life(stateStorageAttachment.Life().String()), 208 }, nil 209 } 210 211 // WatchUnitStorageAttachments creates watchers for a collection of units, 212 // each of which can be used to watch for lifecycle changes to the corresponding 213 // unit's storage attachments. 214 func (s *StorageAPI) WatchUnitStorageAttachments(args params.Entities) (params.StringsWatchResults, error) { 215 canAccess, err := s.accessUnit() 216 if err != nil { 217 return params.StringsWatchResults{}, err 218 } 219 results := params.StringsWatchResults{ 220 Results: make([]params.StringsWatchResult, len(args.Entities)), 221 } 222 for i, entity := range args.Entities { 223 result, err := s.watchOneUnitStorageAttachments(entity.Tag, canAccess) 224 if err == nil { 225 results.Results[i] = result 226 } 227 results.Results[i].Error = common.ServerError(err) 228 } 229 return results, nil 230 } 231 232 func (s *StorageAPI) watchOneUnitStorageAttachments(tag string, canAccess func(names.Tag) bool) (params.StringsWatchResult, error) { 233 nothing := params.StringsWatchResult{} 234 unitTag, err := names.ParseUnitTag(tag) 235 if err != nil || !canAccess(unitTag) { 236 return nothing, common.ErrPerm 237 } 238 watch := s.storage.WatchStorageAttachments(unitTag) 239 if changes, ok := <-watch.Changes(); ok { 240 return params.StringsWatchResult{ 241 StringsWatcherId: s.resources.Register(watch), 242 Changes: changes, 243 }, nil 244 } 245 return nothing, watcher.EnsureErr(watch) 246 } 247 248 // WatchStorageAttachments creates watchers for a collection of storage 249 // attachments, each of which can be used to watch changes to storage 250 // attachment info. 251 func (s *StorageAPI) WatchStorageAttachments(args params.StorageAttachmentIds) (params.NotifyWatchResults, error) { 252 canAccess, err := s.accessUnit() 253 if err != nil { 254 return params.NotifyWatchResults{}, err 255 } 256 results := params.NotifyWatchResults{ 257 Results: make([]params.NotifyWatchResult, len(args.Ids)), 258 } 259 for i, id := range args.Ids { 260 result, err := s.watchOneStorageAttachment(id, canAccess) 261 if err == nil { 262 results.Results[i] = result 263 } 264 results.Results[i].Error = common.ServerError(err) 265 } 266 return results, nil 267 } 268 269 func (s *StorageAPI) watchOneStorageAttachment(id params.StorageAttachmentId, canAccess func(names.Tag) bool) (params.NotifyWatchResult, error) { 270 // Watching a storage attachment is implemented as watching the 271 // underlying volume or filesystem attachment. The only thing 272 // we don't necessarily see in doing this is the lifecycle state 273 // changes, but these may be observed by using the 274 // WatchUnitStorageAttachments watcher. 275 nothing := params.NotifyWatchResult{} 276 unitTag, err := names.ParseUnitTag(id.UnitTag) 277 if err != nil || !canAccess(unitTag) { 278 return nothing, common.ErrPerm 279 } 280 storageTag, err := names.ParseStorageTag(id.StorageTag) 281 if err != nil { 282 return nothing, err 283 } 284 285 var hostTag names.Tag 286 hostTag = unitTag 287 u, err := s.backend.Unit(unitTag.Id()) 288 if err != nil { 289 return nothing, err 290 } 291 if u.ShouldBeAssigned() { 292 hostTag, err = unitAssignedMachine(s.backend, unitTag) 293 if err != nil { 294 return nothing, err 295 } 296 } 297 watch, err := watchStorageAttachment( 298 s.storage, s.storage.VolumeAccess(), s.storage.FilesystemAccess(), storageTag, hostTag, unitTag) 299 if err != nil { 300 return nothing, errors.Trace(err) 301 } 302 if _, ok := <-watch.Changes(); ok { 303 return params.NotifyWatchResult{ 304 NotifyWatcherId: s.resources.Register(watch), 305 }, nil 306 } 307 return nothing, watcher.EnsureErr(watch) 308 } 309 310 // RemoveStorageAttachments removes the specified storage 311 // attachments from state. 312 func (s *StorageAPI) RemoveStorageAttachments(args params.StorageAttachmentIds) (params.ErrorResults, error) { 313 canAccess, err := s.accessUnit() 314 if err != nil { 315 return params.ErrorResults{}, err 316 } 317 results := params.ErrorResults{ 318 Results: make([]params.ErrorResult, len(args.Ids)), 319 } 320 for i, id := range args.Ids { 321 err := s.removeOneStorageAttachment(id, canAccess) 322 if err != nil { 323 results.Results[i].Error = common.ServerError(err) 324 } 325 } 326 return results, nil 327 } 328 329 func (s *StorageAPI) removeOneStorageAttachment(id params.StorageAttachmentId, canAccess func(names.Tag) bool) error { 330 unitTag, err := names.ParseUnitTag(id.UnitTag) 331 if err != nil { 332 return err 333 } 334 if !canAccess(unitTag) { 335 return common.ErrPerm 336 } 337 storageTag, err := names.ParseStorageTag(id.StorageTag) 338 if err != nil { 339 return err 340 } 341 err = s.storage.RemoveStorageAttachment(storageTag, unitTag) 342 if errors.IsNotFound(err) { 343 err = nil 344 } 345 return err 346 } 347 348 // AddUnitStorage validates and creates additional storage instances for units. 349 // Failures on an individual storage instance do not block remaining 350 // instances from being processed. 351 func (s *StorageAPI) AddUnitStorage( 352 args params.StoragesAddParams, 353 ) (params.ErrorResults, error) { 354 canAccess, err := s.accessUnit() 355 if err != nil { 356 return params.ErrorResults{}, err 357 } 358 if len(args.Storages) == 0 { 359 return params.ErrorResults{}, nil 360 } 361 362 serverErr := func(err error) params.ErrorResult { 363 return params.ErrorResult{common.ServerError(err)} 364 } 365 366 storageErr := func(err error, s, u string) params.ErrorResult { 367 return serverErr(errors.Annotatef(err, "adding storage %v for %v", s, u)) 368 } 369 370 result := make([]params.ErrorResult, len(args.Storages)) 371 for i, one := range args.Storages { 372 u, err := accessUnitTag(one.UnitTag, canAccess) 373 if err != nil { 374 result[i] = serverErr(err) 375 continue 376 } 377 378 cons, err := unitStorageConstraints(s.backend, u) 379 if err != nil { 380 result[i] = serverErr(err) 381 continue 382 } 383 384 oneCons, err := validConstraints(one, cons) 385 if err != nil { 386 result[i] = storageErr(err, one.StorageName, one.UnitTag) 387 continue 388 } 389 390 _, err = s.storage.AddStorageForUnit(u, one.StorageName, oneCons) 391 if err != nil { 392 result[i] = storageErr(err, one.StorageName, one.UnitTag) 393 } 394 } 395 return params.ErrorResults{Results: result}, nil 396 } 397 398 func validConstraints( 399 p params.StorageAddParams, 400 cons map[string]state.StorageConstraints, 401 ) (state.StorageConstraints, error) { 402 emptyCons := state.StorageConstraints{} 403 404 result, ok := cons[p.StorageName] 405 if !ok { 406 return emptyCons, errors.NotFoundf("storage %q", p.StorageName) 407 } 408 409 onlyCount := params.StorageConstraints{Count: p.Constraints.Count} 410 if p.Constraints != onlyCount { 411 return emptyCons, errors.New("only count can be specified") 412 } 413 414 if p.Constraints.Count == nil || *p.Constraints.Count == 0 { 415 return emptyCons, errors.New("count must be specified") 416 } 417 418 result.Count = *p.Constraints.Count 419 return result, nil 420 } 421 422 func accessUnitTag(tag string, canAccess func(names.Tag) bool) (names.UnitTag, error) { 423 u, err := names.ParseUnitTag(tag) 424 if err != nil { 425 return names.UnitTag{}, errors.Annotatef(err, "parsing unit tag %v", tag) 426 } 427 if !canAccess(u) { 428 return names.UnitTag{}, common.ErrPerm 429 } 430 return u, nil 431 } 432 433 // watchStorageAttachment returns a state.NotifyWatcher that reacts to changes 434 // to the VolumeAttachmentInfo or FilesystemAttachmentInfo corresponding to the 435 // tags specified. 436 func watchStorageAttachment( 437 st storageInterface, 438 stVolume storageVolumeInterface, 439 stFile storageFilesystemInterface, 440 storageTag names.StorageTag, 441 hostTag names.Tag, 442 unitTag names.UnitTag, 443 ) (state.NotifyWatcher, error) { 444 storageInstance, err := st.StorageInstance(storageTag) 445 if err != nil { 446 return nil, errors.Annotate(err, "getting storage instance") 447 } 448 var watchers []state.NotifyWatcher 449 switch storageInstance.Kind() { 450 case state.StorageKindBlock: 451 if stVolume == nil { 452 return nil, errors.NotImplementedf("BlockStorage instance") 453 } 454 volume, err := stVolume.StorageInstanceVolume(storageTag) 455 if err != nil { 456 return nil, errors.Annotate(err, "getting storage volume") 457 } 458 // We need to watch both the volume attachment, and the 459 // machine's block devices. A volume attachment's block 460 // device could change (most likely, become present). 461 watchers = []state.NotifyWatcher{ 462 stVolume.WatchVolumeAttachment(hostTag, volume.VolumeTag()), 463 } 464 465 // TODO(caas) - we currently only support block devices on machines. 466 if hostTag.Kind() == names.MachineTagKind { 467 // TODO(axw) 2015-09-30 #1501203 468 // We should filter the events to only those relevant 469 // to the volume attachment. This means we would need 470 // to either start th block device watcher after we 471 // have provisioned the volume attachment (cleaner?), 472 // or have the filter ignore changes until the volume 473 // attachment is provisioned. 474 watchers = append(watchers, stVolume.WatchBlockDevices(hostTag.(names.MachineTag))) 475 } 476 case state.StorageKindFilesystem: 477 if stFile == nil { 478 return nil, errors.NotImplementedf("FilesystemStorage instance") 479 } 480 filesystem, err := stFile.StorageInstanceFilesystem(storageTag) 481 if err != nil { 482 return nil, errors.Annotate(err, "getting storage filesystem") 483 } 484 watchers = []state.NotifyWatcher{ 485 stFile.WatchFilesystemAttachment(hostTag, filesystem.FilesystemTag()), 486 } 487 default: 488 return nil, errors.Errorf("invalid storage kind %v", storageInstance.Kind()) 489 } 490 watchers = append(watchers, st.WatchStorageAttachment(storageTag, unitTag)) 491 return common.NewMultiNotifyWatcher(watchers...), nil 492 }