github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/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  	"github.com/juju/errors"
     8  	"github.com/juju/loggo"
     9  	"github.com/juju/names"
    10  	"github.com/juju/utils/set"
    11  	"launchpad.net/tomb"
    12  
    13  	apiwatcher "github.com/juju/juju/api/watcher"
    14  	"github.com/juju/juju/apiserver/params"
    15  	"github.com/juju/juju/environs/config"
    16  	"github.com/juju/juju/state/watcher"
    17  	"github.com/juju/juju/storage"
    18  	"github.com/juju/juju/storage/provider"
    19  	"github.com/juju/juju/worker"
    20  )
    21  
    22  var logger = loggo.GetLogger("juju.worker.storageprovisioner")
    23  
    24  var newManagedFilesystemSource = provider.NewManagedFilesystemSource
    25  
    26  // VolumeAccessor defines an interface used to allow a storage provisioner
    27  // worker to perform volume related operations.
    28  type VolumeAccessor interface {
    29  	// WatchBlockDevices watches for changes to the block devices of the
    30  	// specified machine.
    31  	WatchBlockDevices(names.MachineTag) (apiwatcher.NotifyWatcher, error)
    32  
    33  	// WatchVolumes watches for changes to volumes that this storage
    34  	// provisioner is responsible for.
    35  	WatchVolumes() (apiwatcher.StringsWatcher, error)
    36  
    37  	// WatchVolumeAttachments watches for changes to volume attachments
    38  	// that this storage provisioner is responsible for.
    39  	WatchVolumeAttachments() (apiwatcher.MachineStorageIdsWatcher, error)
    40  
    41  	// Volumes returns details of volumes with the specified tags.
    42  	Volumes([]names.VolumeTag) ([]params.VolumeResult, error)
    43  
    44  	// VolumeBlockDevices returns details of block devices corresponding to
    45  	// the specified volume attachment IDs.
    46  	VolumeBlockDevices([]params.MachineStorageId) ([]params.BlockDeviceResult, error)
    47  
    48  	// VolumeAttachments returns details of volume attachments with
    49  	// the specified tags.
    50  	VolumeAttachments([]params.MachineStorageId) ([]params.VolumeAttachmentResult, error)
    51  
    52  	// VolumeParams returns the parameters for creating the volumes
    53  	// with the specified tags.
    54  	VolumeParams([]names.VolumeTag) ([]params.VolumeParamsResult, error)
    55  
    56  	// VolumeAttachmentParams returns the parameters for creating the
    57  	// volume attachments with the specified tags.
    58  	VolumeAttachmentParams([]params.MachineStorageId) ([]params.VolumeAttachmentParamsResult, error)
    59  
    60  	// SetVolumeInfo records the details of newly provisioned volumes.
    61  	SetVolumeInfo([]params.Volume) ([]params.ErrorResult, error)
    62  
    63  	// SetVolumeAttachmentInfo records the details of newly provisioned
    64  	// volume attachments.
    65  	SetVolumeAttachmentInfo([]params.VolumeAttachment) ([]params.ErrorResult, error)
    66  }
    67  
    68  // FilesystemAccessor defines an interface used to allow a storage provisioner
    69  // worker to perform filesystem related operations.
    70  type FilesystemAccessor interface {
    71  	// WatchFilesystems watches for changes to filesystems that this
    72  	// storage provisioner is responsible for.
    73  	WatchFilesystems() (apiwatcher.StringsWatcher, error)
    74  
    75  	// WatchFilesystemAttachments watches for changes to filesystem attachments
    76  	// that this storage provisioner is responsible for.
    77  	WatchFilesystemAttachments() (apiwatcher.MachineStorageIdsWatcher, error)
    78  
    79  	// Filesystems returns details of filesystems with the specified tags.
    80  	Filesystems([]names.FilesystemTag) ([]params.FilesystemResult, error)
    81  
    82  	// FilesystemAttachments returns details of filesystem attachments with
    83  	// the specified tags.
    84  	FilesystemAttachments([]params.MachineStorageId) ([]params.FilesystemAttachmentResult, error)
    85  
    86  	// FilesystemParams returns the parameters for creating the filesystems
    87  	// with the specified tags.
    88  	FilesystemParams([]names.FilesystemTag) ([]params.FilesystemParamsResult, error)
    89  
    90  	// FilesystemAttachmentParams returns the parameters for creating the
    91  	// filesystem attachments with the specified tags.
    92  	FilesystemAttachmentParams([]params.MachineStorageId) ([]params.FilesystemAttachmentParamsResult, error)
    93  
    94  	// SetFilesystemInfo records the details of newly provisioned filesystems.
    95  	SetFilesystemInfo([]params.Filesystem) ([]params.ErrorResult, error)
    96  
    97  	// SetFilesystemAttachmentInfo records the details of newly provisioned
    98  	// filesystem attachments.
    99  	SetFilesystemAttachmentInfo([]params.FilesystemAttachment) ([]params.ErrorResult, error)
   100  }
   101  
   102  // MachineAccessor defines an interface used to allow a storage provisioner
   103  // worker to perform machine related operations.
   104  type MachineAccessor interface {
   105  	// WatchMachine watches for changes to the specified machine.
   106  	WatchMachine(names.MachineTag) (apiwatcher.NotifyWatcher, error)
   107  
   108  	// InstanceIds returns the instance IDs of each machine.
   109  	InstanceIds([]names.MachineTag) ([]params.StringResult, error)
   110  }
   111  
   112  // LifecycleManager defines an interface used to enable a storage provisioner
   113  // worker to perform lifcycle-related operations on storage entities and
   114  // attachments.
   115  type LifecycleManager interface {
   116  	// Life returns the lifecycle state of the specified entities.
   117  	Life([]names.Tag) ([]params.LifeResult, error)
   118  
   119  	// Remove removes the specified entities from state.
   120  	Remove([]names.Tag) ([]params.ErrorResult, error)
   121  
   122  	// AttachmentLife returns the lifecycle state of the specified
   123  	// machine/entity attachments.
   124  	AttachmentLife([]params.MachineStorageId) ([]params.LifeResult, error)
   125  
   126  	// RemoveAttachments removes the specified machine/entity attachments
   127  	// from state.
   128  	RemoveAttachments([]params.MachineStorageId) ([]params.ErrorResult, error)
   129  }
   130  
   131  // EnvironAccessor defines an interface used to enable a storage provisioner
   132  // worker to watch changes to and read environment config, to use when
   133  // provisioning storage.
   134  type EnvironAccessor interface {
   135  	// WatchForEnvironConfigChanges returns a watcher that will be notified
   136  	// whenever the environment config changes in state.
   137  	WatchForEnvironConfigChanges() (apiwatcher.NotifyWatcher, error)
   138  
   139  	// EnvironConfig returns the current environment config.
   140  	EnvironConfig() (*config.Config, error)
   141  }
   142  
   143  // NewStorageProvisioner returns a Worker which manages
   144  // provisioning (deprovisioning), and attachment (detachment)
   145  // of first-class volumes and filesystems.
   146  //
   147  // Machine-scoped storage workers will be provided with
   148  // a storage directory, while environment-scoped workers
   149  // will not. If the directory path is non-empty, then it
   150  // will be passed to the storage source via its config.
   151  func NewStorageProvisioner(
   152  	scope names.Tag,
   153  	storageDir string,
   154  	v VolumeAccessor,
   155  	f FilesystemAccessor,
   156  	l LifecycleManager,
   157  	e EnvironAccessor,
   158  	m MachineAccessor,
   159  ) worker.Worker {
   160  	w := &storageprovisioner{
   161  		scope:       scope,
   162  		storageDir:  storageDir,
   163  		volumes:     v,
   164  		filesystems: f,
   165  		life:        l,
   166  		environ:     e,
   167  		machines:    m,
   168  	}
   169  	go func() {
   170  		defer w.tomb.Done()
   171  		err := w.loop()
   172  		if err != tomb.ErrDying {
   173  			logger.Errorf("%s", err)
   174  		}
   175  		w.tomb.Kill(err)
   176  	}()
   177  	return w
   178  }
   179  
   180  type storageprovisioner struct {
   181  	tomb        tomb.Tomb
   182  	scope       names.Tag
   183  	storageDir  string
   184  	volumes     VolumeAccessor
   185  	filesystems FilesystemAccessor
   186  	life        LifecycleManager
   187  	environ     EnvironAccessor
   188  	machines    MachineAccessor
   189  }
   190  
   191  // Kill implements Worker.Kill().
   192  func (w *storageprovisioner) Kill() {
   193  	w.tomb.Kill(nil)
   194  }
   195  
   196  // Wait implements Worker.Wait().
   197  func (w *storageprovisioner) Wait() error {
   198  	return w.tomb.Wait()
   199  }
   200  
   201  func (w *storageprovisioner) loop() error {
   202  	var environConfigChanges <-chan struct{}
   203  	var volumesWatcher apiwatcher.StringsWatcher
   204  	var filesystemsWatcher apiwatcher.StringsWatcher
   205  	var volumesChanges <-chan []string
   206  	var filesystemsChanges <-chan []string
   207  	var volumeAttachmentsWatcher apiwatcher.MachineStorageIdsWatcher
   208  	var filesystemAttachmentsWatcher apiwatcher.MachineStorageIdsWatcher
   209  	var volumeAttachmentsChanges <-chan []params.MachineStorageId
   210  	var filesystemAttachmentsChanges <-chan []params.MachineStorageId
   211  	var machineBlockDevicesWatcher apiwatcher.NotifyWatcher
   212  	var machineBlockDevicesChanges <-chan struct{}
   213  	machineChanges := make(chan names.MachineTag)
   214  
   215  	environConfigWatcher, err := w.environ.WatchForEnvironConfigChanges()
   216  	if err != nil {
   217  		return errors.Annotate(err, "watching environ config")
   218  	}
   219  	defer watcher.Stop(environConfigWatcher, &w.tomb)
   220  	environConfigChanges = environConfigWatcher.Changes()
   221  
   222  	// Machine-scoped provisioners need to watch block devices, to create
   223  	// volume-backed filesystems.
   224  	if machineTag, ok := w.scope.(names.MachineTag); ok {
   225  		machineBlockDevicesWatcher, err = w.volumes.WatchBlockDevices(machineTag)
   226  		if err != nil {
   227  			return errors.Annotate(err, "watching block devices")
   228  		}
   229  		defer watcher.Stop(machineBlockDevicesWatcher, &w.tomb)
   230  		machineBlockDevicesChanges = machineBlockDevicesWatcher.Changes()
   231  	}
   232  
   233  	// The other watchers are started dynamically; stop only if started.
   234  	defer w.maybeStopWatcher(volumesWatcher)
   235  	defer w.maybeStopWatcher(volumeAttachmentsWatcher)
   236  	defer w.maybeStopWatcher(filesystemsWatcher)
   237  	defer w.maybeStopWatcher(filesystemAttachmentsWatcher)
   238  
   239  	startWatchers := func() error {
   240  		var err error
   241  		volumesWatcher, err = w.volumes.WatchVolumes()
   242  		if err != nil {
   243  			return errors.Annotate(err, "watching volumes")
   244  		}
   245  		filesystemsWatcher, err = w.filesystems.WatchFilesystems()
   246  		if err != nil {
   247  			return errors.Annotate(err, "watching filesystems")
   248  		}
   249  		volumeAttachmentsWatcher, err = w.volumes.WatchVolumeAttachments()
   250  		if err != nil {
   251  			return errors.Annotate(err, "watching volume attachments")
   252  		}
   253  		filesystemAttachmentsWatcher, err = w.filesystems.WatchFilesystemAttachments()
   254  		if err != nil {
   255  			return errors.Annotate(err, "watching filesystem attachments")
   256  		}
   257  		volumesChanges = volumesWatcher.Changes()
   258  		filesystemsChanges = filesystemsWatcher.Changes()
   259  		volumeAttachmentsChanges = volumeAttachmentsWatcher.Changes()
   260  		filesystemAttachmentsChanges = filesystemAttachmentsWatcher.Changes()
   261  		return nil
   262  	}
   263  
   264  	ctx := context{
   265  		scope:                             w.scope,
   266  		storageDir:                        w.storageDir,
   267  		volumeAccessor:                    w.volumes,
   268  		filesystemAccessor:                w.filesystems,
   269  		life:                              w.life,
   270  		machineAccessor:                   w.machines,
   271  		volumes:                           make(map[names.VolumeTag]storage.Volume),
   272  		volumeAttachments:                 make(map[params.MachineStorageId]storage.VolumeAttachment),
   273  		volumeBlockDevices:                make(map[names.VolumeTag]storage.BlockDevice),
   274  		filesystems:                       make(map[names.FilesystemTag]storage.Filesystem),
   275  		filesystemAttachments:             make(map[params.MachineStorageId]storage.FilesystemAttachment),
   276  		machines:                          make(map[names.MachineTag]*machineWatcher),
   277  		machineChanges:                    machineChanges,
   278  		pendingVolumes:                    make(map[names.VolumeTag]storage.VolumeParams),
   279  		pendingVolumeAttachments:          make(map[params.MachineStorageId]storage.VolumeAttachmentParams),
   280  		pendingVolumeBlockDevices:         make(set.Tags),
   281  		pendingFilesystems:                make(map[names.FilesystemTag]storage.FilesystemParams),
   282  		pendingFilesystemAttachments:      make(map[params.MachineStorageId]storage.FilesystemAttachmentParams),
   283  		pendingDyingFilesystemAttachments: make(map[params.MachineStorageId]storage.FilesystemAttachmentParams),
   284  	}
   285  	ctx.managedFilesystemSource = newManagedFilesystemSource(
   286  		ctx.volumeBlockDevices, ctx.filesystems,
   287  	)
   288  	defer func() {
   289  		for _, w := range ctx.machines {
   290  			w.stop()
   291  		}
   292  	}()
   293  
   294  	for {
   295  		// Check if any pending operations can be fulfilled.
   296  		if err := processPending(&ctx); err != nil {
   297  			return errors.Trace(err)
   298  		}
   299  
   300  		select {
   301  		case <-w.tomb.Dying():
   302  			return tomb.ErrDying
   303  		case _, ok := <-environConfigChanges:
   304  			if !ok {
   305  				return watcher.EnsureErr(environConfigWatcher)
   306  			}
   307  			environConfig, err := w.environ.EnvironConfig()
   308  			if err != nil {
   309  				return errors.Annotate(err, "getting environ config")
   310  			}
   311  			if ctx.environConfig == nil {
   312  				// We've received the initial environ config,
   313  				// so we can begin provisioning storage.
   314  				if err := startWatchers(); err != nil {
   315  					return err
   316  				}
   317  			}
   318  			ctx.environConfig = environConfig
   319  		case changes, ok := <-volumesChanges:
   320  			if !ok {
   321  				return watcher.EnsureErr(volumesWatcher)
   322  			}
   323  			if err := volumesChanged(&ctx, changes); err != nil {
   324  				return errors.Trace(err)
   325  			}
   326  		case changes, ok := <-volumeAttachmentsChanges:
   327  			if !ok {
   328  				return watcher.EnsureErr(volumeAttachmentsWatcher)
   329  			}
   330  			if err := volumeAttachmentsChanged(&ctx, changes); err != nil {
   331  				return errors.Trace(err)
   332  			}
   333  		case changes, ok := <-filesystemsChanges:
   334  			if !ok {
   335  				return watcher.EnsureErr(filesystemsWatcher)
   336  			}
   337  			if err := filesystemsChanged(&ctx, changes); err != nil {
   338  				return errors.Trace(err)
   339  			}
   340  		case changes, ok := <-filesystemAttachmentsChanges:
   341  			if !ok {
   342  				return watcher.EnsureErr(filesystemAttachmentsWatcher)
   343  			}
   344  			if err := filesystemAttachmentsChanged(&ctx, changes); err != nil {
   345  				return errors.Trace(err)
   346  			}
   347  		case _, ok := <-machineBlockDevicesChanges:
   348  			if !ok {
   349  				return watcher.EnsureErr(machineBlockDevicesWatcher)
   350  			}
   351  			if err := machineBlockDevicesChanged(&ctx); err != nil {
   352  				return errors.Trace(err)
   353  			}
   354  		case machineTag := <-machineChanges:
   355  			if err := refreshMachine(&ctx, machineTag); err != nil {
   356  				return errors.Trace(err)
   357  			}
   358  		}
   359  	}
   360  }
   361  
   362  // processPending checks if the pending operations' prerequisites have
   363  // been met, and processes them if so.
   364  func processPending(ctx *context) error {
   365  	if err := processPendingVolumes(ctx); err != nil {
   366  		return errors.Annotate(err, "processing pending volumes")
   367  	}
   368  	if err := processPendingVolumeAttachments(ctx); err != nil {
   369  		return errors.Annotate(err, "processing pending volume attachments")
   370  	}
   371  	if err := processPendingVolumeBlockDevices(ctx); err != nil {
   372  		return errors.Annotate(err, "processing pending block devices")
   373  	}
   374  	if err := processPendingFilesystems(ctx); err != nil {
   375  		return errors.Annotate(err, "processing pending filesystems")
   376  	}
   377  	if err := processPendingDyingFilesystemAttachments(ctx); err != nil {
   378  		return errors.Annotate(err, "processing pending, dying filesystem attachments")
   379  	}
   380  	if err := processPendingFilesystemAttachments(ctx); err != nil {
   381  		return errors.Annotate(err, "processing pending filesystem attachments")
   382  	}
   383  	return nil
   384  }
   385  
   386  func (p *storageprovisioner) maybeStopWatcher(w watcher.Stopper) {
   387  	if w != nil {
   388  		watcher.Stop(w, &p.tomb)
   389  	}
   390  }
   391  
   392  type context struct {
   393  	scope              names.Tag
   394  	environConfig      *config.Config
   395  	storageDir         string
   396  	volumeAccessor     VolumeAccessor
   397  	filesystemAccessor FilesystemAccessor
   398  	life               LifecycleManager
   399  	machineAccessor    MachineAccessor
   400  
   401  	// volumes contains information about provisioned volumes.
   402  	volumes map[names.VolumeTag]storage.Volume
   403  
   404  	// volumeAttachments contains information about attached volumes.
   405  	volumeAttachments map[params.MachineStorageId]storage.VolumeAttachment
   406  
   407  	// volumeBlockDevices contains information about block devices
   408  	// corresponding to volumes attached to the scope-machine. This
   409  	// is only used by the machine-scoped storage provisioner.
   410  	volumeBlockDevices map[names.VolumeTag]storage.BlockDevice
   411  
   412  	// filesystems contains information about provisioned filesystems.
   413  	filesystems map[names.FilesystemTag]storage.Filesystem
   414  
   415  	// filesystemAttachments contains information about attached filesystems.
   416  	filesystemAttachments map[params.MachineStorageId]storage.FilesystemAttachment
   417  
   418  	// machines contains information about machines in the worker's scope.
   419  	machines map[names.MachineTag]*machineWatcher
   420  
   421  	// machineChanges is a channel that machine watchers will send to once
   422  	// their machine is known to have been provisioned.
   423  	machineChanges chan<- names.MachineTag
   424  
   425  	// pendingVolumes contains parameters for volumes that are yet to be
   426  	// created.
   427  	pendingVolumes map[names.VolumeTag]storage.VolumeParams
   428  
   429  	// pendingVolumeAttachments contains parameters for volume attachments
   430  	// that are yet to be created.
   431  	pendingVolumeAttachments map[params.MachineStorageId]storage.VolumeAttachmentParams
   432  
   433  	// pendingVolumeBlockDevices contains the tags of volumes about whose
   434  	// block devices we wish to enquire.
   435  	pendingVolumeBlockDevices set.Tags
   436  
   437  	// pendingFilesystems contains parameters for filesystems that are
   438  	// yet to be created.
   439  	pendingFilesystems map[names.FilesystemTag]storage.FilesystemParams
   440  
   441  	// pendingFilesystemAttachments contains parameters for filesystem attachments
   442  	// that are yet to be created.
   443  	pendingFilesystemAttachments map[params.MachineStorageId]storage.FilesystemAttachmentParams
   444  
   445  	// pendingDyingFilesystemAttachments contains parameters for filesystem attachments
   446  	// that are to be destroyed.
   447  	pendingDyingFilesystemAttachments map[params.MachineStorageId]storage.FilesystemAttachmentParams
   448  
   449  	// managedFilesystemSource is a storage.FilesystemSource that
   450  	// manages filesystems backed by volumes attached to the host
   451  	// machine.
   452  	managedFilesystemSource storage.FilesystemSource
   453  }