github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/agent/storageprovisioner/storageprovisioner.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package storageprovisioner 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/loggo" 9 "gopkg.in/juju/names.v2" 10 11 "github.com/juju/juju/apiserver/common" 12 "github.com/juju/juju/apiserver/common/storagecommon" 13 "github.com/juju/juju/apiserver/facade" 14 "github.com/juju/juju/apiserver/facades/agent/storageprovisioner/internal/filesystemwatcher" 15 "github.com/juju/juju/apiserver/params" 16 "github.com/juju/juju/core/instance" 17 "github.com/juju/juju/state" 18 "github.com/juju/juju/state/watcher" 19 "github.com/juju/juju/storage" 20 "github.com/juju/juju/storage/poolmanager" 21 ) 22 23 var logger = loggo.GetLogger("juju.apiserver.storageprovisioner") 24 25 // StorageProvisionerAPIv4 provides the StorageProvisioner API v4 facade. 26 type StorageProvisionerAPIv4 struct { 27 *StorageProvisionerAPIv3 28 } 29 30 // StorageProvisionerAPIv3 provides the StorageProvisioner API v3 facade. 31 type StorageProvisionerAPIv3 struct { 32 *common.LifeGetter 33 *common.DeadEnsurer 34 *common.InstanceIdGetter 35 *common.StatusSetter 36 37 st Backend 38 sb StorageBackend 39 resources facade.Resources 40 authorizer facade.Authorizer 41 registry storage.ProviderRegistry 42 poolManager poolmanager.PoolManager 43 getScopeAuthFunc common.GetAuthFunc 44 getStorageEntityAuthFunc common.GetAuthFunc 45 getMachineAuthFunc common.GetAuthFunc 46 getBlockDevicesAuthFunc common.GetAuthFunc 47 getAttachmentAuthFunc func() (func(names.Tag, names.Tag) bool, error) 48 } 49 50 // NewStorageProvisionerAPIv4 creates a new server-side StorageProvisioner v4 facade. 51 func NewStorageProvisionerAPIv4(v3 *StorageProvisionerAPIv3) *StorageProvisionerAPIv4 { 52 return &StorageProvisionerAPIv4{v3} 53 } 54 55 // NewStorageProvisionerAPIv3 creates a new server-side StorageProvisioner v3 facade. 56 func NewStorageProvisionerAPIv3( 57 st Backend, 58 sb StorageBackend, 59 resources facade.Resources, 60 authorizer facade.Authorizer, 61 registry storage.ProviderRegistry, 62 poolManager poolmanager.PoolManager, 63 ) (*StorageProvisionerAPIv3, error) { 64 if !authorizer.AuthMachineAgent() { 65 return nil, common.ErrPerm 66 } 67 canAccessStorageMachine := func(tag names.Tag, allowController bool) bool { 68 authEntityTag := authorizer.GetAuthTag() 69 if tag == authEntityTag { 70 // Machine agents can access volumes 71 // scoped to their own machine. 72 return true 73 } 74 parentId := state.ParentId(tag.Id()) 75 if parentId == "" { 76 return allowController && authorizer.AuthController() 77 } 78 // All containers with the authenticated 79 // machine as a parent are accessible by it. 80 return names.NewMachineTag(parentId) == authEntityTag 81 } 82 getScopeAuthFunc := func() (common.AuthFunc, error) { 83 return func(tag names.Tag) bool { 84 switch tag := tag.(type) { 85 case names.ModelTag: 86 // Controllers can access all volumes 87 // and filesystems scoped to the environment. 88 isModelManager := authorizer.AuthController() 89 return isModelManager && tag == st.ModelTag() 90 case names.MachineTag: 91 return canAccessStorageMachine(tag, false) 92 case names.ApplicationTag: 93 return authorizer.AuthController() 94 default: 95 return false 96 } 97 }, nil 98 } 99 canAccessStorageEntity := func(tag names.Tag, allowMachines bool) bool { 100 switch tag := tag.(type) { 101 case names.VolumeTag: 102 machineTag, ok := names.VolumeMachine(tag) 103 if ok { 104 return canAccessStorageMachine(machineTag, false) 105 } 106 return authorizer.AuthController() 107 case names.FilesystemTag: 108 machineTag, ok := names.FilesystemMachine(tag) 109 if ok { 110 return canAccessStorageMachine(machineTag, false) 111 } 112 _, ok = names.FilesystemUnit(tag) 113 if ok { 114 return authorizer.AuthController() 115 } 116 f, err := sb.Filesystem(tag) 117 if err != nil { 118 return false 119 } 120 volumeTag, err := f.Volume() 121 if err == nil { 122 // The filesystem has a backing volume. If the 123 // authenticated agent has access to any of the 124 // machines that the volume is attached to, then 125 // it may access the filesystem too. 126 volumeAttachments, err := sb.VolumeAttachments(volumeTag) 127 if err != nil { 128 return false 129 } 130 for _, a := range volumeAttachments { 131 if canAccessStorageMachine(a.Host(), false) { 132 return true 133 } 134 } 135 } else if err != state.ErrNoBackingVolume { 136 return false 137 } 138 return authorizer.AuthController() 139 case names.MachineTag: 140 return allowMachines && canAccessStorageMachine(tag, true) 141 case names.ApplicationTag: 142 return authorizer.AuthController() 143 default: 144 return false 145 } 146 } 147 getStorageEntityAuthFunc := func() (common.AuthFunc, error) { 148 return func(tag names.Tag) bool { 149 return canAccessStorageEntity(tag, false) 150 }, nil 151 } 152 getLifeAuthFunc := func() (common.AuthFunc, error) { 153 return func(tag names.Tag) bool { 154 return canAccessStorageEntity(tag, true) 155 }, nil 156 } 157 getAttachmentAuthFunc := func() (func(names.Tag, names.Tag) bool, error) { 158 // getAttachmentAuthFunc returns a function that validates 159 // access by the authenticated user to an attachment. 160 return func(hostTag names.Tag, attachmentTag names.Tag) bool { 161 if hostTag.Kind() == names.UnitTagKind { 162 return authorizer.AuthController() 163 } 164 165 // Machine agents can access their own machine, and 166 // machines contained. Controllers can access 167 // top-level machines. 168 machineAccessOk := canAccessStorageMachine(hostTag, true) 169 170 if !machineAccessOk { 171 return false 172 } 173 174 // Controllers can access model-scoped 175 // volumes and volumes scoped to their own machines. 176 // Other machine agents can access volumes regardless 177 // of their scope. 178 if !authorizer.AuthController() { 179 return true 180 } 181 var machineScope names.MachineTag 182 var hasMachineScope bool 183 switch attachmentTag := attachmentTag.(type) { 184 case names.VolumeTag: 185 machineScope, hasMachineScope = names.VolumeMachine(attachmentTag) 186 case names.FilesystemTag: 187 machineScope, hasMachineScope = names.FilesystemMachine(attachmentTag) 188 } 189 return !hasMachineScope || machineScope == authorizer.GetAuthTag() 190 }, nil 191 } 192 getMachineAuthFunc := func() (common.AuthFunc, error) { 193 return func(tag names.Tag) bool { 194 if tag, ok := tag.(names.MachineTag); ok { 195 return canAccessStorageMachine(tag, true) 196 } 197 return false 198 }, nil 199 } 200 getBlockDevicesAuthFunc := func() (common.AuthFunc, error) { 201 return func(tag names.Tag) bool { 202 if tag, ok := tag.(names.MachineTag); ok { 203 return canAccessStorageMachine(tag, false) 204 } 205 return false 206 }, nil 207 } 208 return &StorageProvisionerAPIv3{ 209 LifeGetter: common.NewLifeGetter(st, getLifeAuthFunc), 210 DeadEnsurer: common.NewDeadEnsurer(st, getStorageEntityAuthFunc), 211 InstanceIdGetter: common.NewInstanceIdGetter(st, getMachineAuthFunc), 212 StatusSetter: common.NewStatusSetter(st, getStorageEntityAuthFunc), 213 214 st: st, 215 sb: sb, 216 resources: resources, 217 authorizer: authorizer, 218 registry: registry, 219 poolManager: poolManager, 220 getScopeAuthFunc: getScopeAuthFunc, 221 getStorageEntityAuthFunc: getStorageEntityAuthFunc, 222 getAttachmentAuthFunc: getAttachmentAuthFunc, 223 getMachineAuthFunc: getMachineAuthFunc, 224 getBlockDevicesAuthFunc: getBlockDevicesAuthFunc, 225 }, nil 226 } 227 228 // WatchApplications starts a StringsWatcher to watch CAAS applications 229 // deployed to this model. 230 func (s *StorageProvisionerAPIv4) WatchApplications() (params.StringsWatchResult, error) { 231 watch := s.st.WatchApplications() 232 if changes, ok := <-watch.Changes(); ok { 233 return params.StringsWatchResult{ 234 StringsWatcherId: s.resources.Register(watch), 235 Changes: changes, 236 }, nil 237 } 238 return params.StringsWatchResult{}, watcher.EnsureErr(watch) 239 } 240 241 // WatchBlockDevices watches for changes to the specified machines' block devices. 242 func (s *StorageProvisionerAPIv3) WatchBlockDevices(args params.Entities) (params.NotifyWatchResults, error) { 243 canAccess, err := s.getBlockDevicesAuthFunc() 244 if err != nil { 245 return params.NotifyWatchResults{}, common.ServerError(common.ErrPerm) 246 } 247 results := params.NotifyWatchResults{ 248 Results: make([]params.NotifyWatchResult, len(args.Entities)), 249 } 250 one := func(arg params.Entity) (string, error) { 251 machineTag, err := names.ParseMachineTag(arg.Tag) 252 if err != nil { 253 return "", err 254 } 255 if !canAccess(machineTag) { 256 return "", common.ErrPerm 257 } 258 w := s.sb.WatchBlockDevices(machineTag) 259 if _, ok := <-w.Changes(); ok { 260 return s.resources.Register(w), nil 261 } 262 return "", watcher.EnsureErr(w) 263 } 264 for i, arg := range args.Entities { 265 var result params.NotifyWatchResult 266 id, err := one(arg) 267 if err != nil { 268 result.Error = common.ServerError(err) 269 } else { 270 result.NotifyWatcherId = id 271 } 272 results.Results[i] = result 273 } 274 return results, nil 275 } 276 277 // WatchMachines watches for changes to the specified machines. 278 func (s *StorageProvisionerAPIv3) WatchMachines(args params.Entities) (params.NotifyWatchResults, error) { 279 canAccess, err := s.getMachineAuthFunc() 280 if err != nil { 281 return params.NotifyWatchResults{}, common.ServerError(common.ErrPerm) 282 } 283 results := params.NotifyWatchResults{ 284 Results: make([]params.NotifyWatchResult, len(args.Entities)), 285 } 286 one := func(arg params.Entity) (string, error) { 287 machineTag, err := names.ParseMachineTag(arg.Tag) 288 if err != nil { 289 return "", err 290 } 291 if !canAccess(machineTag) { 292 return "", common.ErrPerm 293 } 294 w, err := s.st.WatchMachine(machineTag) 295 if err != nil { 296 return "", errors.Trace(err) 297 } 298 if _, ok := <-w.Changes(); ok { 299 return s.resources.Register(w), nil 300 } 301 return "", watcher.EnsureErr(w) 302 } 303 for i, arg := range args.Entities { 304 var result params.NotifyWatchResult 305 id, err := one(arg) 306 if err != nil { 307 result.Error = common.ServerError(err) 308 } else { 309 result.NotifyWatcherId = id 310 } 311 results.Results[i] = result 312 } 313 return results, nil 314 } 315 316 // WatchVolumes watches for changes to volumes scoped to the 317 // entity with the tag passed to NewState. 318 func (s *StorageProvisionerAPIv3) WatchVolumes(args params.Entities) (params.StringsWatchResults, error) { 319 return s.watchStorageEntities(args, s.sb.WatchModelVolumes, s.sb.WatchMachineVolumes, nil) 320 } 321 322 // WatchFilesystems watches for changes to filesystems scoped 323 // to the entity with the tag passed to NewState. 324 func (s *StorageProvisionerAPIv3) WatchFilesystems(args params.Entities) (params.StringsWatchResults, error) { 325 w := filesystemwatcher.Watchers{s.sb} 326 return s.watchStorageEntities(args, 327 w.WatchModelManagedFilesystems, 328 w.WatchMachineManagedFilesystems, 329 w.WatchUnitManagedFilesystems) 330 } 331 332 func (s *StorageProvisionerAPIv3) watchStorageEntities( 333 args params.Entities, 334 watchEnvironStorage func() state.StringsWatcher, 335 watchMachineStorage func(names.MachineTag) state.StringsWatcher, 336 watchApplicationStorage func(tag names.ApplicationTag) state.StringsWatcher, 337 ) (params.StringsWatchResults, error) { 338 canAccess, err := s.getScopeAuthFunc() 339 if err != nil { 340 return params.StringsWatchResults{}, common.ServerError(common.ErrPerm) 341 } 342 results := params.StringsWatchResults{ 343 Results: make([]params.StringsWatchResult, len(args.Entities)), 344 } 345 one := func(arg params.Entity) (string, []string, error) { 346 tag, err := names.ParseTag(arg.Tag) 347 if err != nil || !canAccess(tag) { 348 return "", nil, common.ErrPerm 349 } 350 var w state.StringsWatcher 351 switch tag := tag.(type) { 352 case names.MachineTag: 353 w = watchMachineStorage(tag) 354 case names.ModelTag: 355 w = watchEnvironStorage() 356 case names.ApplicationTag: 357 w = watchApplicationStorage(tag) 358 default: 359 return "", nil, common.ServerError(errors.NotSupportedf("watching storage for %v", tag)) 360 } 361 362 if changes, ok := <-w.Changes(); ok { 363 return s.resources.Register(w), changes, nil 364 } 365 return "", nil, watcher.EnsureErr(w) 366 } 367 for i, arg := range args.Entities { 368 var result params.StringsWatchResult 369 id, changes, err := one(arg) 370 if err != nil { 371 result.Error = common.ServerError(err) 372 } else { 373 result.StringsWatcherId = id 374 result.Changes = changes 375 } 376 results.Results[i] = result 377 } 378 return results, nil 379 } 380 381 // WatchVolumeAttachments watches for changes to volume attachments scoped to 382 // the entity with the tag passed to NewState. 383 func (s *StorageProvisionerAPIv3) WatchVolumeAttachments(args params.Entities) (params.MachineStorageIdsWatchResults, error) { 384 return s.watchAttachments( 385 args, 386 s.sb.WatchModelVolumeAttachments, 387 s.sb.WatchMachineVolumeAttachments, 388 s.sb.WatchUnitVolumeAttachments, 389 storagecommon.ParseVolumeAttachmentIds, 390 ) 391 } 392 393 // WatchFilesystemAttachments watches for changes to filesystem attachments 394 // scoped to the entity with the tag passed to NewState. 395 func (s *StorageProvisionerAPIv3) WatchFilesystemAttachments(args params.Entities) (params.MachineStorageIdsWatchResults, error) { 396 w := filesystemwatcher.Watchers{s.sb} 397 return s.watchAttachments( 398 args, 399 w.WatchModelManagedFilesystemAttachments, 400 w.WatchMachineManagedFilesystemAttachments, 401 w.WatchUnitManagedFilesystemAttachments, 402 storagecommon.ParseFilesystemAttachmentIds, 403 ) 404 } 405 406 // WatchVolumeAttachmentPlans watches for changes to volume attachments for a machine for the purpose of allowing 407 // that machine to run any initialization needed, for that volume to actually appear as a block device (ie: iSCSI) 408 func (s *StorageProvisionerAPIv3) WatchVolumeAttachmentPlans(args params.Entities) (params.MachineStorageIdsWatchResults, error) { 409 canAccess, err := s.getMachineAuthFunc() 410 if err != nil { 411 return params.MachineStorageIdsWatchResults{}, common.ServerError(common.ErrPerm) 412 } 413 results := params.MachineStorageIdsWatchResults{ 414 Results: make([]params.MachineStorageIdsWatchResult, len(args.Entities)), 415 } 416 one := func(arg params.Entity) (string, []params.MachineStorageId, error) { 417 tag, err := names.ParseTag(arg.Tag) 418 if err != nil || !canAccess(tag) { 419 return "", nil, common.ErrPerm 420 } 421 var w state.StringsWatcher 422 if tag, ok := tag.(names.MachineTag); ok { 423 w = s.sb.WatchMachineAttachmentsPlans(tag) 424 } else { 425 return "", nil, common.ErrPerm 426 } 427 if stringChanges, ok := <-w.Changes(); ok { 428 changes, err := storagecommon.ParseVolumeAttachmentIds(stringChanges) 429 if err != nil { 430 w.Stop() 431 return "", nil, err 432 } 433 return s.resources.Register(w), changes, nil 434 } 435 return "", nil, watcher.EnsureErr(w) 436 } 437 for i, arg := range args.Entities { 438 var result params.MachineStorageIdsWatchResult 439 id, changes, err := one(arg) 440 if err != nil { 441 result.Error = common.ServerError(err) 442 } else { 443 result.MachineStorageIdsWatcherId = id 444 result.Changes = changes 445 } 446 results.Results[i] = result 447 } 448 return results, nil 449 } 450 451 func (s *StorageProvisionerAPIv3) RemoveVolumeAttachmentPlan(args params.MachineStorageIds) (params.ErrorResults, error) { 452 canAccess, err := s.getMachineAuthFunc() 453 if err != nil { 454 return params.ErrorResults{}, common.ServerError(common.ErrPerm) 455 } 456 results := params.ErrorResults{ 457 Results: make([]params.ErrorResult, len(args.Ids)), 458 } 459 460 one := func(arg params.MachineStorageId) error { 461 volumeAttachmentPlan, err := s.oneVolumeAttachmentPlan(arg, canAccess) 462 if err != nil { 463 if errors.IsNotFound(err) { 464 return common.ErrPerm 465 } 466 return common.ServerError(err) 467 } 468 if volumeAttachmentPlan.Life() != state.Dying { 469 return common.ErrPerm 470 } 471 return s.sb.RemoveVolumeAttachmentPlan( 472 volumeAttachmentPlan.Machine(), 473 volumeAttachmentPlan.Volume()) 474 } 475 for i, arg := range args.Ids { 476 err := one(arg) 477 results.Results[i].Error = common.ServerError(err) 478 } 479 return results, nil 480 } 481 482 func (s *StorageProvisionerAPIv3) watchAttachments( 483 args params.Entities, 484 watchEnvironAttachments func() state.StringsWatcher, 485 watchMachineAttachments func(names.MachineTag) state.StringsWatcher, 486 watchUnitAttachments func(names.ApplicationTag) state.StringsWatcher, 487 parseAttachmentIds func([]string) ([]params.MachineStorageId, error), 488 ) (params.MachineStorageIdsWatchResults, error) { 489 canAccess, err := s.getScopeAuthFunc() 490 if err != nil { 491 return params.MachineStorageIdsWatchResults{}, common.ServerError(common.ErrPerm) 492 } 493 results := params.MachineStorageIdsWatchResults{ 494 Results: make([]params.MachineStorageIdsWatchResult, len(args.Entities)), 495 } 496 one := func(arg params.Entity) (string, []params.MachineStorageId, error) { 497 tag, err := names.ParseTag(arg.Tag) 498 if err != nil || !canAccess(tag) { 499 return "", nil, common.ErrPerm 500 } 501 var w state.StringsWatcher 502 switch tag := tag.(type) { 503 case names.MachineTag: 504 w = watchMachineAttachments(tag) 505 case names.ApplicationTag: 506 w = watchUnitAttachments(tag) 507 default: 508 w = watchEnvironAttachments() 509 } 510 if stringChanges, ok := <-w.Changes(); ok { 511 changes, err := parseAttachmentIds(stringChanges) 512 if err != nil { 513 w.Stop() 514 return "", nil, err 515 } 516 return s.resources.Register(w), changes, nil 517 } 518 return "", nil, watcher.EnsureErr(w) 519 } 520 for i, arg := range args.Entities { 521 var result params.MachineStorageIdsWatchResult 522 id, changes, err := one(arg) 523 if err != nil { 524 result.Error = common.ServerError(err) 525 } else { 526 result.MachineStorageIdsWatcherId = id 527 result.Changes = changes 528 } 529 results.Results[i] = result 530 } 531 return results, nil 532 } 533 534 // Volumes returns details of volumes with the specified tags. 535 func (s *StorageProvisionerAPIv3) Volumes(args params.Entities) (params.VolumeResults, error) { 536 canAccess, err := s.getStorageEntityAuthFunc() 537 if err != nil { 538 return params.VolumeResults{}, common.ServerError(common.ErrPerm) 539 } 540 results := params.VolumeResults{ 541 Results: make([]params.VolumeResult, len(args.Entities)), 542 } 543 one := func(arg params.Entity) (params.Volume, error) { 544 tag, err := names.ParseVolumeTag(arg.Tag) 545 if err != nil || !canAccess(tag) { 546 return params.Volume{}, common.ErrPerm 547 } 548 volume, err := s.sb.Volume(tag) 549 if errors.IsNotFound(err) { 550 return params.Volume{}, common.ErrPerm 551 } else if err != nil { 552 return params.Volume{}, err 553 } 554 return storagecommon.VolumeFromState(volume) 555 } 556 for i, arg := range args.Entities { 557 var result params.VolumeResult 558 volume, err := one(arg) 559 if err != nil { 560 result.Error = common.ServerError(err) 561 } else { 562 result.Result = volume 563 } 564 results.Results[i] = result 565 } 566 return results, nil 567 } 568 569 // Filesystems returns details of filesystems with the specified tags. 570 func (s *StorageProvisionerAPIv3) Filesystems(args params.Entities) (params.FilesystemResults, error) { 571 canAccess, err := s.getStorageEntityAuthFunc() 572 if err != nil { 573 return params.FilesystemResults{}, common.ServerError(common.ErrPerm) 574 } 575 results := params.FilesystemResults{ 576 Results: make([]params.FilesystemResult, len(args.Entities)), 577 } 578 one := func(arg params.Entity) (params.Filesystem, error) { 579 tag, err := names.ParseFilesystemTag(arg.Tag) 580 if err != nil || !canAccess(tag) { 581 return params.Filesystem{}, common.ErrPerm 582 } 583 filesystem, err := s.sb.Filesystem(tag) 584 if errors.IsNotFound(err) { 585 return params.Filesystem{}, common.ErrPerm 586 } else if err != nil { 587 return params.Filesystem{}, err 588 } 589 return storagecommon.FilesystemFromState(filesystem) 590 } 591 for i, arg := range args.Entities { 592 var result params.FilesystemResult 593 filesystem, err := one(arg) 594 if err != nil { 595 result.Error = common.ServerError(err) 596 } else { 597 result.Result = filesystem 598 } 599 results.Results[i] = result 600 } 601 return results, nil 602 } 603 604 // VolumeAttachmentPlans returns details of volume attachment plans with the specified IDs. 605 func (s *StorageProvisionerAPIv3) VolumeAttachmentPlans(args params.MachineStorageIds) (params.VolumeAttachmentPlanResults, error) { 606 // NOTE(gsamfira): Containers will probably not be a concern for this at the moment 607 // revisit this if containers should be treated 608 canAccess, err := s.getMachineAuthFunc() 609 if err != nil { 610 return params.VolumeAttachmentPlanResults{}, common.ServerError(common.ErrPerm) 611 } 612 results := params.VolumeAttachmentPlanResults{ 613 Results: make([]params.VolumeAttachmentPlanResult, len(args.Ids)), 614 } 615 one := func(arg params.MachineStorageId) (params.VolumeAttachmentPlan, error) { 616 volumeAttachmentPlan, err := s.oneVolumeAttachmentPlan(arg, canAccess) 617 if err != nil { 618 return params.VolumeAttachmentPlan{}, err 619 } 620 return storagecommon.VolumeAttachmentPlanFromState(volumeAttachmentPlan) 621 } 622 for i, arg := range args.Ids { 623 var result params.VolumeAttachmentPlanResult 624 volumeAttachmentPlan, err := one(arg) 625 if err != nil { 626 result.Error = common.ServerError(err) 627 } else { 628 result.Result = volumeAttachmentPlan 629 } 630 results.Results[i] = result 631 } 632 return results, nil 633 } 634 635 // VolumeAttachments returns details of volume attachments with the specified IDs. 636 func (s *StorageProvisionerAPIv3) VolumeAttachments(args params.MachineStorageIds) (params.VolumeAttachmentResults, error) { 637 canAccess, err := s.getAttachmentAuthFunc() 638 if err != nil { 639 return params.VolumeAttachmentResults{}, common.ServerError(common.ErrPerm) 640 } 641 results := params.VolumeAttachmentResults{ 642 Results: make([]params.VolumeAttachmentResult, len(args.Ids)), 643 } 644 one := func(arg params.MachineStorageId) (params.VolumeAttachment, error) { 645 volumeAttachment, err := s.oneVolumeAttachment(arg, canAccess) 646 if err != nil { 647 return params.VolumeAttachment{}, err 648 } 649 return storagecommon.VolumeAttachmentFromState(volumeAttachment) 650 } 651 for i, arg := range args.Ids { 652 var result params.VolumeAttachmentResult 653 volumeAttachment, err := one(arg) 654 if err != nil { 655 result.Error = common.ServerError(err) 656 } else { 657 result.Result = volumeAttachment 658 } 659 results.Results[i] = result 660 } 661 return results, nil 662 } 663 664 // VolumeBlockDevices returns details of the block devices corresponding to the 665 // volume attachments with the specified IDs. 666 func (s *StorageProvisionerAPIv3) VolumeBlockDevices(args params.MachineStorageIds) (params.BlockDeviceResults, error) { 667 canAccess, err := s.getAttachmentAuthFunc() 668 if err != nil { 669 return params.BlockDeviceResults{}, common.ServerError(common.ErrPerm) 670 } 671 results := params.BlockDeviceResults{ 672 Results: make([]params.BlockDeviceResult, len(args.Ids)), 673 } 674 one := func(arg params.MachineStorageId) (storage.BlockDevice, error) { 675 stateBlockDevice, err := s.oneVolumeBlockDevice(arg, canAccess) 676 if err != nil { 677 return storage.BlockDevice{}, err 678 } 679 return storagecommon.BlockDeviceFromState(stateBlockDevice), nil 680 } 681 for i, arg := range args.Ids { 682 var result params.BlockDeviceResult 683 blockDevice, err := one(arg) 684 if err != nil { 685 result.Error = common.ServerError(err) 686 } else { 687 result.Result = blockDevice 688 } 689 results.Results[i] = result 690 } 691 return results, nil 692 } 693 694 // FilesystemAttachments returns details of filesystem attachments with the specified IDs. 695 func (s *StorageProvisionerAPIv3) FilesystemAttachments(args params.MachineStorageIds) (params.FilesystemAttachmentResults, error) { 696 canAccess, err := s.getAttachmentAuthFunc() 697 if err != nil { 698 return params.FilesystemAttachmentResults{}, common.ServerError(common.ErrPerm) 699 } 700 results := params.FilesystemAttachmentResults{ 701 Results: make([]params.FilesystemAttachmentResult, len(args.Ids)), 702 } 703 one := func(arg params.MachineStorageId) (params.FilesystemAttachment, error) { 704 filesystemAttachment, err := s.oneFilesystemAttachment(arg, canAccess) 705 if err != nil { 706 return params.FilesystemAttachment{}, err 707 } 708 return storagecommon.FilesystemAttachmentFromState(filesystemAttachment) 709 } 710 for i, arg := range args.Ids { 711 var result params.FilesystemAttachmentResult 712 filesystemAttachment, err := one(arg) 713 if err != nil { 714 result.Error = common.ServerError(err) 715 } else { 716 result.Result = filesystemAttachment 717 } 718 results.Results[i] = result 719 } 720 return results, nil 721 } 722 723 // VolumeParams returns the parameters for creating or destroying 724 // the volumes with the specified tags. 725 func (s *StorageProvisionerAPIv3) VolumeParams(args params.Entities) (params.VolumeParamsResults, error) { 726 canAccess, err := s.getStorageEntityAuthFunc() 727 if err != nil { 728 return params.VolumeParamsResults{}, err 729 } 730 modelCfg, err := s.st.ModelConfig() 731 if err != nil { 732 return params.VolumeParamsResults{}, err 733 } 734 controllerCfg, err := s.st.ControllerConfig() 735 if err != nil { 736 return params.VolumeParamsResults{}, err 737 } 738 results := params.VolumeParamsResults{ 739 Results: make([]params.VolumeParamsResult, len(args.Entities)), 740 } 741 one := func(arg params.Entity) (params.VolumeParams, error) { 742 tag, err := names.ParseVolumeTag(arg.Tag) 743 if err != nil || !canAccess(tag) { 744 return params.VolumeParams{}, common.ErrPerm 745 } 746 volume, err := s.sb.Volume(tag) 747 if errors.IsNotFound(err) { 748 return params.VolumeParams{}, common.ErrPerm 749 } else if err != nil { 750 return params.VolumeParams{}, err 751 } 752 volumeAttachments, err := s.sb.VolumeAttachments(tag) 753 if err != nil { 754 return params.VolumeParams{}, err 755 } 756 storageInstance, err := storagecommon.MaybeAssignedStorageInstance( 757 volume.StorageInstance, 758 s.sb.StorageInstance, 759 ) 760 if err != nil { 761 return params.VolumeParams{}, err 762 } 763 volumeParams, err := storagecommon.VolumeParams( 764 volume, storageInstance, modelCfg.UUID(), controllerCfg.ControllerUUID(), 765 modelCfg, s.poolManager, s.registry, 766 ) 767 if err != nil { 768 return params.VolumeParams{}, err 769 } 770 if len(volumeAttachments) == 1 { 771 // There is exactly one attachment to be made, so make 772 // it immediately. Otherwise we will defer attachments 773 // until later. 774 volumeAttachment := volumeAttachments[0] 775 volumeAttachmentParams, ok := volumeAttachment.Params() 776 if !ok { 777 return params.VolumeParams{}, errors.Errorf( 778 "volume %q is already attached to %q", 779 volumeAttachment.Volume().Id(), 780 names.ReadableString(volumeAttachment.Host()), 781 ) 782 } 783 // Volumes can be attached to units (caas models) or machines. 784 // We only care about instance id for machine attachments. 785 var instanceId instance.Id 786 if machineTag, ok := volumeAttachment.Host().(names.MachineTag); ok { 787 instanceId, err = s.st.MachineInstanceId(machineTag) 788 if errors.IsNotProvisioned(err) { 789 // Leave the attachment until later. 790 instanceId = "" 791 } else if err != nil { 792 return params.VolumeParams{}, err 793 } 794 } 795 volumeParams.Attachment = ¶ms.VolumeAttachmentParams{ 796 VolumeTag: tag.String(), 797 MachineTag: volumeAttachment.Host().String(), 798 VolumeId: "", 799 InstanceId: string(instanceId), 800 Provider: volumeParams.Provider, 801 ReadOnly: volumeAttachmentParams.ReadOnly, 802 } 803 } 804 return volumeParams, nil 805 } 806 for i, arg := range args.Entities { 807 var result params.VolumeParamsResult 808 volumeParams, err := one(arg) 809 if err != nil { 810 result.Error = common.ServerError(err) 811 } else { 812 result.Result = volumeParams 813 } 814 results.Results[i] = result 815 } 816 return results, nil 817 } 818 819 // RemoveVolumeParams returns the parameters for destroying 820 // or releasing the volumes with the specified tags. 821 func (s *StorageProvisionerAPIv4) RemoveVolumeParams(args params.Entities) (params.RemoveVolumeParamsResults, error) { 822 canAccess, err := s.getStorageEntityAuthFunc() 823 if err != nil { 824 return params.RemoveVolumeParamsResults{}, err 825 } 826 results := params.RemoveVolumeParamsResults{ 827 Results: make([]params.RemoveVolumeParamsResult, len(args.Entities)), 828 } 829 one := func(arg params.Entity) (params.RemoveVolumeParams, error) { 830 tag, err := names.ParseVolumeTag(arg.Tag) 831 if err != nil || !canAccess(tag) { 832 return params.RemoveVolumeParams{}, common.ErrPerm 833 } 834 volume, err := s.sb.Volume(tag) 835 if errors.IsNotFound(err) { 836 return params.RemoveVolumeParams{}, common.ErrPerm 837 } else if err != nil { 838 return params.RemoveVolumeParams{}, err 839 } 840 if life := volume.Life(); life != state.Dead { 841 return params.RemoveVolumeParams{}, errors.Errorf( 842 "%s is not dead (%s)", 843 names.ReadableString(tag), life, 844 ) 845 } 846 volumeInfo, err := volume.Info() 847 if err != nil { 848 return params.RemoveVolumeParams{}, err 849 } 850 provider, _, err := storagecommon.StoragePoolConfig( 851 volumeInfo.Pool, s.poolManager, s.registry, 852 ) 853 if err != nil { 854 return params.RemoveVolumeParams{}, err 855 } 856 return params.RemoveVolumeParams{ 857 Provider: string(provider), 858 VolumeId: volumeInfo.VolumeId, 859 Destroy: !volume.Releasing(), 860 }, nil 861 } 862 for i, arg := range args.Entities { 863 var result params.RemoveVolumeParamsResult 864 volumeParams, err := one(arg) 865 if err != nil { 866 result.Error = common.ServerError(err) 867 } else { 868 result.Result = volumeParams 869 } 870 results.Results[i] = result 871 } 872 return results, nil 873 } 874 875 // FilesystemParams returns the parameters for creating the filesystems 876 // with the specified tags. 877 func (s *StorageProvisionerAPIv3) FilesystemParams(args params.Entities) (params.FilesystemParamsResults, error) { 878 canAccess, err := s.getStorageEntityAuthFunc() 879 if err != nil { 880 return params.FilesystemParamsResults{}, err 881 } 882 modelConfig, err := s.st.ModelConfig() 883 if err != nil { 884 return params.FilesystemParamsResults{}, err 885 } 886 controllerCfg, err := s.st.ControllerConfig() 887 if err != nil { 888 return params.FilesystemParamsResults{}, err 889 } 890 results := params.FilesystemParamsResults{ 891 Results: make([]params.FilesystemParamsResult, len(args.Entities)), 892 } 893 one := func(arg params.Entity) (params.FilesystemParams, error) { 894 tag, err := names.ParseFilesystemTag(arg.Tag) 895 if err != nil || !canAccess(tag) { 896 return params.FilesystemParams{}, common.ErrPerm 897 } 898 filesystem, err := s.sb.Filesystem(tag) 899 if errors.IsNotFound(err) { 900 return params.FilesystemParams{}, common.ErrPerm 901 } else if err != nil { 902 return params.FilesystemParams{}, err 903 } 904 storageInstance, err := storagecommon.MaybeAssignedStorageInstance( 905 filesystem.Storage, 906 s.sb.StorageInstance, 907 ) 908 if err != nil { 909 return params.FilesystemParams{}, err 910 } 911 filesystemParams, err := storagecommon.FilesystemParams( 912 filesystem, storageInstance, modelConfig.UUID(), controllerCfg.ControllerUUID(), 913 modelConfig, s.poolManager, s.registry, 914 ) 915 if err != nil { 916 return params.FilesystemParams{}, err 917 } 918 return filesystemParams, nil 919 } 920 for i, arg := range args.Entities { 921 var result params.FilesystemParamsResult 922 filesystemParams, err := one(arg) 923 if err != nil { 924 result.Error = common.ServerError(err) 925 } else { 926 result.Result = filesystemParams 927 } 928 results.Results[i] = result 929 } 930 return results, nil 931 } 932 933 // RemoveFilesystemParams returns the parameters for destroying or 934 // releasing the filesystems with the specified tags. 935 func (s *StorageProvisionerAPIv4) RemoveFilesystemParams(args params.Entities) (params.RemoveFilesystemParamsResults, error) { 936 canAccess, err := s.getStorageEntityAuthFunc() 937 if err != nil { 938 return params.RemoveFilesystemParamsResults{}, err 939 } 940 results := params.RemoveFilesystemParamsResults{ 941 Results: make([]params.RemoveFilesystemParamsResult, len(args.Entities)), 942 } 943 one := func(arg params.Entity) (params.RemoveFilesystemParams, error) { 944 tag, err := names.ParseFilesystemTag(arg.Tag) 945 if err != nil || !canAccess(tag) { 946 return params.RemoveFilesystemParams{}, common.ErrPerm 947 } 948 filesystem, err := s.sb.Filesystem(tag) 949 if errors.IsNotFound(err) { 950 return params.RemoveFilesystemParams{}, common.ErrPerm 951 } else if err != nil { 952 return params.RemoveFilesystemParams{}, err 953 } 954 if life := filesystem.Life(); life != state.Dead { 955 return params.RemoveFilesystemParams{}, errors.Errorf( 956 "%s is not dead (%s)", 957 names.ReadableString(tag), life, 958 ) 959 } 960 filesystemInfo, err := filesystem.Info() 961 if err != nil { 962 return params.RemoveFilesystemParams{}, err 963 } 964 provider, _, err := storagecommon.StoragePoolConfig( 965 filesystemInfo.Pool, s.poolManager, s.registry, 966 ) 967 if err != nil { 968 return params.RemoveFilesystemParams{}, err 969 } 970 return params.RemoveFilesystemParams{ 971 Provider: string(provider), 972 FilesystemId: filesystemInfo.FilesystemId, 973 Destroy: !filesystem.Releasing(), 974 }, nil 975 } 976 for i, arg := range args.Entities { 977 var result params.RemoveFilesystemParamsResult 978 filesystemParams, err := one(arg) 979 if err != nil { 980 result.Error = common.ServerError(err) 981 } else { 982 result.Result = filesystemParams 983 } 984 results.Results[i] = result 985 } 986 return results, nil 987 } 988 989 // VolumeAttachmentParams returns the parameters for creating the volume 990 // attachments with the specified IDs. 991 func (s *StorageProvisionerAPIv3) VolumeAttachmentParams( 992 args params.MachineStorageIds, 993 ) (params.VolumeAttachmentParamsResults, error) { 994 canAccess, err := s.getAttachmentAuthFunc() 995 if err != nil { 996 return params.VolumeAttachmentParamsResults{}, common.ServerError(common.ErrPerm) 997 } 998 results := params.VolumeAttachmentParamsResults{ 999 Results: make([]params.VolumeAttachmentParamsResult, len(args.Ids)), 1000 } 1001 one := func(arg params.MachineStorageId) (params.VolumeAttachmentParams, error) { 1002 volumeAttachment, err := s.oneVolumeAttachment(arg, canAccess) 1003 if err != nil { 1004 return params.VolumeAttachmentParams{}, err 1005 } 1006 // Volumes can be attached to units (caas models) or machines. 1007 // We only care about instance id for machine attachments. 1008 var instanceId instance.Id 1009 if machineTag, ok := volumeAttachment.Host().(names.MachineTag); ok { 1010 instanceId, err = s.st.MachineInstanceId(machineTag) 1011 if errors.IsNotProvisioned(err) { 1012 // The worker must watch for machine provisioning events. 1013 instanceId = "" 1014 } else if err != nil { 1015 return params.VolumeAttachmentParams{}, err 1016 } 1017 } 1018 volume, err := s.sb.Volume(volumeAttachment.Volume()) 1019 if err != nil { 1020 return params.VolumeAttachmentParams{}, err 1021 } 1022 var volumeId string 1023 var pool string 1024 if volumeParams, ok := volume.Params(); ok { 1025 pool = volumeParams.Pool 1026 } else { 1027 volumeInfo, err := volume.Info() 1028 if err != nil { 1029 return params.VolumeAttachmentParams{}, err 1030 } 1031 volumeId = volumeInfo.VolumeId 1032 pool = volumeInfo.Pool 1033 } 1034 providerType, _, err := storagecommon.StoragePoolConfig(pool, s.poolManager, s.registry) 1035 if err != nil { 1036 return params.VolumeAttachmentParams{}, errors.Trace(err) 1037 } 1038 var readOnly bool 1039 if volumeAttachmentParams, ok := volumeAttachment.Params(); ok { 1040 readOnly = volumeAttachmentParams.ReadOnly 1041 } else { 1042 // Attachment parameters may be requested even if the 1043 // attachment exists; i.e. for reattachment. 1044 volumeAttachmentInfo, err := volumeAttachment.Info() 1045 if err != nil { 1046 return params.VolumeAttachmentParams{}, errors.Trace(err) 1047 } 1048 readOnly = volumeAttachmentInfo.ReadOnly 1049 } 1050 return params.VolumeAttachmentParams{ 1051 VolumeTag: volumeAttachment.Volume().String(), 1052 MachineTag: volumeAttachment.Host().String(), 1053 VolumeId: volumeId, 1054 InstanceId: string(instanceId), 1055 Provider: string(providerType), 1056 ReadOnly: readOnly, 1057 }, nil 1058 } 1059 for i, arg := range args.Ids { 1060 var result params.VolumeAttachmentParamsResult 1061 volumeAttachment, err := one(arg) 1062 if err != nil { 1063 result.Error = common.ServerError(err) 1064 } else { 1065 result.Result = volumeAttachment 1066 } 1067 results.Results[i] = result 1068 } 1069 return results, nil 1070 } 1071 1072 // FilesystemAttachmentParams returns the parameters for creating the filesystem 1073 // attachments with the specified IDs. 1074 func (s *StorageProvisionerAPIv3) FilesystemAttachmentParams( 1075 args params.MachineStorageIds, 1076 ) (params.FilesystemAttachmentParamsResults, error) { 1077 canAccess, err := s.getAttachmentAuthFunc() 1078 if err != nil { 1079 return params.FilesystemAttachmentParamsResults{}, common.ServerError(common.ErrPerm) 1080 } 1081 results := params.FilesystemAttachmentParamsResults{ 1082 Results: make([]params.FilesystemAttachmentParamsResult, len(args.Ids)), 1083 } 1084 one := func(arg params.MachineStorageId) (params.FilesystemAttachmentParams, error) { 1085 filesystemAttachment, err := s.oneFilesystemAttachment(arg, canAccess) 1086 if err != nil { 1087 return params.FilesystemAttachmentParams{}, errors.Trace(err) 1088 } 1089 hostTag := filesystemAttachment.Host() 1090 // Filesystems can be attached to units (caas models) or machines. 1091 // We only care about instance id for machine attachments. 1092 var instanceId instance.Id 1093 if machineTag, ok := filesystemAttachment.Host().(names.MachineTag); ok { 1094 instanceId, err = s.st.MachineInstanceId(machineTag) 1095 if errors.IsNotProvisioned(err) { 1096 // The worker must watch for machine provisioning events. 1097 instanceId = "" 1098 } else if err != nil { 1099 return params.FilesystemAttachmentParams{}, errors.Trace(err) 1100 } 1101 } 1102 filesystem, err := s.sb.Filesystem(filesystemAttachment.Filesystem()) 1103 if err != nil { 1104 return params.FilesystemAttachmentParams{}, errors.Trace(err) 1105 } 1106 var filesystemId string 1107 var pool string 1108 if filesystemParams, ok := filesystem.Params(); ok { 1109 pool = filesystemParams.Pool 1110 } else { 1111 filesystemInfo, err := filesystem.Info() 1112 if err != nil { 1113 return params.FilesystemAttachmentParams{}, errors.Trace(err) 1114 } 1115 filesystemId = filesystemInfo.FilesystemId 1116 pool = filesystemInfo.Pool 1117 } 1118 providerType, _, err := storagecommon.StoragePoolConfig(pool, s.poolManager, s.registry) 1119 if err != nil { 1120 return params.FilesystemAttachmentParams{}, errors.Trace(err) 1121 } 1122 var location string 1123 var readOnly bool 1124 if filesystemAttachmentParams, ok := filesystemAttachment.Params(); ok { 1125 location = filesystemAttachmentParams.Location 1126 readOnly = filesystemAttachmentParams.ReadOnly 1127 } else { 1128 // Attachment parameters may be requested even if the 1129 // attachment exists; i.e. for reattachment. 1130 filesystemAttachmentInfo, err := filesystemAttachment.Info() 1131 if err != nil { 1132 return params.FilesystemAttachmentParams{}, errors.Trace(err) 1133 } 1134 location = filesystemAttachmentInfo.MountPoint 1135 readOnly = filesystemAttachmentInfo.ReadOnly 1136 } 1137 return params.FilesystemAttachmentParams{ 1138 FilesystemTag: filesystemAttachment.Filesystem().String(), 1139 MachineTag: hostTag.String(), 1140 FilesystemId: filesystemId, 1141 InstanceId: string(instanceId), 1142 Provider: string(providerType), 1143 // TODO(axw) dealias MountPoint. We now have 1144 // Path, MountPoint and Location in different 1145 // parts of the codebase. 1146 MountPoint: location, 1147 ReadOnly: readOnly, 1148 }, nil 1149 } 1150 for i, arg := range args.Ids { 1151 var result params.FilesystemAttachmentParamsResult 1152 filesystemAttachment, err := one(arg) 1153 if err != nil { 1154 result.Error = common.ServerError(err) 1155 } else { 1156 result.Result = filesystemAttachment 1157 } 1158 results.Results[i] = result 1159 } 1160 return results, nil 1161 } 1162 1163 func (s *StorageProvisionerAPIv3) oneVolumeAttachmentPlan( 1164 id params.MachineStorageId, canAccess common.AuthFunc, 1165 ) (state.VolumeAttachmentPlan, error) { 1166 machineTag, err := names.ParseMachineTag(id.MachineTag) 1167 if err != nil { 1168 return nil, err 1169 } 1170 volumeTag, err := names.ParseVolumeTag(id.AttachmentTag) 1171 if err != nil { 1172 return nil, err 1173 } 1174 if !canAccess(machineTag) { 1175 return nil, common.ErrPerm 1176 } 1177 volumeAttachmentPlan, err := s.sb.VolumeAttachmentPlan(machineTag, volumeTag) 1178 if err != nil { 1179 return nil, err 1180 } 1181 return volumeAttachmentPlan, nil 1182 } 1183 1184 func (s *StorageProvisionerAPIv3) oneVolumeAttachment( 1185 id params.MachineStorageId, canAccess func(names.Tag, names.Tag) bool, 1186 ) (state.VolumeAttachment, error) { 1187 hostTag, err := names.ParseTag(id.MachineTag) 1188 if err != nil { 1189 return nil, err 1190 } 1191 if hostTag.Kind() != names.MachineTagKind && hostTag.Kind() != names.UnitTagKind { 1192 return nil, errors.NotValidf("volume attachment host tag %q", hostTag) 1193 } 1194 volumeTag, err := names.ParseVolumeTag(id.AttachmentTag) 1195 if err != nil { 1196 return nil, err 1197 } 1198 if !canAccess(hostTag, volumeTag) { 1199 return nil, common.ErrPerm 1200 } 1201 volumeAttachment, err := s.sb.VolumeAttachment(hostTag, volumeTag) 1202 if errors.IsNotFound(err) { 1203 return nil, common.ErrPerm 1204 } else if err != nil { 1205 return nil, err 1206 } 1207 return volumeAttachment, nil 1208 } 1209 1210 func (s *StorageProvisionerAPIv3) oneVolumeBlockDevice( 1211 id params.MachineStorageId, canAccess func(names.Tag, names.Tag) bool, 1212 ) (state.BlockDeviceInfo, error) { 1213 volumeAttachment, err := s.oneVolumeAttachment(id, canAccess) 1214 if err != nil { 1215 return state.BlockDeviceInfo{}, err 1216 } 1217 volume, err := s.sb.Volume(volumeAttachment.Volume()) 1218 if err != nil { 1219 return state.BlockDeviceInfo{}, err 1220 } 1221 volumeInfo, err := volume.Info() 1222 if err != nil { 1223 return state.BlockDeviceInfo{}, err 1224 } 1225 volumeAttachmentInfo, err := volumeAttachment.Info() 1226 if err != nil { 1227 return state.BlockDeviceInfo{}, err 1228 } 1229 planCanAccess, err := s.getMachineAuthFunc() 1230 if err != nil && !errors.IsNotFound(err) { 1231 return state.BlockDeviceInfo{}, err 1232 } 1233 var blockDeviceInfo state.BlockDeviceInfo 1234 1235 volumeAttachmentPlan, err := s.oneVolumeAttachmentPlan(id, planCanAccess) 1236 1237 if err != nil { 1238 // Volume attachment plans are optional. We should not err out 1239 // if one is missing, and simply return an empty state.BlockDeviceInfo{} 1240 if !errors.IsNotFound(err) { 1241 return state.BlockDeviceInfo{}, err 1242 } 1243 blockDeviceInfo = state.BlockDeviceInfo{} 1244 } else { 1245 blockDeviceInfo, err = volumeAttachmentPlan.BlockDeviceInfo() 1246 1247 if err != nil { 1248 // Volume attachment plans are optional. We should not err out 1249 // if one is missing, and simply return an empty state.BlockDeviceInfo{} 1250 if !errors.IsNotFound(err) { 1251 return state.BlockDeviceInfo{}, err 1252 } 1253 blockDeviceInfo = state.BlockDeviceInfo{} 1254 } 1255 } 1256 blockDevices, err := s.sb.BlockDevices(volumeAttachment.Host().(names.MachineTag)) 1257 if err != nil { 1258 return state.BlockDeviceInfo{}, err 1259 } 1260 blockDevice, ok := storagecommon.MatchingBlockDevice( 1261 blockDevices, 1262 volumeInfo, 1263 volumeAttachmentInfo, 1264 blockDeviceInfo, 1265 ) 1266 if !ok { 1267 return state.BlockDeviceInfo{}, errors.NotFoundf( 1268 "block device for volume %v on %v", 1269 volumeAttachment.Volume().Id(), 1270 names.ReadableString(volumeAttachment.Host()), 1271 ) 1272 } 1273 return *blockDevice, nil 1274 } 1275 1276 func (s *StorageProvisionerAPIv3) oneFilesystemAttachment( 1277 id params.MachineStorageId, canAccess func(names.Tag, names.Tag) bool, 1278 ) (state.FilesystemAttachment, error) { 1279 hostTag, err := names.ParseTag(id.MachineTag) 1280 if err != nil { 1281 return nil, err 1282 } 1283 if hostTag.Kind() != names.MachineTagKind && hostTag.Kind() != names.UnitTagKind { 1284 return nil, errors.NotValidf("filesystem attachment host tag %q", hostTag) 1285 } 1286 filesystemTag, err := names.ParseFilesystemTag(id.AttachmentTag) 1287 if err != nil { 1288 return nil, err 1289 } 1290 if !canAccess(hostTag, filesystemTag) { 1291 return nil, common.ErrPerm 1292 } 1293 filesystemAttachment, err := s.sb.FilesystemAttachment(hostTag, filesystemTag) 1294 if errors.IsNotFound(err) { 1295 return nil, common.ErrPerm 1296 } else if err != nil { 1297 return nil, err 1298 } 1299 return filesystemAttachment, nil 1300 } 1301 1302 // SetVolumeInfo records the details of newly provisioned volumes. 1303 func (s *StorageProvisionerAPIv3) SetVolumeInfo(args params.Volumes) (params.ErrorResults, error) { 1304 canAccessVolume, err := s.getStorageEntityAuthFunc() 1305 if err != nil { 1306 return params.ErrorResults{}, err 1307 } 1308 results := params.ErrorResults{ 1309 Results: make([]params.ErrorResult, len(args.Volumes)), 1310 } 1311 one := func(arg params.Volume) error { 1312 volumeTag, volumeInfo, err := storagecommon.VolumeToState(arg) 1313 if err != nil { 1314 return errors.Trace(err) 1315 } else if !canAccessVolume(volumeTag) { 1316 return common.ErrPerm 1317 } 1318 err = s.sb.SetVolumeInfo(volumeTag, volumeInfo) 1319 if errors.IsNotFound(err) { 1320 return common.ErrPerm 1321 } 1322 return errors.Trace(err) 1323 } 1324 for i, arg := range args.Volumes { 1325 err := one(arg) 1326 results.Results[i].Error = common.ServerError(err) 1327 } 1328 return results, nil 1329 } 1330 1331 // SetFilesystemInfo records the details of newly provisioned filesystems. 1332 func (s *StorageProvisionerAPIv3) SetFilesystemInfo(args params.Filesystems) (params.ErrorResults, error) { 1333 canAccessFilesystem, err := s.getStorageEntityAuthFunc() 1334 if err != nil { 1335 return params.ErrorResults{}, err 1336 } 1337 results := params.ErrorResults{ 1338 Results: make([]params.ErrorResult, len(args.Filesystems)), 1339 } 1340 one := func(arg params.Filesystem) error { 1341 filesystemTag, filesystemInfo, err := storagecommon.FilesystemToState(arg) 1342 if err != nil { 1343 return errors.Trace(err) 1344 } else if !canAccessFilesystem(filesystemTag) { 1345 return common.ErrPerm 1346 } 1347 err = s.sb.SetFilesystemInfo(filesystemTag, filesystemInfo) 1348 if errors.IsNotFound(err) { 1349 return common.ErrPerm 1350 } 1351 return errors.Trace(err) 1352 } 1353 for i, arg := range args.Filesystems { 1354 err := one(arg) 1355 results.Results[i].Error = common.ServerError(err) 1356 } 1357 return results, nil 1358 } 1359 1360 func (s *StorageProvisionerAPIv3) CreateVolumeAttachmentPlans(args params.VolumeAttachmentPlans) (params.ErrorResults, error) { 1361 canAccess, err := s.getAttachmentAuthFunc() 1362 if err != nil { 1363 return params.ErrorResults{}, common.ServerError(common.ErrPerm) 1364 } 1365 results := params.ErrorResults{ 1366 Results: make([]params.ErrorResult, len(args.VolumeAttachmentPlans)), 1367 } 1368 one := func(arg params.VolumeAttachmentPlan) error { 1369 machineTag, volumeTag, planInfo, _, err := storagecommon.VolumeAttachmentPlanToState(arg) 1370 if err != nil { 1371 return errors.Trace(err) 1372 } 1373 if !canAccess(machineTag, volumeTag) { 1374 return common.ErrPerm 1375 } 1376 err = s.sb.CreateVolumeAttachmentPlan(machineTag, volumeTag, planInfo) 1377 if err != nil { 1378 return errors.Trace(err) 1379 } 1380 return nil 1381 } 1382 for i, plan := range args.VolumeAttachmentPlans { 1383 err := one(plan) 1384 results.Results[i].Error = common.ServerError(err) 1385 } 1386 return results, nil 1387 } 1388 1389 func (s *StorageProvisionerAPIv3) SetVolumeAttachmentPlanBlockInfo(args params.VolumeAttachmentPlans) (params.ErrorResults, error) { 1390 canAccess, err := s.getAttachmentAuthFunc() 1391 if err != nil { 1392 return params.ErrorResults{}, common.ServerError(common.ErrPerm) 1393 } 1394 results := params.ErrorResults{ 1395 Results: make([]params.ErrorResult, len(args.VolumeAttachmentPlans)), 1396 } 1397 one := func(arg params.VolumeAttachmentPlan) error { 1398 machineTag, volumeTag, _, blockInfo, err := storagecommon.VolumeAttachmentPlanToState(arg) 1399 if err != nil { 1400 return errors.Trace(err) 1401 } 1402 if !canAccess(machineTag, volumeTag) { 1403 return common.ErrPerm 1404 } 1405 err = s.sb.SetVolumeAttachmentPlanBlockInfo(machineTag, volumeTag, blockInfo) 1406 if err != nil { 1407 return errors.Trace(err) 1408 } 1409 return nil 1410 } 1411 for i, plan := range args.VolumeAttachmentPlans { 1412 err := one(plan) 1413 results.Results[i].Error = common.ServerError(err) 1414 } 1415 return results, nil 1416 } 1417 1418 // SetVolumeAttachmentInfo records the details of newly provisioned volume 1419 // attachments. 1420 func (s *StorageProvisionerAPIv3) SetVolumeAttachmentInfo( 1421 args params.VolumeAttachments, 1422 ) (params.ErrorResults, error) { 1423 canAccess, err := s.getAttachmentAuthFunc() 1424 if err != nil { 1425 return params.ErrorResults{}, err 1426 } 1427 results := params.ErrorResults{ 1428 Results: make([]params.ErrorResult, len(args.VolumeAttachments)), 1429 } 1430 one := func(arg params.VolumeAttachment) error { 1431 machineTag, volumeTag, volumeAttachmentInfo, err := storagecommon.VolumeAttachmentToState(arg) 1432 if err != nil { 1433 return errors.Trace(err) 1434 } 1435 if !canAccess(machineTag, volumeTag) { 1436 return common.ErrPerm 1437 } 1438 err = s.sb.SetVolumeAttachmentInfo(machineTag, volumeTag, volumeAttachmentInfo) 1439 if errors.IsNotFound(err) { 1440 return common.ErrPerm 1441 } 1442 return errors.Trace(err) 1443 } 1444 for i, arg := range args.VolumeAttachments { 1445 err := one(arg) 1446 results.Results[i].Error = common.ServerError(err) 1447 } 1448 return results, nil 1449 } 1450 1451 // SetFilesystemAttachmentInfo records the details of newly provisioned filesystem 1452 // attachments. 1453 func (s *StorageProvisionerAPIv3) SetFilesystemAttachmentInfo( 1454 args params.FilesystemAttachments, 1455 ) (params.ErrorResults, error) { 1456 canAccess, err := s.getAttachmentAuthFunc() 1457 if err != nil { 1458 return params.ErrorResults{}, err 1459 } 1460 results := params.ErrorResults{ 1461 Results: make([]params.ErrorResult, len(args.FilesystemAttachments)), 1462 } 1463 one := func(arg params.FilesystemAttachment) error { 1464 machineTag, filesystemTag, filesystemAttachmentInfo, err := storagecommon.FilesystemAttachmentToState(arg) 1465 if err != nil { 1466 return errors.Trace(err) 1467 } 1468 if !canAccess(machineTag, filesystemTag) { 1469 return common.ErrPerm 1470 } 1471 err = s.sb.SetFilesystemAttachmentInfo(machineTag, filesystemTag, filesystemAttachmentInfo) 1472 if errors.IsNotFound(err) { 1473 return common.ErrPerm 1474 } 1475 return errors.Trace(err) 1476 } 1477 for i, arg := range args.FilesystemAttachments { 1478 err := one(arg) 1479 results.Results[i].Error = common.ServerError(err) 1480 } 1481 return results, nil 1482 } 1483 1484 // AttachmentLife returns the lifecycle state of each specified machine 1485 // storage attachment. 1486 func (s *StorageProvisionerAPIv3) AttachmentLife(args params.MachineStorageIds) (params.LifeResults, error) { 1487 canAccess, err := s.getAttachmentAuthFunc() 1488 if err != nil { 1489 return params.LifeResults{}, err 1490 } 1491 results := params.LifeResults{ 1492 Results: make([]params.LifeResult, len(args.Ids)), 1493 } 1494 one := func(arg params.MachineStorageId) (params.Life, error) { 1495 hostTag, err := names.ParseTag(arg.MachineTag) 1496 if err != nil { 1497 return "", err 1498 } 1499 if hostTag.Kind() != names.MachineTagKind && hostTag.Kind() != names.UnitTagKind { 1500 return "", errors.NotValidf("attachment host tag %q", hostTag) 1501 } 1502 attachmentTag, err := names.ParseTag(arg.AttachmentTag) 1503 if err != nil { 1504 return "", err 1505 } 1506 if !canAccess(hostTag, attachmentTag) { 1507 return "", common.ErrPerm 1508 } 1509 var lifer state.Lifer 1510 switch attachmentTag := attachmentTag.(type) { 1511 case names.VolumeTag: 1512 lifer, err = s.sb.VolumeAttachment(hostTag, attachmentTag) 1513 case names.FilesystemTag: 1514 lifer, err = s.sb.FilesystemAttachment(hostTag, attachmentTag) 1515 } 1516 if err != nil { 1517 return "", errors.Trace(err) 1518 } 1519 return params.Life(lifer.Life().String()), nil 1520 } 1521 for i, arg := range args.Ids { 1522 life, err := one(arg) 1523 if err != nil { 1524 results.Results[i].Error = common.ServerError(err) 1525 } else { 1526 results.Results[i].Life = life 1527 } 1528 } 1529 return results, nil 1530 } 1531 1532 // Remove removes volumes and filesystems from state. 1533 func (s *StorageProvisionerAPIv3) Remove(args params.Entities) (params.ErrorResults, error) { 1534 canAccess, err := s.getStorageEntityAuthFunc() 1535 if err != nil { 1536 return params.ErrorResults{}, err 1537 } 1538 results := params.ErrorResults{ 1539 Results: make([]params.ErrorResult, len(args.Entities)), 1540 } 1541 one := func(arg params.Entity) error { 1542 tag, err := names.ParseTag(arg.Tag) 1543 if err != nil { 1544 return errors.Trace(err) 1545 } 1546 if !canAccess(tag) { 1547 return common.ErrPerm 1548 } 1549 switch tag := tag.(type) { 1550 case names.FilesystemTag: 1551 return s.sb.RemoveFilesystem(tag) 1552 case names.VolumeTag: 1553 return s.sb.RemoveVolume(tag) 1554 default: 1555 // should have been picked up by canAccess 1556 logger.Debugf("unexpected %v tag", tag.Kind()) 1557 return common.ErrPerm 1558 } 1559 } 1560 for i, arg := range args.Entities { 1561 err := one(arg) 1562 results.Results[i].Error = common.ServerError(err) 1563 } 1564 return results, nil 1565 } 1566 1567 // RemoveAttachments removes the specified machine storage attachments 1568 // from state. 1569 func (s *StorageProvisionerAPIv3) RemoveAttachment(args params.MachineStorageIds) (params.ErrorResults, error) { 1570 canAccess, err := s.getAttachmentAuthFunc() 1571 if err != nil { 1572 return params.ErrorResults{}, err 1573 } 1574 results := params.ErrorResults{ 1575 Results: make([]params.ErrorResult, len(args.Ids)), 1576 } 1577 removeAttachment := func(arg params.MachineStorageId) error { 1578 hostTag, err := names.ParseTag(arg.MachineTag) 1579 if err != nil { 1580 return err 1581 } 1582 if hostTag.Kind() != names.MachineTagKind && hostTag.Kind() != names.UnitTagKind { 1583 return errors.NotValidf("attachment host tag %q", hostTag) 1584 } 1585 attachmentTag, err := names.ParseTag(arg.AttachmentTag) 1586 if err != nil { 1587 return err 1588 } 1589 if !canAccess(hostTag, attachmentTag) { 1590 return common.ErrPerm 1591 } 1592 switch attachmentTag := attachmentTag.(type) { 1593 case names.VolumeTag: 1594 return s.sb.RemoveVolumeAttachment(hostTag, attachmentTag) 1595 case names.FilesystemTag: 1596 return s.sb.RemoveFilesystemAttachment(hostTag, attachmentTag) 1597 default: 1598 return common.ErrPerm 1599 } 1600 } 1601 for i, arg := range args.Ids { 1602 if err := removeAttachment(arg); err != nil { 1603 results.Results[i].Error = common.ServerError(err) 1604 } 1605 } 1606 return results, nil 1607 }