github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/worker/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  	"time"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/names/v5"
    11  	"github.com/juju/worker/v3"
    12  	"github.com/juju/worker/v3/catacomb"
    13  
    14  	"github.com/juju/juju/core/watcher"
    15  	"github.com/juju/juju/rpc/params"
    16  	"github.com/juju/juju/storage"
    17  	"github.com/juju/juju/storage/provider"
    18  	"github.com/juju/juju/worker/storageprovisioner/internal/schedule"
    19  )
    20  
    21  var (
    22  	// defaultDependentChangesTimeout is the default timeout for waiting for any
    23  	// dependent changes to occur before proceeding with a storage provisioner.
    24  	// This is a variable so it can be overridden in tests (sigh).
    25  	defaultDependentChangesTimeout = time.Second
    26  )
    27  
    28  // logger is here to stop the desire of creating a package level logger.
    29  // Don't do this, instead use the one passed as manifold config.
    30  type logger interface{}
    31  
    32  var _ logger = struct{}{}
    33  
    34  var newManagedFilesystemSource = provider.NewManagedFilesystemSource
    35  
    36  // VolumeAccessor defines an interface used to allow a storage provisioner
    37  // worker to perform volume related operations.
    38  type VolumeAccessor interface {
    39  	// WatchBlockDevices watches for changes to the block devices of the
    40  	// specified machine.
    41  	WatchBlockDevices(names.MachineTag) (watcher.NotifyWatcher, error)
    42  
    43  	// WatchVolumes watches for changes to volumes that this storage
    44  	// provisioner is responsible for.
    45  	WatchVolumes(scope names.Tag) (watcher.StringsWatcher, error)
    46  
    47  	// WatchVolumeAttachments watches for changes to volume attachments
    48  	// that this storage provisioner is responsible for.
    49  	WatchVolumeAttachments(scope names.Tag) (watcher.MachineStorageIdsWatcher, error)
    50  
    51  	// WatchVolumeAttachmentPlans watches for changes to volume attachments
    52  	// destined for this machine. It allows the machine agent to do any extra
    53  	// initialization of the attachment, such as logging into the iSCSI target
    54  	WatchVolumeAttachmentPlans(scope names.Tag) (watcher.MachineStorageIdsWatcher, error)
    55  
    56  	// Volumes returns details of volumes with the specified tags.
    57  	Volumes([]names.VolumeTag) ([]params.VolumeResult, error)
    58  
    59  	// VolumeBlockDevices returns details of block devices corresponding to
    60  	// the specified volume attachment IDs.
    61  	VolumeBlockDevices([]params.MachineStorageId) ([]params.BlockDeviceResult, error)
    62  
    63  	// VolumeAttachments returns details of volume attachments with
    64  	// the specified tags.
    65  	VolumeAttachments([]params.MachineStorageId) ([]params.VolumeAttachmentResult, error)
    66  
    67  	VolumeAttachmentPlans([]params.MachineStorageId) ([]params.VolumeAttachmentPlanResult, error)
    68  
    69  	// VolumeParams returns the parameters for creating the volumes
    70  	// with the specified tags.
    71  	VolumeParams([]names.VolumeTag) ([]params.VolumeParamsResult, error)
    72  
    73  	// RemoveVolumeParams returns the parameters for destroying or
    74  	// releasing the volumes with the specified tags.
    75  	RemoveVolumeParams([]names.VolumeTag) ([]params.RemoveVolumeParamsResult, error)
    76  
    77  	// VolumeAttachmentParams returns the parameters for creating the
    78  	// volume attachments with the specified tags.
    79  	VolumeAttachmentParams([]params.MachineStorageId) ([]params.VolumeAttachmentParamsResult, error)
    80  
    81  	// SetVolumeInfo records the details of newly provisioned volumes.
    82  	SetVolumeInfo([]params.Volume) ([]params.ErrorResult, error)
    83  
    84  	// SetVolumeAttachmentInfo records the details of newly provisioned
    85  	// volume attachments.
    86  	SetVolumeAttachmentInfo([]params.VolumeAttachment) ([]params.ErrorResult, error)
    87  
    88  	CreateVolumeAttachmentPlans(volumeAttachmentPlans []params.VolumeAttachmentPlan) ([]params.ErrorResult, error)
    89  	RemoveVolumeAttachmentPlan([]params.MachineStorageId) ([]params.ErrorResult, error)
    90  	SetVolumeAttachmentPlanBlockInfo(volumeAttachmentPlans []params.VolumeAttachmentPlan) ([]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(scope names.Tag) (watcher.StringsWatcher, error)
    99  
   100  	// WatchFilesystemAttachments watches for changes to filesystem attachments
   101  	// that this storage provisioner is responsible for.
   102  	WatchFilesystemAttachments(scope names.Tag) (watcher.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  	// RemoveFilesystemParams returns the parameters for destroying or
   116  	// releasing the filesystems with the specified tags.
   117  	RemoveFilesystemParams([]names.FilesystemTag) ([]params.RemoveFilesystemParamsResult, error)
   118  
   119  	// FilesystemAttachmentParams returns the parameters for creating the
   120  	// filesystem attachments with the specified tags.
   121  	FilesystemAttachmentParams([]params.MachineStorageId) ([]params.FilesystemAttachmentParamsResult, error)
   122  
   123  	// SetFilesystemInfo records the details of newly provisioned filesystems.
   124  	SetFilesystemInfo([]params.Filesystem) ([]params.ErrorResult, error)
   125  
   126  	// SetFilesystemAttachmentInfo records the details of newly provisioned
   127  	// filesystem attachments.
   128  	SetFilesystemAttachmentInfo([]params.FilesystemAttachment) ([]params.ErrorResult, error)
   129  }
   130  
   131  // MachineAccessor defines an interface used to allow a storage provisioner
   132  // worker to perform machine related operations.
   133  type MachineAccessor interface {
   134  	// WatchMachine watches for changes to the specified machine.
   135  	WatchMachine(names.MachineTag) (watcher.NotifyWatcher, error)
   136  
   137  	// InstanceIds returns the instance IDs of each machine.
   138  	InstanceIds([]names.MachineTag) ([]params.StringResult, error)
   139  }
   140  
   141  // LifecycleManager defines an interface used to enable a storage provisioner
   142  // worker to perform lifcycle-related operations on storage entities and
   143  // attachments.
   144  type LifecycleManager interface {
   145  	// Life returns the lifecycle state of the specified entities.
   146  	Life([]names.Tag) ([]params.LifeResult, error)
   147  
   148  	// Remove removes the specified entities from state.
   149  	Remove([]names.Tag) ([]params.ErrorResult, error)
   150  
   151  	// AttachmentLife returns the lifecycle state of the specified
   152  	// machine/entity attachments.
   153  	AttachmentLife([]params.MachineStorageId) ([]params.LifeResult, error)
   154  
   155  	// RemoveAttachments removes the specified machine/entity attachments
   156  	// from state.
   157  	RemoveAttachments([]params.MachineStorageId) ([]params.ErrorResult, error)
   158  }
   159  
   160  // StatusSetter defines an interface used to set the status of entities.
   161  type StatusSetter interface {
   162  	SetStatus([]params.EntityStatusArgs) error
   163  }
   164  
   165  // NewStorageProvisioner returns a Worker which manages
   166  // provisioning (deprovisioning), and attachment (detachment)
   167  // of first-class volumes and filesystems.
   168  //
   169  // Machine-scoped storage workers will be provided with
   170  // a storage directory, while model-scoped workers
   171  // will not. If the directory path is non-empty, then it
   172  // will be passed to the storage source via its config.
   173  var NewStorageProvisioner = func(config Config) (worker.Worker, error) {
   174  	if err := config.Validate(); err != nil {
   175  		return nil, errors.Trace(err)
   176  	}
   177  	w := &storageProvisioner{
   178  		config: config,
   179  	}
   180  	err := catacomb.Invoke(catacomb.Plan{
   181  		Site: &w.catacomb,
   182  		Work: w.loop,
   183  	})
   184  	if err != nil {
   185  		return nil, errors.Trace(err)
   186  	}
   187  	return w, nil
   188  }
   189  
   190  type storageProvisioner struct {
   191  	catacomb catacomb.Catacomb
   192  	config   Config
   193  }
   194  
   195  // Kill implements Worker.Kill().
   196  func (w *storageProvisioner) Kill() {
   197  	w.catacomb.Kill(nil)
   198  }
   199  
   200  // Wait implements Worker.Wait().
   201  func (w *storageProvisioner) Wait() error {
   202  	err := w.catacomb.Wait()
   203  	return err
   204  }
   205  
   206  func (w *storageProvisioner) loop() error {
   207  	var (
   208  		volumesChanges               watcher.StringsChannel
   209  		filesystemsChanges           watcher.StringsChannel
   210  		volumeAttachmentsChanges     watcher.MachineStorageIdsChannel
   211  		volumeAttachmentPlansChanges watcher.MachineStorageIdsChannel
   212  		filesystemAttachmentsChanges watcher.MachineStorageIdsChannel
   213  		machineBlockDevicesChanges   <-chan struct{}
   214  	)
   215  	machineChanges := make(chan names.MachineTag)
   216  
   217  	// Machine-scoped provisioners need to watch block devices, to create
   218  	// volume-backed filesystems.
   219  	if machineTag, ok := w.config.Scope.(names.MachineTag); ok {
   220  		machineBlockDevicesWatcher, err := w.config.Volumes.WatchBlockDevices(machineTag)
   221  		if err != nil {
   222  			return errors.Annotate(err, "watching block devices")
   223  		}
   224  		if err := w.catacomb.Add(machineBlockDevicesWatcher); err != nil {
   225  			return errors.Trace(err)
   226  		}
   227  		machineBlockDevicesChanges = machineBlockDevicesWatcher.Changes()
   228  
   229  		volumeAttachmentPlansWatcher, err := w.config.Volumes.WatchVolumeAttachmentPlans(machineTag)
   230  		if err != nil {
   231  			return errors.Annotate(err, "watching volume attachment plans")
   232  		}
   233  		if err := w.catacomb.Add(volumeAttachmentPlansWatcher); err != nil {
   234  			return errors.Trace(err)
   235  		}
   236  
   237  		volumeAttachmentPlansChanges = volumeAttachmentPlansWatcher.Changes()
   238  	}
   239  
   240  	ctx := context{
   241  		kill:                                 w.catacomb.Kill,
   242  		addWorker:                            w.catacomb.Add,
   243  		config:                               w.config,
   244  		volumes:                              make(map[names.VolumeTag]storage.Volume),
   245  		volumeAttachments:                    make(map[params.MachineStorageId]storage.VolumeAttachment),
   246  		volumeBlockDevices:                   make(map[names.VolumeTag]storage.BlockDevice),
   247  		filesystems:                          make(map[names.FilesystemTag]storage.Filesystem),
   248  		filesystemAttachments:                make(map[params.MachineStorageId]storage.FilesystemAttachment),
   249  		machines:                             make(map[names.MachineTag]*machineWatcher),
   250  		machineChanges:                       machineChanges,
   251  		schedule:                             schedule.NewSchedule(w.config.Clock),
   252  		incompleteVolumeParams:               make(map[names.VolumeTag]storage.VolumeParams),
   253  		incompleteVolumeAttachmentParams:     make(map[params.MachineStorageId]storage.VolumeAttachmentParams),
   254  		incompleteFilesystemParams:           make(map[names.FilesystemTag]storage.FilesystemParams),
   255  		incompleteFilesystemAttachmentParams: make(map[params.MachineStorageId]storage.FilesystemAttachmentParams),
   256  		pendingVolumeBlockDevices:            names.NewSet(),
   257  	}
   258  	ctx.managedFilesystemSource = newManagedFilesystemSource(
   259  		ctx.volumeBlockDevices, ctx.filesystems,
   260  	)
   261  	// Units don't use managed volume backed filesystems.
   262  	if ctx.isApplicationKind() {
   263  		ctx.managedFilesystemSource = &noopFilesystemSource{}
   264  	}
   265  
   266  	// Units don't have unit-scoped volumes - all volumes are
   267  	// associated with the model (namespace).
   268  	if !ctx.isApplicationKind() {
   269  		volumesWatcher, err := w.config.Volumes.WatchVolumes(w.config.Scope)
   270  		if err != nil {
   271  			return errors.Annotate(err, "watching volumes")
   272  		}
   273  		if err := w.catacomb.Add(volumesWatcher); err != nil {
   274  			return errors.Trace(err)
   275  		}
   276  		volumesChanges = volumesWatcher.Changes()
   277  	}
   278  
   279  	filesystemsWatcher, err := w.config.Filesystems.WatchFilesystems(w.config.Scope)
   280  	if err != nil {
   281  		return errors.Annotate(err, "watching filesystems")
   282  	}
   283  	if err := w.catacomb.Add(filesystemsWatcher); err != nil {
   284  		return errors.Trace(err)
   285  	}
   286  	filesystemsChanges = filesystemsWatcher.Changes()
   287  
   288  	volumeAttachmentsWatcher, err := w.config.Volumes.WatchVolumeAttachments(w.config.Scope)
   289  	if err != nil {
   290  		return errors.Annotate(err, "watching volume attachments")
   291  	}
   292  	if err := w.catacomb.Add(volumeAttachmentsWatcher); err != nil {
   293  		return errors.Trace(err)
   294  	}
   295  	volumeAttachmentsChanges = volumeAttachmentsWatcher.Changes()
   296  
   297  	filesystemAttachmentsWatcher, err := w.config.Filesystems.WatchFilesystemAttachments(w.config.Scope)
   298  	if err != nil {
   299  		return errors.Annotate(err, "watching filesystem attachments")
   300  	}
   301  	if err := w.catacomb.Add(filesystemAttachmentsWatcher); err != nil {
   302  		return errors.Trace(err)
   303  	}
   304  	filesystemAttachmentsChanges = filesystemAttachmentsWatcher.Changes()
   305  
   306  	for {
   307  
   308  		// Check if block devices need to be refreshed.
   309  		if err := processPendingVolumeBlockDevices(&ctx); err != nil {
   310  			return errors.Annotate(err, "processing pending block devices")
   311  		}
   312  
   313  		select {
   314  		case <-w.catacomb.Dying():
   315  			return w.catacomb.ErrDying()
   316  		case changes, ok := <-volumesChanges:
   317  			if !ok {
   318  				return errors.New("volumes watcher closed")
   319  			}
   320  			if err := volumesChanged(&ctx, changes); err != nil {
   321  				return errors.Trace(err)
   322  			}
   323  		case changes, ok := <-volumeAttachmentsChanges:
   324  			if !ok {
   325  				return errors.New("volume attachments watcher closed")
   326  			}
   327  			// Process volume changes before volume attachments changes.
   328  			// This is because volume attachments are dependent on
   329  			// volumes, and reveals itself during a reboot of a machine. All
   330  			// the watcher changes come at once, but the order of the select
   331  			// case statements is not guaranteed.
   332  			if err := w.processDependentChanges(&ctx, volumesChanges, volumesChanged); err != nil {
   333  				return errors.Trace(err)
   334  			}
   335  			if err := volumeAttachmentsChanged(&ctx, changes); err != nil {
   336  				return errors.Trace(err)
   337  			}
   338  		case changes, ok := <-volumeAttachmentPlansChanges:
   339  			if !ok {
   340  				return errors.New("volume attachment plans watcher closed")
   341  			}
   342  			if err := volumeAttachmentPlansChanged(&ctx, changes); err != nil {
   343  				return errors.Trace(err)
   344  			}
   345  		case changes, ok := <-filesystemsChanges:
   346  			if !ok {
   347  				return errors.New("filesystems watcher closed")
   348  			}
   349  			if err := filesystemsChanged(&ctx, changes); err != nil {
   350  				return errors.Trace(err)
   351  			}
   352  		case changes, ok := <-filesystemAttachmentsChanges:
   353  			if !ok {
   354  				return errors.New("filesystem attachments watcher closed")
   355  			}
   356  			// Process filesystem changes before filesystem attachments changes.
   357  			// This is because filesystem attachments are dependent on
   358  			// filesystems, and reveals itself during a reboot of a machine. All
   359  			// the watcher changes come at once, but the order of the select
   360  			// case statements is not guaranteed.
   361  			if err := w.processDependentChanges(&ctx, filesystemsChanges, filesystemsChanged); err != nil {
   362  				return errors.Trace(err)
   363  			}
   364  			if err := filesystemAttachmentsChanged(&ctx, changes); err != nil {
   365  				return errors.Trace(err)
   366  			}
   367  		case _, ok := <-machineBlockDevicesChanges:
   368  			if !ok {
   369  				return errors.New("machine block devices watcher closed")
   370  			}
   371  			if err := machineBlockDevicesChanged(&ctx); err != nil {
   372  				return errors.Trace(err)
   373  			}
   374  		case machineTag := <-machineChanges:
   375  			if err := refreshMachine(&ctx, machineTag); err != nil {
   376  				return errors.Trace(err)
   377  			}
   378  		case <-ctx.schedule.Next():
   379  			// Ready to pick something(s) off the pending queue.
   380  			if err := processSchedule(&ctx); err != nil {
   381  				return errors.Trace(err)
   382  			}
   383  		}
   384  	}
   385  }
   386  
   387  // processDependentChanges processes changes from a watcher strings channel. If
   388  // there are any changes, it calls the given function, repeating until there are
   389  // no more changes.
   390  // If there are no changes, it returns with no error.
   391  func (w *storageProvisioner) processDependentChanges(ctx *context, source watcher.StringsChannel, fn func(*context, []string) error) error {
   392  	for {
   393  		select {
   394  		case <-w.catacomb.Dying():
   395  			return w.catacomb.ErrDying()
   396  		case changes, ok := <-source:
   397  			if !ok {
   398  				return errors.New("watcher closed")
   399  			}
   400  			if err := fn(ctx, changes); err != nil {
   401  				return errors.Trace(err)
   402  			}
   403  		case <-time.After(defaultDependentChangesTimeout):
   404  			// Nothing to do, we've waited long enough.
   405  			return nil
   406  		}
   407  	}
   408  }
   409  
   410  // processSchedule executes scheduled operations.
   411  func processSchedule(ctx *context) error {
   412  	ready := ctx.schedule.Ready(ctx.config.Clock.Now())
   413  	createVolumeOps := make(map[names.VolumeTag]*createVolumeOp)
   414  	removeVolumeOps := make(map[names.VolumeTag]*removeVolumeOp)
   415  	attachVolumeOps := make(map[params.MachineStorageId]*attachVolumeOp)
   416  	detachVolumeOps := make(map[params.MachineStorageId]*detachVolumeOp)
   417  	createFilesystemOps := make(map[names.FilesystemTag]*createFilesystemOp)
   418  	removeFilesystemOps := make(map[names.FilesystemTag]*removeFilesystemOp)
   419  	attachFilesystemOps := make(map[params.MachineStorageId]*attachFilesystemOp)
   420  	detachFilesystemOps := make(map[params.MachineStorageId]*detachFilesystemOp)
   421  	for _, item := range ready {
   422  		op := item.(scheduleOp)
   423  		key := op.key()
   424  		switch op := op.(type) {
   425  		case *createVolumeOp:
   426  			createVolumeOps[key.(names.VolumeTag)] = op
   427  		case *removeVolumeOp:
   428  			removeVolumeOps[key.(names.VolumeTag)] = op
   429  		case *attachVolumeOp:
   430  			attachVolumeOps[key.(params.MachineStorageId)] = op
   431  		case *detachVolumeOp:
   432  			detachVolumeOps[key.(params.MachineStorageId)] = op
   433  		case *createFilesystemOp:
   434  			createFilesystemOps[key.(names.FilesystemTag)] = op
   435  		case *removeFilesystemOp:
   436  			removeFilesystemOps[key.(names.FilesystemTag)] = op
   437  		case *attachFilesystemOp:
   438  			attachFilesystemOps[key.(params.MachineStorageId)] = op
   439  		case *detachFilesystemOp:
   440  			detachFilesystemOps[key.(params.MachineStorageId)] = op
   441  		}
   442  	}
   443  	if len(removeVolumeOps) > 0 {
   444  		if err := removeVolumes(ctx, removeVolumeOps); err != nil {
   445  			return errors.Annotate(err, "removing volumes")
   446  		}
   447  	}
   448  	if len(createVolumeOps) > 0 {
   449  		if err := createVolumes(ctx, createVolumeOps); err != nil {
   450  			return errors.Annotate(err, "creating volumes")
   451  		}
   452  	}
   453  	if len(detachVolumeOps) > 0 {
   454  		if err := detachVolumes(ctx, detachVolumeOps); err != nil {
   455  			return errors.Annotate(err, "detaching volumes")
   456  		}
   457  	}
   458  	if len(attachVolumeOps) > 0 {
   459  		if err := attachVolumes(ctx, attachVolumeOps); err != nil {
   460  			return errors.Annotate(err, "attaching volumes")
   461  		}
   462  	}
   463  	if len(removeFilesystemOps) > 0 {
   464  		if err := removeFilesystems(ctx, removeFilesystemOps); err != nil {
   465  			return errors.Annotate(err, "removing filesystems")
   466  		}
   467  	}
   468  	if len(createFilesystemOps) > 0 {
   469  		if err := createFilesystems(ctx, createFilesystemOps); err != nil {
   470  			return errors.Annotate(err, "creating filesystems")
   471  		}
   472  	}
   473  	if len(detachFilesystemOps) > 0 {
   474  		if err := detachFilesystems(ctx, detachFilesystemOps); err != nil {
   475  			return errors.Annotate(err, "detaching filesystems")
   476  		}
   477  	}
   478  	if len(attachFilesystemOps) > 0 {
   479  		if err := attachFilesystems(ctx, attachFilesystemOps); err != nil {
   480  			return errors.Annotate(err, "attaching filesystems")
   481  		}
   482  	}
   483  	return nil
   484  }
   485  
   486  type context struct {
   487  	kill      func(error)
   488  	addWorker func(worker.Worker) error
   489  	config    Config
   490  
   491  	// volumes contains information about provisioned volumes.
   492  	volumes map[names.VolumeTag]storage.Volume
   493  
   494  	// volumeAttachments contains information about attached volumes.
   495  	volumeAttachments map[params.MachineStorageId]storage.VolumeAttachment
   496  
   497  	// volumeBlockDevices contains information about block devices
   498  	// corresponding to volumes attached to the scope-machine. This
   499  	// is only used by the machine-scoped storage provisioner.
   500  	volumeBlockDevices map[names.VolumeTag]storage.BlockDevice
   501  
   502  	// filesystems contains information about provisioned filesystems.
   503  	filesystems map[names.FilesystemTag]storage.Filesystem
   504  
   505  	// filesystemAttachments contains information about attached filesystems.
   506  	filesystemAttachments map[params.MachineStorageId]storage.FilesystemAttachment
   507  
   508  	// machines contains information about machines in the worker's scope.
   509  	machines map[names.MachineTag]*machineWatcher
   510  
   511  	// machineChanges is a channel that machine watchers will send to once
   512  	// their machine is known to have been provisioned.
   513  	machineChanges chan<- names.MachineTag
   514  
   515  	// schedule is the schedule of storage operations.
   516  	schedule *schedule.Schedule
   517  
   518  	// incompleteVolumeParams contains incomplete parameters for volumes.
   519  	//
   520  	// Volume parameters are incomplete when they lack information about
   521  	// the initial attachment. Once the initial attachment information is
   522  	// available, the parameters are removed from this map and a volume
   523  	// creation operation is scheduled.
   524  	incompleteVolumeParams map[names.VolumeTag]storage.VolumeParams
   525  
   526  	// incompleteVolumeAttachmentParams contains incomplete parameters
   527  	// for volume attachments
   528  	//
   529  	// Volume attachment parameters are incomplete when they lack
   530  	// information about the associated volume or machine. Once this
   531  	// information is available, the parameters are removed from this
   532  	// map and a volume attachment operation is scheduled.
   533  	incompleteVolumeAttachmentParams map[params.MachineStorageId]storage.VolumeAttachmentParams
   534  
   535  	// incompleteFilesystemParams contains incomplete parameters for
   536  	// filesystems.
   537  	//
   538  	// Filesystem parameters are incomplete when they lack information
   539  	// about the initial attachment. Once the initial attachment
   540  	// information is available, the parameters are removed from this
   541  	// map and a filesystem creation operation is scheduled.
   542  	incompleteFilesystemParams map[names.FilesystemTag]storage.FilesystemParams
   543  
   544  	// incompleteFilesystemAttachmentParams contains incomplete parameters
   545  	// for filesystem attachments
   546  	//
   547  	// Filesystem attachment parameters are incomplete when they lack
   548  	// information about the associated filesystem or machine. Once this
   549  	// information is available, the parameters are removed from this
   550  	// map and a filesystem attachment operation is scheduled.
   551  	incompleteFilesystemAttachmentParams map[params.MachineStorageId]storage.FilesystemAttachmentParams
   552  
   553  	// pendingVolumeBlockDevices contains the tags of volumes about whose
   554  	// block devices we wish to enquire.
   555  	pendingVolumeBlockDevices names.Set
   556  
   557  	// managedFilesystemSource is a storage.FilesystemSource that
   558  	// manages filesystems backed by volumes attached to the host
   559  	// machine.
   560  	managedFilesystemSource storage.FilesystemSource
   561  }
   562  
   563  func (c *context) isApplicationKind() bool {
   564  	return c.config.Scope.Kind() == names.ApplicationTagKind
   565  }