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 = &params.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  }