github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/worker/storageprovisioner/storageprovisioner.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  // Package storageprovisioner provides a worker that manages the provisioning
     5  // and deprovisioning of storage volumes and filesystems, and attaching them
     6  // to and detaching them from machines.
     7  //
     8  // A storage provisioner worker is run at each environment manager, which
     9  // manages environment-scoped storage such as virtual disk services of the
    10  // cloud provider. In addition to this, each machine agent runs a machine-
    11  // storage provisioner worker that manages storage scoped to that machine,
    12  // such as loop devices, temporary filesystems (tmpfs), and rootfs.
    13  //
    14  // The storage provisioner worker is comprised of the following major
    15  // components:
    16  //  - a set of watchers for provisioning and attachment events
    17  //  - a schedule of pending operations
    18  //  - event-handling code fed by the watcher, that identifies
    19  //    interesting changes (unprovisioned -> provisioned, etc.),
    20  //    ensures prerequisites are met (e.g. volume and machine are both
    21  //    provisioned before attachment is attempted), and populates
    22  //    operations into the schedule
    23  //  - operation execution code fed by the schedule, that groups
    24  //    operations to make bulk calls to storage providers; updates
    25  //    status; and reschedules operations upon failure
    26  //
    27  package storageprovisioner
    28  
    29  import (
    30  	"github.com/juju/errors"
    31  	"github.com/juju/loggo"
    32  	"github.com/juju/names"
    33  	"github.com/juju/utils/clock"
    34  	"github.com/juju/utils/set"
    35  	"launchpad.net/tomb"
    36  
    37  	apiwatcher "github.com/juju/juju/api/watcher"
    38  	"github.com/juju/juju/apiserver/params"
    39  	"github.com/juju/juju/environs/config"
    40  	"github.com/juju/juju/state/watcher"
    41  	"github.com/juju/juju/storage"
    42  	"github.com/juju/juju/storage/provider"
    43  	"github.com/juju/juju/worker"
    44  	"github.com/juju/juju/worker/storageprovisioner/internal/schedule"
    45  )
    46  
    47  var logger = loggo.GetLogger("juju.worker.storageprovisioner")
    48  
    49  var newManagedFilesystemSource = provider.NewManagedFilesystemSource
    50  
    51  // VolumeAccessor defines an interface used to allow a storage provisioner
    52  // worker to perform volume related operations.
    53  type VolumeAccessor interface {
    54  	// WatchBlockDevices watches for changes to the block devices of the
    55  	// specified machine.
    56  	WatchBlockDevices(names.MachineTag) (apiwatcher.NotifyWatcher, error)
    57  
    58  	// WatchVolumes watches for changes to volumes that this storage
    59  	// provisioner is responsible for.
    60  	WatchVolumes() (apiwatcher.StringsWatcher, error)
    61  
    62  	// WatchVolumeAttachments watches for changes to volume attachments
    63  	// that this storage provisioner is responsible for.
    64  	WatchVolumeAttachments() (apiwatcher.MachineStorageIdsWatcher, error)
    65  
    66  	// Volumes returns details of volumes with the specified tags.
    67  	Volumes([]names.VolumeTag) ([]params.VolumeResult, error)
    68  
    69  	// VolumeBlockDevices returns details of block devices corresponding to
    70  	// the specified volume attachment IDs.
    71  	VolumeBlockDevices([]params.MachineStorageId) ([]params.BlockDeviceResult, error)
    72  
    73  	// VolumeAttachments returns details of volume attachments with
    74  	// the specified tags.
    75  	VolumeAttachments([]params.MachineStorageId) ([]params.VolumeAttachmentResult, error)
    76  
    77  	// VolumeParams returns the parameters for creating the volumes
    78  	// with the specified tags.
    79  	VolumeParams([]names.VolumeTag) ([]params.VolumeParamsResult, error)
    80  
    81  	// VolumeAttachmentParams returns the parameters for creating the
    82  	// volume attachments with the specified tags.
    83  	VolumeAttachmentParams([]params.MachineStorageId) ([]params.VolumeAttachmentParamsResult, error)
    84  
    85  	// SetVolumeInfo records the details of newly provisioned volumes.
    86  	SetVolumeInfo([]params.Volume) ([]params.ErrorResult, error)
    87  
    88  	// SetVolumeAttachmentInfo records the details of newly provisioned
    89  	// volume attachments.
    90  	SetVolumeAttachmentInfo([]params.VolumeAttachment) ([]params.ErrorResult, error)
    91  }
    92  
    93  // FilesystemAccessor defines an interface used to allow a storage provisioner
    94  // worker to perform filesystem related operations.
    95  type FilesystemAccessor interface {
    96  	// WatchFilesystems watches for changes to filesystems that this
    97  	// storage provisioner is responsible for.
    98  	WatchFilesystems() (apiwatcher.StringsWatcher, error)
    99  
   100  	// WatchFilesystemAttachments watches for changes to filesystem attachments
   101  	// that this storage provisioner is responsible for.
   102  	WatchFilesystemAttachments() (apiwatcher.MachineStorageIdsWatcher, error)
   103  
   104  	// Filesystems returns details of filesystems with the specified tags.
   105  	Filesystems([]names.FilesystemTag) ([]params.FilesystemResult, error)
   106  
   107  	// FilesystemAttachments returns details of filesystem attachments with
   108  	// the specified tags.
   109  	FilesystemAttachments([]params.MachineStorageId) ([]params.FilesystemAttachmentResult, error)
   110  
   111  	// FilesystemParams returns the parameters for creating the filesystems
   112  	// with the specified tags.
   113  	FilesystemParams([]names.FilesystemTag) ([]params.FilesystemParamsResult, error)
   114  
   115  	// FilesystemAttachmentParams returns the parameters for creating the
   116  	// filesystem attachments with the specified tags.
   117  	FilesystemAttachmentParams([]params.MachineStorageId) ([]params.FilesystemAttachmentParamsResult, error)
   118  
   119  	// SetFilesystemInfo records the details of newly provisioned filesystems.
   120  	SetFilesystemInfo([]params.Filesystem) ([]params.ErrorResult, error)
   121  
   122  	// SetFilesystemAttachmentInfo records the details of newly provisioned
   123  	// filesystem attachments.
   124  	SetFilesystemAttachmentInfo([]params.FilesystemAttachment) ([]params.ErrorResult, error)
   125  }
   126  
   127  // MachineAccessor defines an interface used to allow a storage provisioner
   128  // worker to perform machine related operations.
   129  type MachineAccessor interface {
   130  	// WatchMachine watches for changes to the specified machine.
   131  	WatchMachine(names.MachineTag) (apiwatcher.NotifyWatcher, error)
   132  
   133  	// InstanceIds returns the instance IDs of each machine.
   134  	InstanceIds([]names.MachineTag) ([]params.StringResult, error)
   135  }
   136  
   137  // LifecycleManager defines an interface used to enable a storage provisioner
   138  // worker to perform lifcycle-related operations on storage entities and
   139  // attachments.
   140  type LifecycleManager interface {
   141  	// Life returns the lifecycle state of the specified entities.
   142  	Life([]names.Tag) ([]params.LifeResult, error)
   143  
   144  	// Remove removes the specified entities from state.
   145  	Remove([]names.Tag) ([]params.ErrorResult, error)
   146  
   147  	// AttachmentLife returns the lifecycle state of the specified
   148  	// machine/entity attachments.
   149  	AttachmentLife([]params.MachineStorageId) ([]params.LifeResult, error)
   150  
   151  	// RemoveAttachments removes the specified machine/entity attachments
   152  	// from state.
   153  	RemoveAttachments([]params.MachineStorageId) ([]params.ErrorResult, error)
   154  }
   155  
   156  // StatusSetter defines an interface used to set the status of entities.
   157  type StatusSetter interface {
   158  	SetStatus([]params.EntityStatusArgs) error
   159  }
   160  
   161  // EnvironAccessor defines an interface used to enable a storage provisioner
   162  // worker to watch changes to and read environment config, to use when
   163  // provisioning storage.
   164  type EnvironAccessor interface {
   165  	// WatchForEnvironConfigChanges returns a watcher that will be notified
   166  	// whenever the environment config changes in state.
   167  	WatchForEnvironConfigChanges() (apiwatcher.NotifyWatcher, error)
   168  
   169  	// EnvironConfig returns the current environment config.
   170  	EnvironConfig() (*config.Config, error)
   171  }
   172  
   173  // NewStorageProvisioner returns a Worker which manages
   174  // provisioning (deprovisioning), and attachment (detachment)
   175  // of first-class volumes and filesystems.
   176  //
   177  // Machine-scoped storage workers will be provided with
   178  // a storage directory, while environment-scoped workers
   179  // will not. If the directory path is non-empty, then it
   180  // will be passed to the storage source via its config.
   181  func NewStorageProvisioner(
   182  	scope names.Tag,
   183  	storageDir string,
   184  	v VolumeAccessor,
   185  	f FilesystemAccessor,
   186  	l LifecycleManager,
   187  	e EnvironAccessor,
   188  	m MachineAccessor,
   189  	s StatusSetter,
   190  	clock clock.Clock,
   191  ) worker.Worker {
   192  	w := &storageprovisioner{
   193  		scope:       scope,
   194  		storageDir:  storageDir,
   195  		volumes:     v,
   196  		filesystems: f,
   197  		life:        l,
   198  		environ:     e,
   199  		machines:    m,
   200  		status:      s,
   201  		clock:       clock,
   202  	}
   203  	go func() {
   204  		defer w.tomb.Done()
   205  		err := w.loop()
   206  		if err != tomb.ErrDying {
   207  			logger.Errorf("%s", err)
   208  		}
   209  		w.tomb.Kill(err)
   210  	}()
   211  	return w
   212  }
   213  
   214  type storageprovisioner struct {
   215  	tomb        tomb.Tomb
   216  	scope       names.Tag
   217  	storageDir  string
   218  	volumes     VolumeAccessor
   219  	filesystems FilesystemAccessor
   220  	life        LifecycleManager
   221  	environ     EnvironAccessor
   222  	machines    MachineAccessor
   223  	status      StatusSetter
   224  	clock       clock.Clock
   225  }
   226  
   227  // Kill implements Worker.Kill().
   228  func (w *storageprovisioner) Kill() {
   229  	w.tomb.Kill(nil)
   230  }
   231  
   232  // Wait implements Worker.Wait().
   233  func (w *storageprovisioner) Wait() error {
   234  	return w.tomb.Wait()
   235  }
   236  
   237  func (w *storageprovisioner) loop() error {
   238  	var environConfigChanges <-chan struct{}
   239  	var volumesWatcher apiwatcher.StringsWatcher
   240  	var filesystemsWatcher apiwatcher.StringsWatcher
   241  	var volumesChanges <-chan []string
   242  	var filesystemsChanges <-chan []string
   243  	var volumeAttachmentsWatcher apiwatcher.MachineStorageIdsWatcher
   244  	var filesystemAttachmentsWatcher apiwatcher.MachineStorageIdsWatcher
   245  	var volumeAttachmentsChanges <-chan []params.MachineStorageId
   246  	var filesystemAttachmentsChanges <-chan []params.MachineStorageId
   247  	var machineBlockDevicesWatcher apiwatcher.NotifyWatcher
   248  	var machineBlockDevicesChanges <-chan struct{}
   249  	machineChanges := make(chan names.MachineTag)
   250  
   251  	environConfigWatcher, err := w.environ.WatchForEnvironConfigChanges()
   252  	if err != nil {
   253  		return errors.Annotate(err, "watching environ config")
   254  	}
   255  	defer watcher.Stop(environConfigWatcher, &w.tomb)
   256  	environConfigChanges = environConfigWatcher.Changes()
   257  
   258  	// Machine-scoped provisioners need to watch block devices, to create
   259  	// volume-backed filesystems.
   260  	if machineTag, ok := w.scope.(names.MachineTag); ok {
   261  		machineBlockDevicesWatcher, err = w.volumes.WatchBlockDevices(machineTag)
   262  		if err != nil {
   263  			return errors.Annotate(err, "watching block devices")
   264  		}
   265  		defer watcher.Stop(machineBlockDevicesWatcher, &w.tomb)
   266  		machineBlockDevicesChanges = machineBlockDevicesWatcher.Changes()
   267  	}
   268  
   269  	// The other watchers are started dynamically; stop only if started.
   270  	defer w.maybeStopWatcher(volumesWatcher)
   271  	defer w.maybeStopWatcher(volumeAttachmentsWatcher)
   272  	defer w.maybeStopWatcher(filesystemsWatcher)
   273  	defer w.maybeStopWatcher(filesystemAttachmentsWatcher)
   274  
   275  	startWatchers := func() error {
   276  		var err error
   277  		volumesWatcher, err = w.volumes.WatchVolumes()
   278  		if err != nil {
   279  			return errors.Annotate(err, "watching volumes")
   280  		}
   281  		filesystemsWatcher, err = w.filesystems.WatchFilesystems()
   282  		if err != nil {
   283  			return errors.Annotate(err, "watching filesystems")
   284  		}
   285  		volumeAttachmentsWatcher, err = w.volumes.WatchVolumeAttachments()
   286  		if err != nil {
   287  			return errors.Annotate(err, "watching volume attachments")
   288  		}
   289  		filesystemAttachmentsWatcher, err = w.filesystems.WatchFilesystemAttachments()
   290  		if err != nil {
   291  			return errors.Annotate(err, "watching filesystem attachments")
   292  		}
   293  		volumesChanges = volumesWatcher.Changes()
   294  		filesystemsChanges = filesystemsWatcher.Changes()
   295  		volumeAttachmentsChanges = volumeAttachmentsWatcher.Changes()
   296  		filesystemAttachmentsChanges = filesystemAttachmentsWatcher.Changes()
   297  		return nil
   298  	}
   299  
   300  	ctx := context{
   301  		scope:                             w.scope,
   302  		storageDir:                        w.storageDir,
   303  		volumeAccessor:                    w.volumes,
   304  		filesystemAccessor:                w.filesystems,
   305  		life:                              w.life,
   306  		machineAccessor:                   w.machines,
   307  		statusSetter:                      w.status,
   308  		time:                              w.clock,
   309  		volumes:                           make(map[names.VolumeTag]storage.Volume),
   310  		volumeAttachments:                 make(map[params.MachineStorageId]storage.VolumeAttachment),
   311  		volumeBlockDevices:                make(map[names.VolumeTag]storage.BlockDevice),
   312  		filesystems:                       make(map[names.FilesystemTag]storage.Filesystem),
   313  		filesystemAttachments:             make(map[params.MachineStorageId]storage.FilesystemAttachment),
   314  		machines:                          make(map[names.MachineTag]*machineWatcher),
   315  		machineChanges:                    machineChanges,
   316  		schedule:                          schedule.NewSchedule(w.clock),
   317  		pendingVolumeBlockDevices:         make(set.Tags),
   318  		incompleteVolumeParams:            make(map[names.VolumeTag]storage.VolumeParams),
   319  		incompleteVolumeAttachmentParams:  make(map[params.MachineStorageId]storage.VolumeAttachmentParams),
   320  		pendingFilesystems:                make(map[names.FilesystemTag]storage.FilesystemParams),
   321  		pendingFilesystemAttachments:      make(map[params.MachineStorageId]storage.FilesystemAttachmentParams),
   322  		pendingDyingFilesystemAttachments: make(map[params.MachineStorageId]storage.FilesystemAttachmentParams),
   323  	}
   324  	ctx.managedFilesystemSource = newManagedFilesystemSource(
   325  		ctx.volumeBlockDevices, ctx.filesystems,
   326  	)
   327  	defer func() {
   328  		for _, w := range ctx.machines {
   329  			w.stop()
   330  		}
   331  	}()
   332  
   333  	for {
   334  		// Check if any pending operations can be fulfilled.
   335  		if err := processPending(&ctx); err != nil {
   336  			return errors.Trace(err)
   337  		}
   338  
   339  		select {
   340  		case <-w.tomb.Dying():
   341  			return tomb.ErrDying
   342  		case _, ok := <-environConfigChanges:
   343  			if !ok {
   344  				return watcher.EnsureErr(environConfigWatcher)
   345  			}
   346  			environConfig, err := w.environ.EnvironConfig()
   347  			if err != nil {
   348  				return errors.Annotate(err, "getting environ config")
   349  			}
   350  			if ctx.environConfig == nil {
   351  				// We've received the initial environ config,
   352  				// so we can begin provisioning storage.
   353  				if err := startWatchers(); err != nil {
   354  					return err
   355  				}
   356  			}
   357  			ctx.environConfig = environConfig
   358  		case changes, ok := <-volumesChanges:
   359  			if !ok {
   360  				return watcher.EnsureErr(volumesWatcher)
   361  			}
   362  			if err := volumesChanged(&ctx, changes); err != nil {
   363  				return errors.Trace(err)
   364  			}
   365  		case changes, ok := <-volumeAttachmentsChanges:
   366  			if !ok {
   367  				return watcher.EnsureErr(volumeAttachmentsWatcher)
   368  			}
   369  			if err := volumeAttachmentsChanged(&ctx, changes); err != nil {
   370  				return errors.Trace(err)
   371  			}
   372  		case changes, ok := <-filesystemsChanges:
   373  			if !ok {
   374  				return watcher.EnsureErr(filesystemsWatcher)
   375  			}
   376  			if err := filesystemsChanged(&ctx, changes); err != nil {
   377  				return errors.Trace(err)
   378  			}
   379  		case changes, ok := <-filesystemAttachmentsChanges:
   380  			if !ok {
   381  				return watcher.EnsureErr(filesystemAttachmentsWatcher)
   382  			}
   383  			if err := filesystemAttachmentsChanged(&ctx, changes); err != nil {
   384  				return errors.Trace(err)
   385  			}
   386  		case _, ok := <-machineBlockDevicesChanges:
   387  			if !ok {
   388  				return watcher.EnsureErr(machineBlockDevicesWatcher)
   389  			}
   390  			if err := machineBlockDevicesChanged(&ctx); err != nil {
   391  				return errors.Trace(err)
   392  			}
   393  		case machineTag := <-machineChanges:
   394  			if err := refreshMachine(&ctx, machineTag); err != nil {
   395  				return errors.Trace(err)
   396  			}
   397  		case <-ctx.schedule.Next():
   398  			// Ready to pick something(s) off the pending queue.
   399  			if err := processSchedule(&ctx); err != nil {
   400  				return errors.Trace(err)
   401  			}
   402  		}
   403  	}
   404  }
   405  
   406  // processPending checks if the pending operations' prerequisites have
   407  // been met, and processes them if so.
   408  func processPending(ctx *context) error {
   409  	if err := processPendingVolumeBlockDevices(ctx); err != nil {
   410  		return errors.Annotate(err, "processing pending block devices")
   411  	}
   412  	// TODO(axw) below should be handled by processSchedule.
   413  	if err := processPendingFilesystems(ctx); err != nil {
   414  		return errors.Annotate(err, "processing pending filesystems")
   415  	}
   416  	if err := processPendingDyingFilesystemAttachments(ctx); err != nil {
   417  		return errors.Annotate(err, "processing pending, dying filesystem attachments")
   418  	}
   419  	if err := processPendingFilesystemAttachments(ctx); err != nil {
   420  		return errors.Annotate(err, "processing pending filesystem attachments")
   421  	}
   422  	return nil
   423  }
   424  
   425  // processSchedule executes scheduled operations.
   426  func processSchedule(ctx *context) error {
   427  	ready := ctx.schedule.Ready(ctx.time.Now())
   428  	createVolumeOps := make(map[names.VolumeTag]*createVolumeOp)
   429  	destroyVolumeOps := make(map[names.VolumeTag]*destroyVolumeOp)
   430  	attachVolumeOps := make(map[params.MachineStorageId]*attachVolumeOp)
   431  	detachVolumeOps := make(map[params.MachineStorageId]*detachVolumeOp)
   432  	for _, item := range ready {
   433  		op := item.(scheduleOp)
   434  		key := op.key()
   435  		switch op := op.(type) {
   436  		case *createVolumeOp:
   437  			createVolumeOps[key.(names.VolumeTag)] = op
   438  		case *destroyVolumeOp:
   439  			destroyVolumeOps[key.(names.VolumeTag)] = op
   440  		case *attachVolumeOp:
   441  			attachVolumeOps[key.(params.MachineStorageId)] = op
   442  		case *detachVolumeOp:
   443  			detachVolumeOps[key.(params.MachineStorageId)] = op
   444  		}
   445  	}
   446  	if len(destroyVolumeOps) > 0 {
   447  		if err := destroyVolumes(ctx, destroyVolumeOps); err != nil {
   448  			return errors.Annotate(err, "destroying volumes")
   449  		}
   450  	}
   451  	if len(createVolumeOps) > 0 {
   452  		if err := createVolumes(ctx, createVolumeOps); err != nil {
   453  			return errors.Annotate(err, "creating volumes")
   454  		}
   455  	}
   456  	if len(detachVolumeOps) > 0 {
   457  		if err := detachVolumes(ctx, detachVolumeOps); err != nil {
   458  			return errors.Annotate(err, "detaching volumes")
   459  		}
   460  	}
   461  	if len(attachVolumeOps) > 0 {
   462  		if err := attachVolumes(ctx, attachVolumeOps); err != nil {
   463  			return errors.Annotate(err, "attaching volumes")
   464  		}
   465  	}
   466  	return nil
   467  }
   468  
   469  func (p *storageprovisioner) maybeStopWatcher(w watcher.Stopper) {
   470  	if w != nil {
   471  		watcher.Stop(w, &p.tomb)
   472  	}
   473  }
   474  
   475  type context struct {
   476  	scope              names.Tag
   477  	environConfig      *config.Config
   478  	storageDir         string
   479  	volumeAccessor     VolumeAccessor
   480  	filesystemAccessor FilesystemAccessor
   481  	life               LifecycleManager
   482  	machineAccessor    MachineAccessor
   483  	statusSetter       StatusSetter
   484  	time               clock.Clock
   485  
   486  	// volumes contains information about provisioned volumes.
   487  	volumes map[names.VolumeTag]storage.Volume
   488  
   489  	// volumeAttachments contains information about attached volumes.
   490  	volumeAttachments map[params.MachineStorageId]storage.VolumeAttachment
   491  
   492  	// volumeBlockDevices contains information about block devices
   493  	// corresponding to volumes attached to the scope-machine. This
   494  	// is only used by the machine-scoped storage provisioner.
   495  	volumeBlockDevices map[names.VolumeTag]storage.BlockDevice
   496  
   497  	// filesystems contains information about provisioned filesystems.
   498  	filesystems map[names.FilesystemTag]storage.Filesystem
   499  
   500  	// filesystemAttachments contains information about attached filesystems.
   501  	filesystemAttachments map[params.MachineStorageId]storage.FilesystemAttachment
   502  
   503  	// machines contains information about machines in the worker's scope.
   504  	machines map[names.MachineTag]*machineWatcher
   505  
   506  	// machineChanges is a channel that machine watchers will send to once
   507  	// their machine is known to have been provisioned.
   508  	machineChanges chan<- names.MachineTag
   509  
   510  	// schedule is the schedule of storage operations.
   511  	schedule *schedule.Schedule
   512  
   513  	// incompleteVolumeParams contains incomplete parameters for volumes.
   514  	//
   515  	// Volume parameters are incomplete when they lack information about
   516  	// the initial attachment. Once the initial attachment information is
   517  	// available, the parameters are removed from this map and a volume
   518  	// creation operation is scheduled.
   519  	incompleteVolumeParams map[names.VolumeTag]storage.VolumeParams
   520  
   521  	// incompleteVolumeAttachmentParams contains incomplete parameters
   522  	// for volume attachments
   523  	//
   524  	// Volume attachment parameters are incomplete when they lack
   525  	// information about the associated volume or machine. Once this
   526  	// information is available, the parameters are removed from this
   527  	// map and a volume attachment operation is scheduled.
   528  	incompleteVolumeAttachmentParams map[params.MachineStorageId]storage.VolumeAttachmentParams
   529  
   530  	// pendingVolumeBlockDevices contains the tags of volumes about whose
   531  	// block devices we wish to enquire.
   532  	pendingVolumeBlockDevices set.Tags
   533  
   534  	// pendingFilesystems contains parameters for filesystems that are
   535  	// yet to be created.
   536  	pendingFilesystems map[names.FilesystemTag]storage.FilesystemParams
   537  
   538  	// pendingFilesystemAttachments contains parameters for filesystem attachments
   539  	// that are yet to be created.
   540  	pendingFilesystemAttachments map[params.MachineStorageId]storage.FilesystemAttachmentParams
   541  
   542  	// pendingDyingFilesystemAttachments contains parameters for filesystem attachments
   543  	// that are to be destroyed.
   544  	pendingDyingFilesystemAttachments map[params.MachineStorageId]storage.FilesystemAttachmentParams
   545  
   546  	// managedFilesystemSource is a storage.FilesystemSource that
   547  	// manages filesystems backed by volumes attached to the host
   548  	// machine.
   549  	managedFilesystemSource storage.FilesystemSource
   550  }