github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/apiserver/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 "github.com/juju/names" 10 11 "github.com/juju/juju/apiserver/common" 12 "github.com/juju/juju/apiserver/params" 13 "github.com/juju/juju/state" 14 "github.com/juju/juju/state/watcher" 15 "github.com/juju/juju/storage" 16 "github.com/juju/juju/storage/poolmanager" 17 ) 18 19 var logger = loggo.GetLogger("juju.apiserver.storageprovisioner") 20 21 func init() { 22 common.RegisterStandardFacade("StorageProvisioner", 1, NewStorageProvisionerAPI) 23 } 24 25 // StorageProvisionerAPI provides access to the Provisioner API facade. 26 type StorageProvisionerAPI struct { 27 *common.LifeGetter 28 *common.DeadEnsurer 29 *common.EnvironWatcher 30 *common.InstanceIdGetter 31 *common.StatusSetter 32 33 st provisionerState 34 settings poolmanager.SettingsManager 35 resources *common.Resources 36 authorizer common.Authorizer 37 getScopeAuthFunc common.GetAuthFunc 38 getStorageEntityAuthFunc common.GetAuthFunc 39 getMachineAuthFunc common.GetAuthFunc 40 getBlockDevicesAuthFunc common.GetAuthFunc 41 getAttachmentAuthFunc func() (func(names.MachineTag, names.Tag) bool, error) 42 } 43 44 var getState = func(st *state.State) provisionerState { 45 return stateShim{st} 46 } 47 48 var getSettingsManager = func(st *state.State) poolmanager.SettingsManager { 49 return state.NewStateSettings(st) 50 } 51 52 // NewStorageProvisionerAPI creates a new server-side StorageProvisionerAPI facade. 53 func NewStorageProvisionerAPI(st *state.State, resources *common.Resources, authorizer common.Authorizer) (*StorageProvisionerAPI, error) { 54 if !authorizer.AuthMachineAgent() { 55 return nil, common.ErrPerm 56 } 57 canAccessStorageMachine := func(tag names.MachineTag, allowEnvironManager bool) bool { 58 authEntityTag := authorizer.GetAuthTag() 59 if tag == authEntityTag { 60 // Machine agents can access volumes 61 // scoped to their own machine. 62 return true 63 } 64 parentId := state.ParentId(tag.Id()) 65 if parentId == "" { 66 return allowEnvironManager && authorizer.AuthEnvironManager() 67 } 68 // All containers with the authenticated 69 // machine as a parent are accessible by it. 70 return names.NewMachineTag(parentId) == authEntityTag 71 } 72 getScopeAuthFunc := func() (common.AuthFunc, error) { 73 return func(tag names.Tag) bool { 74 switch tag := tag.(type) { 75 case names.EnvironTag: 76 // Environment managers can access all volumes 77 // and filesystems scoped to the environment. 78 isEnvironManager := authorizer.AuthEnvironManager() 79 return isEnvironManager && tag == st.EnvironTag() 80 case names.MachineTag: 81 return canAccessStorageMachine(tag, false) 82 default: 83 return false 84 } 85 }, nil 86 } 87 canAccessStorageEntity := func(tag names.Tag, allowMachines bool) bool { 88 switch tag := tag.(type) { 89 case names.VolumeTag: 90 machineTag, ok := names.VolumeMachine(tag) 91 if ok { 92 return canAccessStorageMachine(machineTag, false) 93 } 94 return authorizer.AuthEnvironManager() 95 case names.FilesystemTag: 96 machineTag, ok := names.FilesystemMachine(tag) 97 if ok { 98 return canAccessStorageMachine(machineTag, false) 99 } 100 return authorizer.AuthEnvironManager() 101 case names.MachineTag: 102 return allowMachines && canAccessStorageMachine(tag, true) 103 default: 104 return false 105 } 106 } 107 getStorageEntityAuthFunc := func() (common.AuthFunc, error) { 108 return func(tag names.Tag) bool { 109 return canAccessStorageEntity(tag, false) 110 }, nil 111 } 112 getLifeAuthFunc := func() (common.AuthFunc, error) { 113 return func(tag names.Tag) bool { 114 return canAccessStorageEntity(tag, true) 115 }, nil 116 } 117 getAttachmentAuthFunc := func() (func(names.MachineTag, names.Tag) bool, error) { 118 // getAttachmentAuthFunc returns a function that validates 119 // access by the authenticated user to an attachment. 120 return func(machineTag names.MachineTag, attachmentTag names.Tag) bool { 121 // Machine agents can access their own machine, and 122 // machines contained. Environment managers can access 123 // top-level machines. 124 if !canAccessStorageMachine(machineTag, true) { 125 return false 126 } 127 // Environment managers can access environment-scoped 128 // volumes and volumes scoped to their own machines. 129 // Other machine agents can access volumes regardless 130 // of their scope. 131 if !authorizer.AuthEnvironManager() { 132 return true 133 } 134 var machineScope names.MachineTag 135 var hasMachineScope bool 136 switch attachmentTag := attachmentTag.(type) { 137 case names.VolumeTag: 138 machineScope, hasMachineScope = names.VolumeMachine(attachmentTag) 139 case names.FilesystemTag: 140 machineScope, hasMachineScope = names.FilesystemMachine(attachmentTag) 141 } 142 return !hasMachineScope || machineScope == authorizer.GetAuthTag() 143 }, nil 144 } 145 getMachineAuthFunc := func() (common.AuthFunc, error) { 146 return func(tag names.Tag) bool { 147 if tag, ok := tag.(names.MachineTag); ok { 148 return canAccessStorageMachine(tag, true) 149 } 150 return false 151 }, nil 152 } 153 getBlockDevicesAuthFunc := func() (common.AuthFunc, error) { 154 return func(tag names.Tag) bool { 155 if tag, ok := tag.(names.MachineTag); ok { 156 return canAccessStorageMachine(tag, false) 157 } 158 return false 159 }, nil 160 } 161 stateInterface := getState(st) 162 settings := getSettingsManager(st) 163 return &StorageProvisionerAPI{ 164 LifeGetter: common.NewLifeGetter(stateInterface, getLifeAuthFunc), 165 DeadEnsurer: common.NewDeadEnsurer(stateInterface, getStorageEntityAuthFunc), 166 EnvironWatcher: common.NewEnvironWatcher(stateInterface, resources, authorizer), 167 InstanceIdGetter: common.NewInstanceIdGetter(st, getMachineAuthFunc), 168 StatusSetter: common.NewStatusSetter(st, getStorageEntityAuthFunc), 169 170 st: stateInterface, 171 settings: settings, 172 resources: resources, 173 authorizer: authorizer, 174 getScopeAuthFunc: getScopeAuthFunc, 175 getStorageEntityAuthFunc: getStorageEntityAuthFunc, 176 getAttachmentAuthFunc: getAttachmentAuthFunc, 177 getMachineAuthFunc: getMachineAuthFunc, 178 getBlockDevicesAuthFunc: getBlockDevicesAuthFunc, 179 }, nil 180 } 181 182 // WatchBlockDevices watches for changes to the specified machines' block devices. 183 func (s *StorageProvisionerAPI) WatchBlockDevices(args params.Entities) (params.NotifyWatchResults, error) { 184 canAccess, err := s.getBlockDevicesAuthFunc() 185 if err != nil { 186 return params.NotifyWatchResults{}, common.ServerError(common.ErrPerm) 187 } 188 results := params.NotifyWatchResults{ 189 Results: make([]params.NotifyWatchResult, len(args.Entities)), 190 } 191 one := func(arg params.Entity) (string, error) { 192 machineTag, err := names.ParseMachineTag(arg.Tag) 193 if err != nil { 194 return "", err 195 } 196 if !canAccess(machineTag) { 197 return "", common.ErrPerm 198 } 199 w := s.st.WatchBlockDevices(machineTag) 200 if _, ok := <-w.Changes(); ok { 201 return s.resources.Register(w), nil 202 } 203 return "", watcher.EnsureErr(w) 204 } 205 for i, arg := range args.Entities { 206 var result params.NotifyWatchResult 207 id, err := one(arg) 208 if err != nil { 209 result.Error = common.ServerError(err) 210 } else { 211 result.NotifyWatcherId = id 212 } 213 results.Results[i] = result 214 } 215 return results, nil 216 } 217 218 // WatchMachines watches for changes to the specified machines. 219 func (s *StorageProvisionerAPI) WatchMachines(args params.Entities) (params.NotifyWatchResults, error) { 220 canAccess, err := s.getMachineAuthFunc() 221 if err != nil { 222 return params.NotifyWatchResults{}, common.ServerError(common.ErrPerm) 223 } 224 results := params.NotifyWatchResults{ 225 Results: make([]params.NotifyWatchResult, len(args.Entities)), 226 } 227 one := func(arg params.Entity) (string, error) { 228 machineTag, err := names.ParseMachineTag(arg.Tag) 229 if err != nil { 230 return "", err 231 } 232 if !canAccess(machineTag) { 233 return "", common.ErrPerm 234 } 235 w, err := s.st.WatchMachine(machineTag) 236 if err != nil { 237 return "", errors.Trace(err) 238 } 239 if _, ok := <-w.Changes(); ok { 240 return s.resources.Register(w), nil 241 } 242 return "", watcher.EnsureErr(w) 243 } 244 for i, arg := range args.Entities { 245 var result params.NotifyWatchResult 246 id, err := one(arg) 247 if err != nil { 248 result.Error = common.ServerError(err) 249 } else { 250 result.NotifyWatcherId = id 251 } 252 results.Results[i] = result 253 } 254 return results, nil 255 } 256 257 // WatchVolumes watches for changes to volumes scoped to the 258 // entity with the tag passed to NewState. 259 func (s *StorageProvisionerAPI) WatchVolumes(args params.Entities) (params.StringsWatchResults, error) { 260 return s.watchStorageEntities(args, s.st.WatchEnvironVolumes, s.st.WatchMachineVolumes) 261 } 262 263 // WatchFilesystems watches for changes to filesystems scoped 264 // to the entity with the tag passed to NewState. 265 func (s *StorageProvisionerAPI) WatchFilesystems(args params.Entities) (params.StringsWatchResults, error) { 266 return s.watchStorageEntities(args, s.st.WatchEnvironFilesystems, s.st.WatchMachineFilesystems) 267 } 268 269 func (s *StorageProvisionerAPI) watchStorageEntities( 270 args params.Entities, 271 watchEnvironStorage func() state.StringsWatcher, 272 watchMachineStorage func(names.MachineTag) state.StringsWatcher, 273 ) (params.StringsWatchResults, error) { 274 canAccess, err := s.getScopeAuthFunc() 275 if err != nil { 276 return params.StringsWatchResults{}, common.ServerError(common.ErrPerm) 277 } 278 results := params.StringsWatchResults{ 279 Results: make([]params.StringsWatchResult, len(args.Entities)), 280 } 281 one := func(arg params.Entity) (string, []string, error) { 282 tag, err := names.ParseTag(arg.Tag) 283 if err != nil || !canAccess(tag) { 284 return "", nil, common.ErrPerm 285 } 286 var w state.StringsWatcher 287 if tag, ok := tag.(names.MachineTag); ok { 288 w = watchMachineStorage(tag) 289 } else { 290 w = watchEnvironStorage() 291 } 292 if changes, ok := <-w.Changes(); ok { 293 return s.resources.Register(w), changes, nil 294 } 295 return "", nil, watcher.EnsureErr(w) 296 } 297 for i, arg := range args.Entities { 298 var result params.StringsWatchResult 299 id, changes, err := one(arg) 300 if err != nil { 301 result.Error = common.ServerError(err) 302 } else { 303 result.StringsWatcherId = id 304 result.Changes = changes 305 } 306 results.Results[i] = result 307 } 308 return results, nil 309 } 310 311 // WatchVolumeAttachments watches for changes to volume attachments scoped to 312 // the entity with the tag passed to NewState. 313 func (s *StorageProvisionerAPI) WatchVolumeAttachments(args params.Entities) (params.MachineStorageIdsWatchResults, error) { 314 return s.watchAttachments( 315 args, 316 s.st.WatchEnvironVolumeAttachments, 317 s.st.WatchMachineVolumeAttachments, 318 common.ParseVolumeAttachmentIds, 319 ) 320 } 321 322 // WatchFilesystemAttachments watches for changes to filesystem attachments 323 // scoped to the entity with the tag passed to NewState. 324 func (s *StorageProvisionerAPI) WatchFilesystemAttachments(args params.Entities) (params.MachineStorageIdsWatchResults, error) { 325 return s.watchAttachments( 326 args, 327 s.st.WatchEnvironFilesystemAttachments, 328 s.st.WatchMachineFilesystemAttachments, 329 common.ParseFilesystemAttachmentIds, 330 ) 331 } 332 333 func (s *StorageProvisionerAPI) watchAttachments( 334 args params.Entities, 335 watchEnvironAttachments func() state.StringsWatcher, 336 watchMachineAttachments func(names.MachineTag) state.StringsWatcher, 337 parseAttachmentIds func([]string) ([]params.MachineStorageId, error), 338 ) (params.MachineStorageIdsWatchResults, error) { 339 canAccess, err := s.getScopeAuthFunc() 340 if err != nil { 341 return params.MachineStorageIdsWatchResults{}, common.ServerError(common.ErrPerm) 342 } 343 results := params.MachineStorageIdsWatchResults{ 344 Results: make([]params.MachineStorageIdsWatchResult, len(args.Entities)), 345 } 346 one := func(arg params.Entity) (string, []params.MachineStorageId, error) { 347 tag, err := names.ParseTag(arg.Tag) 348 if err != nil || !canAccess(tag) { 349 return "", nil, common.ErrPerm 350 } 351 var w state.StringsWatcher 352 if tag, ok := tag.(names.MachineTag); ok { 353 w = watchMachineAttachments(tag) 354 } else { 355 w = watchEnvironAttachments() 356 } 357 if stringChanges, ok := <-w.Changes(); ok { 358 changes, err := parseAttachmentIds(stringChanges) 359 if err != nil { 360 w.Stop() 361 return "", nil, err 362 } 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.MachineStorageIdsWatchResult 369 id, changes, err := one(arg) 370 if err != nil { 371 result.Error = common.ServerError(err) 372 } else { 373 result.MachineStorageIdsWatcherId = id 374 result.Changes = changes 375 } 376 results.Results[i] = result 377 } 378 return results, nil 379 } 380 381 // Volumes returns details of volumes with the specified tags. 382 func (s *StorageProvisionerAPI) Volumes(args params.Entities) (params.VolumeResults, error) { 383 canAccess, err := s.getStorageEntityAuthFunc() 384 if err != nil { 385 return params.VolumeResults{}, common.ServerError(common.ErrPerm) 386 } 387 results := params.VolumeResults{ 388 Results: make([]params.VolumeResult, len(args.Entities)), 389 } 390 one := func(arg params.Entity) (params.Volume, error) { 391 tag, err := names.ParseVolumeTag(arg.Tag) 392 if err != nil || !canAccess(tag) { 393 return params.Volume{}, common.ErrPerm 394 } 395 volume, err := s.st.Volume(tag) 396 if errors.IsNotFound(err) { 397 return params.Volume{}, common.ErrPerm 398 } else if err != nil { 399 return params.Volume{}, err 400 } 401 return common.VolumeFromState(volume) 402 } 403 for i, arg := range args.Entities { 404 var result params.VolumeResult 405 volume, err := one(arg) 406 if err != nil { 407 result.Error = common.ServerError(err) 408 } else { 409 result.Result = volume 410 } 411 results.Results[i] = result 412 } 413 return results, nil 414 } 415 416 // Filesystems returns details of filesystems with the specified tags. 417 func (s *StorageProvisionerAPI) Filesystems(args params.Entities) (params.FilesystemResults, error) { 418 canAccess, err := s.getStorageEntityAuthFunc() 419 if err != nil { 420 return params.FilesystemResults{}, common.ServerError(common.ErrPerm) 421 } 422 results := params.FilesystemResults{ 423 Results: make([]params.FilesystemResult, len(args.Entities)), 424 } 425 one := func(arg params.Entity) (params.Filesystem, error) { 426 tag, err := names.ParseFilesystemTag(arg.Tag) 427 if err != nil || !canAccess(tag) { 428 return params.Filesystem{}, common.ErrPerm 429 } 430 filesystem, err := s.st.Filesystem(tag) 431 if errors.IsNotFound(err) { 432 return params.Filesystem{}, common.ErrPerm 433 } else if err != nil { 434 return params.Filesystem{}, err 435 } 436 return common.FilesystemFromState(filesystem) 437 } 438 for i, arg := range args.Entities { 439 var result params.FilesystemResult 440 filesystem, err := one(arg) 441 if err != nil { 442 result.Error = common.ServerError(err) 443 } else { 444 result.Result = filesystem 445 } 446 results.Results[i] = result 447 } 448 return results, nil 449 } 450 451 // VolumeAttachments returns details of volume attachments with the specified IDs. 452 func (s *StorageProvisionerAPI) VolumeAttachments(args params.MachineStorageIds) (params.VolumeAttachmentResults, error) { 453 canAccess, err := s.getAttachmentAuthFunc() 454 if err != nil { 455 return params.VolumeAttachmentResults{}, common.ServerError(common.ErrPerm) 456 } 457 results := params.VolumeAttachmentResults{ 458 Results: make([]params.VolumeAttachmentResult, len(args.Ids)), 459 } 460 one := func(arg params.MachineStorageId) (params.VolumeAttachment, error) { 461 volumeAttachment, err := s.oneVolumeAttachment(arg, canAccess) 462 if err != nil { 463 return params.VolumeAttachment{}, err 464 } 465 return common.VolumeAttachmentFromState(volumeAttachment) 466 } 467 for i, arg := range args.Ids { 468 var result params.VolumeAttachmentResult 469 volumeAttachment, err := one(arg) 470 if err != nil { 471 result.Error = common.ServerError(err) 472 } else { 473 result.Result = volumeAttachment 474 } 475 results.Results[i] = result 476 } 477 return results, nil 478 } 479 480 // VolumeBlockDevices returns details of the block devices corresponding to the 481 // volume attachments with the specified IDs. 482 func (s *StorageProvisionerAPI) VolumeBlockDevices(args params.MachineStorageIds) (params.BlockDeviceResults, error) { 483 canAccess, err := s.getAttachmentAuthFunc() 484 if err != nil { 485 return params.BlockDeviceResults{}, common.ServerError(common.ErrPerm) 486 } 487 results := params.BlockDeviceResults{ 488 Results: make([]params.BlockDeviceResult, len(args.Ids)), 489 } 490 one := func(arg params.MachineStorageId) (storage.BlockDevice, error) { 491 stateBlockDevice, err := s.oneVolumeBlockDevice(arg, canAccess) 492 if err != nil { 493 return storage.BlockDevice{}, err 494 } 495 return common.BlockDeviceFromState(stateBlockDevice), nil 496 } 497 for i, arg := range args.Ids { 498 var result params.BlockDeviceResult 499 blockDevice, err := one(arg) 500 if err != nil { 501 result.Error = common.ServerError(err) 502 } else { 503 result.Result = blockDevice 504 } 505 results.Results[i] = result 506 } 507 return results, nil 508 } 509 510 // FilesystemAttachments returns details of filesystem attachments with the specified IDs. 511 func (s *StorageProvisionerAPI) FilesystemAttachments(args params.MachineStorageIds) (params.FilesystemAttachmentResults, error) { 512 canAccess, err := s.getAttachmentAuthFunc() 513 if err != nil { 514 return params.FilesystemAttachmentResults{}, common.ServerError(common.ErrPerm) 515 } 516 results := params.FilesystemAttachmentResults{ 517 Results: make([]params.FilesystemAttachmentResult, len(args.Ids)), 518 } 519 one := func(arg params.MachineStorageId) (params.FilesystemAttachment, error) { 520 filesystemAttachment, err := s.oneFilesystemAttachment(arg, canAccess) 521 if err != nil { 522 return params.FilesystemAttachment{}, err 523 } 524 return common.FilesystemAttachmentFromState(filesystemAttachment) 525 } 526 for i, arg := range args.Ids { 527 var result params.FilesystemAttachmentResult 528 filesystemAttachment, err := one(arg) 529 if err != nil { 530 result.Error = common.ServerError(err) 531 } else { 532 result.Result = filesystemAttachment 533 } 534 results.Results[i] = result 535 } 536 return results, nil 537 } 538 539 // VolumeParams returns the parameters for creating or destroying 540 // the volumes with the specified tags. 541 func (s *StorageProvisionerAPI) VolumeParams(args params.Entities) (params.VolumeParamsResults, error) { 542 canAccess, err := s.getStorageEntityAuthFunc() 543 if err != nil { 544 return params.VolumeParamsResults{}, err 545 } 546 envConfig, err := s.st.EnvironConfig() 547 if err != nil { 548 return params.VolumeParamsResults{}, err 549 } 550 results := params.VolumeParamsResults{ 551 Results: make([]params.VolumeParamsResult, len(args.Entities)), 552 } 553 poolManager := poolmanager.New(s.settings) 554 one := func(arg params.Entity) (params.VolumeParams, error) { 555 tag, err := names.ParseVolumeTag(arg.Tag) 556 if err != nil || !canAccess(tag) { 557 return params.VolumeParams{}, common.ErrPerm 558 } 559 volume, err := s.st.Volume(tag) 560 if errors.IsNotFound(err) { 561 return params.VolumeParams{}, common.ErrPerm 562 } else if err != nil { 563 return params.VolumeParams{}, err 564 } 565 volumeAttachments, err := s.st.VolumeAttachments(tag) 566 if err != nil { 567 return params.VolumeParams{}, err 568 } 569 storageInstance, err := common.MaybeAssignedStorageInstance( 570 volume.StorageInstance, 571 s.st.StorageInstance, 572 ) 573 if err != nil { 574 return params.VolumeParams{}, err 575 } 576 volumeParams, err := common.VolumeParams(volume, storageInstance, envConfig, poolManager) 577 if err != nil { 578 return params.VolumeParams{}, err 579 } 580 if len(volumeAttachments) == 1 { 581 // There is exactly one attachment to be made, so make 582 // it immediately. Otherwise we will defer attachments 583 // until later. 584 volumeAttachment := volumeAttachments[0] 585 volumeAttachmentParams, ok := volumeAttachment.Params() 586 if !ok { 587 return params.VolumeParams{}, errors.Errorf( 588 "volume %q is already attached to machine %q", 589 volumeAttachment.Volume().Id(), 590 volumeAttachment.Machine().Id(), 591 ) 592 } 593 machineTag := volumeAttachment.Machine() 594 instanceId, err := s.st.MachineInstanceId(machineTag) 595 if errors.IsNotProvisioned(err) { 596 // Leave the attachment until later. 597 instanceId = "" 598 } else if err != nil { 599 return params.VolumeParams{}, err 600 } 601 volumeParams.Attachment = ¶ms.VolumeAttachmentParams{ 602 tag.String(), 603 machineTag.String(), 604 "", // volume ID 605 string(instanceId), 606 volumeParams.Provider, 607 volumeAttachmentParams.ReadOnly, 608 } 609 } 610 return volumeParams, nil 611 } 612 for i, arg := range args.Entities { 613 var result params.VolumeParamsResult 614 volumeParams, err := one(arg) 615 if err != nil { 616 result.Error = common.ServerError(err) 617 } else { 618 result.Result = volumeParams 619 } 620 results.Results[i] = result 621 } 622 return results, nil 623 } 624 625 // FilesystemParams returns the parameters for creating the filesystems 626 // with the specified tags. 627 func (s *StorageProvisionerAPI) FilesystemParams(args params.Entities) (params.FilesystemParamsResults, error) { 628 canAccess, err := s.getStorageEntityAuthFunc() 629 if err != nil { 630 return params.FilesystemParamsResults{}, err 631 } 632 envConfig, err := s.st.EnvironConfig() 633 if err != nil { 634 return params.FilesystemParamsResults{}, err 635 } 636 results := params.FilesystemParamsResults{ 637 Results: make([]params.FilesystemParamsResult, len(args.Entities)), 638 } 639 poolManager := poolmanager.New(s.settings) 640 one := func(arg params.Entity) (params.FilesystemParams, error) { 641 tag, err := names.ParseFilesystemTag(arg.Tag) 642 if err != nil || !canAccess(tag) { 643 return params.FilesystemParams{}, common.ErrPerm 644 } 645 filesystem, err := s.st.Filesystem(tag) 646 if errors.IsNotFound(err) { 647 return params.FilesystemParams{}, common.ErrPerm 648 } else if err != nil { 649 return params.FilesystemParams{}, err 650 } 651 storageInstance, err := common.MaybeAssignedStorageInstance( 652 filesystem.Storage, 653 s.st.StorageInstance, 654 ) 655 if err != nil { 656 return params.FilesystemParams{}, err 657 } 658 filesystemParams, err := common.FilesystemParams( 659 filesystem, storageInstance, envConfig, poolManager, 660 ) 661 if err != nil { 662 return params.FilesystemParams{}, err 663 } 664 return filesystemParams, nil 665 } 666 for i, arg := range args.Entities { 667 var result params.FilesystemParamsResult 668 filesystemParams, err := one(arg) 669 if err != nil { 670 result.Error = common.ServerError(err) 671 } else { 672 result.Result = filesystemParams 673 } 674 results.Results[i] = result 675 } 676 return results, nil 677 } 678 679 // VolumeAttachmentParams returns the parameters for creating the volume 680 // attachments with the specified IDs. 681 func (s *StorageProvisionerAPI) VolumeAttachmentParams( 682 args params.MachineStorageIds, 683 ) (params.VolumeAttachmentParamsResults, error) { 684 canAccess, err := s.getAttachmentAuthFunc() 685 if err != nil { 686 return params.VolumeAttachmentParamsResults{}, common.ServerError(common.ErrPerm) 687 } 688 results := params.VolumeAttachmentParamsResults{ 689 Results: make([]params.VolumeAttachmentParamsResult, len(args.Ids)), 690 } 691 poolManager := poolmanager.New(s.settings) 692 one := func(arg params.MachineStorageId) (params.VolumeAttachmentParams, error) { 693 volumeAttachment, err := s.oneVolumeAttachment(arg, canAccess) 694 if err != nil { 695 return params.VolumeAttachmentParams{}, err 696 } 697 instanceId, err := s.st.MachineInstanceId(volumeAttachment.Machine()) 698 if errors.IsNotProvisioned(err) { 699 // The worker must watch for machine provisioning events. 700 instanceId = "" 701 } else if err != nil { 702 return params.VolumeAttachmentParams{}, err 703 } 704 volume, err := s.st.Volume(volumeAttachment.Volume()) 705 if err != nil { 706 return params.VolumeAttachmentParams{}, err 707 } 708 var volumeId string 709 var pool string 710 if volumeParams, ok := volume.Params(); ok { 711 pool = volumeParams.Pool 712 } else { 713 volumeInfo, err := volume.Info() 714 if err != nil { 715 return params.VolumeAttachmentParams{}, err 716 } 717 volumeId = volumeInfo.VolumeId 718 pool = volumeInfo.Pool 719 } 720 providerType, _, err := common.StoragePoolConfig(pool, poolManager) 721 if err != nil { 722 return params.VolumeAttachmentParams{}, errors.Trace(err) 723 } 724 var readOnly bool 725 if volumeAttachmentParams, ok := volumeAttachment.Params(); ok { 726 readOnly = volumeAttachmentParams.ReadOnly 727 } else { 728 // Attachment parameters may be requested even if the 729 // attachment exists; i.e. for reattachment. 730 volumeAttachmentInfo, err := volumeAttachment.Info() 731 if err != nil { 732 return params.VolumeAttachmentParams{}, errors.Trace(err) 733 } 734 readOnly = volumeAttachmentInfo.ReadOnly 735 } 736 return params.VolumeAttachmentParams{ 737 volumeAttachment.Volume().String(), 738 volumeAttachment.Machine().String(), 739 volumeId, 740 string(instanceId), 741 string(providerType), 742 readOnly, 743 }, nil 744 } 745 for i, arg := range args.Ids { 746 var result params.VolumeAttachmentParamsResult 747 volumeAttachment, err := one(arg) 748 if err != nil { 749 result.Error = common.ServerError(err) 750 } else { 751 result.Result = volumeAttachment 752 } 753 results.Results[i] = result 754 } 755 return results, nil 756 } 757 758 // FilesystemAttachmentParams returns the parameters for creating the filesystem 759 // attachments with the specified IDs. 760 func (s *StorageProvisionerAPI) FilesystemAttachmentParams( 761 args params.MachineStorageIds, 762 ) (params.FilesystemAttachmentParamsResults, error) { 763 canAccess, err := s.getAttachmentAuthFunc() 764 if err != nil { 765 return params.FilesystemAttachmentParamsResults{}, common.ServerError(common.ErrPerm) 766 } 767 results := params.FilesystemAttachmentParamsResults{ 768 Results: make([]params.FilesystemAttachmentParamsResult, len(args.Ids)), 769 } 770 poolManager := poolmanager.New(s.settings) 771 one := func(arg params.MachineStorageId) (params.FilesystemAttachmentParams, error) { 772 filesystemAttachment, err := s.oneFilesystemAttachment(arg, canAccess) 773 if err != nil { 774 return params.FilesystemAttachmentParams{}, err 775 } 776 instanceId, err := s.st.MachineInstanceId(filesystemAttachment.Machine()) 777 if errors.IsNotProvisioned(err) { 778 // The worker must watch for machine provisioning events. 779 instanceId = "" 780 } else if err != nil { 781 return params.FilesystemAttachmentParams{}, err 782 } 783 filesystem, err := s.st.Filesystem(filesystemAttachment.Filesystem()) 784 if err != nil { 785 return params.FilesystemAttachmentParams{}, err 786 } 787 var filesystemId string 788 var pool string 789 if filesystemParams, ok := filesystem.Params(); ok { 790 pool = filesystemParams.Pool 791 } else { 792 filesystemInfo, err := filesystem.Info() 793 if err != nil { 794 return params.FilesystemAttachmentParams{}, err 795 } 796 filesystemId = filesystemInfo.FilesystemId 797 pool = filesystemInfo.Pool 798 } 799 providerType, _, err := common.StoragePoolConfig(pool, poolManager) 800 if err != nil { 801 return params.FilesystemAttachmentParams{}, errors.Trace(err) 802 } 803 var location string 804 var readOnly bool 805 if filesystemAttachmentParams, ok := filesystemAttachment.Params(); ok { 806 location = filesystemAttachmentParams.Location 807 readOnly = filesystemAttachmentParams.ReadOnly 808 } else { 809 // Attachment parameters may be requested even if the 810 // attachment exists; i.e. for reattachment. 811 filesystemAttachmentInfo, err := filesystemAttachment.Info() 812 if err != nil { 813 return params.FilesystemAttachmentParams{}, errors.Trace(err) 814 } 815 location = filesystemAttachmentInfo.MountPoint 816 readOnly = filesystemAttachmentInfo.ReadOnly 817 } 818 return params.FilesystemAttachmentParams{ 819 filesystemAttachment.Filesystem().String(), 820 filesystemAttachment.Machine().String(), 821 filesystemId, 822 string(instanceId), 823 string(providerType), 824 // TODO(axw) dealias MountPoint. We now have 825 // Path, MountPoint and Location in different 826 // parts of the codebase. 827 location, 828 readOnly, 829 }, nil 830 } 831 for i, arg := range args.Ids { 832 var result params.FilesystemAttachmentParamsResult 833 filesystemAttachment, err := one(arg) 834 if err != nil { 835 result.Error = common.ServerError(err) 836 } else { 837 result.Result = filesystemAttachment 838 } 839 results.Results[i] = result 840 } 841 return results, nil 842 } 843 844 func (s *StorageProvisionerAPI) oneVolumeAttachment( 845 id params.MachineStorageId, canAccess func(names.MachineTag, names.Tag) bool, 846 ) (state.VolumeAttachment, error) { 847 machineTag, err := names.ParseMachineTag(id.MachineTag) 848 if err != nil { 849 return nil, err 850 } 851 volumeTag, err := names.ParseVolumeTag(id.AttachmentTag) 852 if err != nil { 853 return nil, err 854 } 855 if !canAccess(machineTag, volumeTag) { 856 return nil, common.ErrPerm 857 } 858 volumeAttachment, err := s.st.VolumeAttachment(machineTag, volumeTag) 859 if errors.IsNotFound(err) { 860 return nil, common.ErrPerm 861 } else if err != nil { 862 return nil, err 863 } 864 return volumeAttachment, nil 865 } 866 867 func (s *StorageProvisionerAPI) oneVolumeBlockDevice( 868 id params.MachineStorageId, canAccess func(names.MachineTag, names.Tag) bool, 869 ) (state.BlockDeviceInfo, error) { 870 volumeAttachment, err := s.oneVolumeAttachment(id, canAccess) 871 if err != nil { 872 return state.BlockDeviceInfo{}, err 873 } 874 volume, err := s.st.Volume(volumeAttachment.Volume()) 875 if err != nil { 876 return state.BlockDeviceInfo{}, err 877 } 878 volumeInfo, err := volume.Info() 879 if err != nil { 880 return state.BlockDeviceInfo{}, err 881 } 882 volumeAttachmentInfo, err := volumeAttachment.Info() 883 if err != nil { 884 return state.BlockDeviceInfo{}, err 885 } 886 blockDevices, err := s.st.BlockDevices(volumeAttachment.Machine()) 887 if err != nil { 888 return state.BlockDeviceInfo{}, err 889 } 890 blockDevice, ok := common.MatchingBlockDevice( 891 blockDevices, 892 volumeInfo, 893 volumeAttachmentInfo, 894 ) 895 if !ok { 896 return state.BlockDeviceInfo{}, errors.NotFoundf( 897 "block device for volume %v on machine %v", 898 volumeAttachment.Volume().Id(), 899 volumeAttachment.Machine().Id(), 900 ) 901 } 902 return *blockDevice, nil 903 } 904 905 func (s *StorageProvisionerAPI) oneFilesystemAttachment( 906 id params.MachineStorageId, canAccess func(names.MachineTag, names.Tag) bool, 907 ) (state.FilesystemAttachment, error) { 908 machineTag, err := names.ParseMachineTag(id.MachineTag) 909 if err != nil { 910 return nil, err 911 } 912 filesystemTag, err := names.ParseFilesystemTag(id.AttachmentTag) 913 if err != nil { 914 return nil, err 915 } 916 if !canAccess(machineTag, filesystemTag) { 917 return nil, common.ErrPerm 918 } 919 filesystemAttachment, err := s.st.FilesystemAttachment(machineTag, filesystemTag) 920 if errors.IsNotFound(err) { 921 return nil, common.ErrPerm 922 } else if err != nil { 923 return nil, err 924 } 925 return filesystemAttachment, nil 926 } 927 928 // SetVolumeInfo records the details of newly provisioned volumes. 929 func (s *StorageProvisionerAPI) SetVolumeInfo(args params.Volumes) (params.ErrorResults, error) { 930 canAccessVolume, err := s.getStorageEntityAuthFunc() 931 if err != nil { 932 return params.ErrorResults{}, err 933 } 934 results := params.ErrorResults{ 935 Results: make([]params.ErrorResult, len(args.Volumes)), 936 } 937 one := func(arg params.Volume) error { 938 volumeTag, volumeInfo, err := common.VolumeToState(arg) 939 if err != nil { 940 return errors.Trace(err) 941 } else if !canAccessVolume(volumeTag) { 942 return common.ErrPerm 943 } 944 err = s.st.SetVolumeInfo(volumeTag, volumeInfo) 945 if errors.IsNotFound(err) { 946 return common.ErrPerm 947 } 948 return errors.Trace(err) 949 } 950 for i, arg := range args.Volumes { 951 err := one(arg) 952 results.Results[i].Error = common.ServerError(err) 953 } 954 return results, nil 955 } 956 957 // SetFilesystemInfo records the details of newly provisioned filesystems. 958 func (s *StorageProvisionerAPI) SetFilesystemInfo(args params.Filesystems) (params.ErrorResults, error) { 959 canAccessFilesystem, err := s.getStorageEntityAuthFunc() 960 if err != nil { 961 return params.ErrorResults{}, err 962 } 963 results := params.ErrorResults{ 964 Results: make([]params.ErrorResult, len(args.Filesystems)), 965 } 966 one := func(arg params.Filesystem) error { 967 filesystemTag, filesystemInfo, err := common.FilesystemToState(arg) 968 if err != nil { 969 return errors.Trace(err) 970 } else if !canAccessFilesystem(filesystemTag) { 971 return common.ErrPerm 972 } 973 err = s.st.SetFilesystemInfo(filesystemTag, filesystemInfo) 974 if errors.IsNotFound(err) { 975 return common.ErrPerm 976 } 977 return errors.Trace(err) 978 } 979 for i, arg := range args.Filesystems { 980 err := one(arg) 981 results.Results[i].Error = common.ServerError(err) 982 } 983 return results, nil 984 } 985 986 // SetVolumeAttachmentInfo records the details of newly provisioned volume 987 // attachments. 988 func (s *StorageProvisionerAPI) SetVolumeAttachmentInfo( 989 args params.VolumeAttachments, 990 ) (params.ErrorResults, error) { 991 canAccess, err := s.getAttachmentAuthFunc() 992 if err != nil { 993 return params.ErrorResults{}, err 994 } 995 results := params.ErrorResults{ 996 Results: make([]params.ErrorResult, len(args.VolumeAttachments)), 997 } 998 one := func(arg params.VolumeAttachment) error { 999 machineTag, volumeTag, volumeAttachmentInfo, err := common.VolumeAttachmentToState(arg) 1000 if err != nil { 1001 return errors.Trace(err) 1002 } 1003 if !canAccess(machineTag, volumeTag) { 1004 return common.ErrPerm 1005 } 1006 err = s.st.SetVolumeAttachmentInfo(machineTag, volumeTag, volumeAttachmentInfo) 1007 if errors.IsNotFound(err) { 1008 return common.ErrPerm 1009 } 1010 return errors.Trace(err) 1011 } 1012 for i, arg := range args.VolumeAttachments { 1013 err := one(arg) 1014 results.Results[i].Error = common.ServerError(err) 1015 } 1016 return results, nil 1017 } 1018 1019 // SetFilesystemAttachmentInfo records the details of newly provisioned filesystem 1020 // attachments. 1021 func (s *StorageProvisionerAPI) SetFilesystemAttachmentInfo( 1022 args params.FilesystemAttachments, 1023 ) (params.ErrorResults, error) { 1024 canAccess, err := s.getAttachmentAuthFunc() 1025 if err != nil { 1026 return params.ErrorResults{}, err 1027 } 1028 results := params.ErrorResults{ 1029 Results: make([]params.ErrorResult, len(args.FilesystemAttachments)), 1030 } 1031 one := func(arg params.FilesystemAttachment) error { 1032 machineTag, filesystemTag, filesystemAttachmentInfo, err := common.FilesystemAttachmentToState(arg) 1033 if err != nil { 1034 return errors.Trace(err) 1035 } 1036 if !canAccess(machineTag, filesystemTag) { 1037 return common.ErrPerm 1038 } 1039 err = s.st.SetFilesystemAttachmentInfo(machineTag, filesystemTag, filesystemAttachmentInfo) 1040 if errors.IsNotFound(err) { 1041 return common.ErrPerm 1042 } 1043 return errors.Trace(err) 1044 } 1045 for i, arg := range args.FilesystemAttachments { 1046 err := one(arg) 1047 results.Results[i].Error = common.ServerError(err) 1048 } 1049 return results, nil 1050 } 1051 1052 // AttachmentLife returns the lifecycle state of each specified machine 1053 // storage attachment. 1054 func (s *StorageProvisionerAPI) AttachmentLife(args params.MachineStorageIds) (params.LifeResults, error) { 1055 canAccess, err := s.getAttachmentAuthFunc() 1056 if err != nil { 1057 return params.LifeResults{}, err 1058 } 1059 results := params.LifeResults{ 1060 Results: make([]params.LifeResult, len(args.Ids)), 1061 } 1062 one := func(arg params.MachineStorageId) (params.Life, error) { 1063 machineTag, err := names.ParseMachineTag(arg.MachineTag) 1064 if err != nil { 1065 return "", err 1066 } 1067 attachmentTag, err := names.ParseTag(arg.AttachmentTag) 1068 if err != nil { 1069 return "", err 1070 } 1071 if !canAccess(machineTag, attachmentTag) { 1072 return "", common.ErrPerm 1073 } 1074 var lifer state.Lifer 1075 switch attachmentTag := attachmentTag.(type) { 1076 case names.VolumeTag: 1077 lifer, err = s.st.VolumeAttachment(machineTag, attachmentTag) 1078 case names.FilesystemTag: 1079 lifer, err = s.st.FilesystemAttachment(machineTag, attachmentTag) 1080 } 1081 if errors.IsNotFound(err) { 1082 return "", common.ErrPerm 1083 } else if err != nil { 1084 return "", errors.Trace(err) 1085 } 1086 return params.Life(lifer.Life().String()), nil 1087 } 1088 for i, arg := range args.Ids { 1089 life, err := one(arg) 1090 if err != nil { 1091 results.Results[i].Error = common.ServerError(err) 1092 } else { 1093 results.Results[i].Life = life 1094 } 1095 } 1096 return results, nil 1097 } 1098 1099 // Remove removes volumes and filesystems from state. 1100 func (s *StorageProvisionerAPI) Remove(args params.Entities) (params.ErrorResults, error) { 1101 canAccess, err := s.getStorageEntityAuthFunc() 1102 if err != nil { 1103 return params.ErrorResults{}, err 1104 } 1105 results := params.ErrorResults{ 1106 Results: make([]params.ErrorResult, len(args.Entities)), 1107 } 1108 one := func(arg params.Entity) error { 1109 tag, err := names.ParseTag(arg.Tag) 1110 if err != nil { 1111 return errors.Trace(err) 1112 } 1113 if !canAccess(tag) { 1114 return common.ErrPerm 1115 } 1116 switch tag := tag.(type) { 1117 case names.FilesystemTag: 1118 return s.st.RemoveFilesystem(tag) 1119 case names.VolumeTag: 1120 return s.st.RemoveVolume(tag) 1121 default: 1122 // should have been picked up by canAccess 1123 logger.Debugf("unexpected %v tag", tag.Kind()) 1124 return common.ErrPerm 1125 } 1126 } 1127 for i, arg := range args.Entities { 1128 err := one(arg) 1129 results.Results[i].Error = common.ServerError(err) 1130 } 1131 return results, nil 1132 } 1133 1134 // RemoveAttachments removes the specified machine storage attachments 1135 // from state. 1136 func (s *StorageProvisionerAPI) RemoveAttachment(args params.MachineStorageIds) (params.ErrorResults, error) { 1137 canAccess, err := s.getAttachmentAuthFunc() 1138 if err != nil { 1139 return params.ErrorResults{}, err 1140 } 1141 results := params.ErrorResults{ 1142 Results: make([]params.ErrorResult, len(args.Ids)), 1143 } 1144 removeAttachment := func(arg params.MachineStorageId) error { 1145 machineTag, err := names.ParseMachineTag(arg.MachineTag) 1146 if err != nil { 1147 return err 1148 } 1149 attachmentTag, err := names.ParseTag(arg.AttachmentTag) 1150 if err != nil { 1151 return err 1152 } 1153 if !canAccess(machineTag, attachmentTag) { 1154 return common.ErrPerm 1155 } 1156 switch attachmentTag := attachmentTag.(type) { 1157 case names.VolumeTag: 1158 return s.st.RemoveVolumeAttachment(machineTag, attachmentTag) 1159 case names.FilesystemTag: 1160 return s.st.RemoveFilesystemAttachment(machineTag, attachmentTag) 1161 default: 1162 return common.ErrPerm 1163 } 1164 } 1165 for i, arg := range args.Ids { 1166 if err := removeAttachment(arg); err != nil { 1167 results.Results[i].Error = common.ServerError(err) 1168 } 1169 } 1170 return results, nil 1171 }