github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/worker/uniter/storage/resolver.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package storage
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"gopkg.in/juju/charm.v6-unstable/hooks"
     9  	"gopkg.in/juju/names.v2"
    10  
    11  	"github.com/juju/juju/apiserver/params"
    12  	"github.com/juju/juju/storage"
    13  	"github.com/juju/juju/worker/uniter/hook"
    14  	"github.com/juju/juju/worker/uniter/operation"
    15  	"github.com/juju/juju/worker/uniter/remotestate"
    16  	"github.com/juju/juju/worker/uniter/resolver"
    17  )
    18  
    19  // StorageResolverOperations instances know how to make operations
    20  // required by the resolver.
    21  type StorageResolverOperations interface {
    22  	NewUpdateStorage(tags []names.StorageTag) (operation.Operation, error)
    23  	NewRunHook(hookInfo hook.Info) (operation.Operation, error)
    24  }
    25  
    26  type storageResolver struct {
    27  	storage *Attachments
    28  	dying   bool
    29  	life    map[names.StorageTag]params.Life
    30  }
    31  
    32  // NewResolver returns a new storage resolver.
    33  func NewResolver(storage *Attachments) resolver.Resolver {
    34  	return &storageResolver{
    35  		storage: storage,
    36  		life:    make(map[names.StorageTag]params.Life),
    37  	}
    38  }
    39  
    40  // NextOp is defined on the Resolver interface.
    41  func (s *storageResolver) NextOp(
    42  	localState resolver.LocalState,
    43  	remoteState remotestate.Snapshot,
    44  	opFactory operation.Factory,
    45  ) (operation.Operation, error) {
    46  
    47  	if remoteState.Life == params.Dying {
    48  		// The unit is dying, so destroy all of its storage.
    49  		if !s.dying {
    50  			if err := s.storage.SetDying(); err != nil {
    51  				return nil, errors.Trace(err)
    52  			}
    53  			s.dying = true
    54  		}
    55  		for tag, snap := range remoteState.Storage {
    56  			snap.Life = params.Dying
    57  			remoteState.Storage[tag] = snap
    58  		}
    59  	}
    60  
    61  	if err := s.maybeShortCircuitRemoval(remoteState.Storage); err != nil {
    62  		return nil, errors.Trace(err)
    63  	}
    64  
    65  	var runStorageHooks bool
    66  	switch {
    67  	case localState.Kind == operation.Continue:
    68  		// There's nothing in progress.
    69  		runStorageHooks = true
    70  	case !localState.Installed && localState.Kind == operation.RunHook && localState.Step == operation.Queued:
    71  		// The install operation completed, and there's an install
    72  		// hook queued. Run storage-attached hooks first.
    73  		runStorageHooks = true
    74  	}
    75  	if !runStorageHooks {
    76  		return nil, resolver.ErrNoOperation
    77  	}
    78  
    79  	if !localState.Installed && s.storage.Pending() == 0 {
    80  		logger.Infof("initial storage attachments ready")
    81  	}
    82  
    83  	for tag, snap := range remoteState.Storage {
    84  		op, err := s.nextHookOp(tag, snap, opFactory)
    85  		if errors.Cause(err) == resolver.ErrNoOperation {
    86  			continue
    87  		}
    88  		return op, err
    89  	}
    90  	if s.storage.Pending() > 0 {
    91  		logger.Debugf("still pending %v", s.storage.pending.SortedValues())
    92  		if !localState.Installed {
    93  			// We only wait for pending storage before
    94  			// the install hook runs; we should not block
    95  			// other hooks from running while storage is
    96  			// being provisioned.
    97  			return nil, resolver.ErrWaiting
    98  		}
    99  	}
   100  	return nil, resolver.ErrNoOperation
   101  }
   102  
   103  // maybeShortCircuitRemoval removes any storage that is not alive,
   104  // and has not had a storage-attached hook committed.
   105  func (s *storageResolver) maybeShortCircuitRemoval(remote map[names.StorageTag]remotestate.StorageSnapshot) error {
   106  	for tag, snap := range remote {
   107  		local, ok := s.storage.storageAttachments[tag]
   108  		if (ok && local.attached) || snap.Life == params.Alive {
   109  			continue
   110  		}
   111  		if err := s.storage.removeStorageAttachment(tag); err != nil {
   112  			return errors.Trace(err)
   113  		}
   114  		delete(remote, tag)
   115  	}
   116  	return nil
   117  }
   118  
   119  func (s *storageResolver) nextHookOp(
   120  	tag names.StorageTag,
   121  	snap remotestate.StorageSnapshot,
   122  	opFactory operation.Factory,
   123  ) (operation.Operation, error) {
   124  
   125  	logger.Debugf("next hook op for %v: %+v", tag, snap)
   126  
   127  	if snap.Life == params.Dead {
   128  		// Storage must have been Dying to become Dead;
   129  		// no further action is required.
   130  		return nil, resolver.ErrNoOperation
   131  	}
   132  
   133  	hookInfo := hook.Info{StorageId: tag.Id()}
   134  	switch snap.Life {
   135  	case params.Alive:
   136  		storageAttachment, ok := s.storage.storageAttachments[tag]
   137  		if ok && storageAttachment.attached {
   138  			// Once the storage is attached, we only care about
   139  			// lifecycle state changes.
   140  			return nil, resolver.ErrNoOperation
   141  		}
   142  		// The storage-attached hook has not been committed, so add the
   143  		// storage to the pending set.
   144  		s.storage.pending.Add(tag)
   145  		if !snap.Attached {
   146  			// The storage attachment has not been provisioned yet,
   147  			// so just ignore it for now. We'll be notified again
   148  			// when it has been provisioned.
   149  			return nil, resolver.ErrNoOperation
   150  		}
   151  		// The storage is alive, but we haven't previously run the
   152  		// "storage-attached" hook. Do so now.
   153  		hookInfo.Kind = hooks.StorageAttached
   154  	case params.Dying:
   155  		storageAttachment, ok := s.storage.storageAttachments[tag]
   156  		if !ok || !storageAttachment.attached {
   157  			// Nothing to do: attachment is dying, but
   158  			// the storage-attached hook has not been
   159  			// issued.
   160  			return nil, resolver.ErrNoOperation
   161  		}
   162  		// The storage is dying, but we haven't previously run the
   163  		// "storage-detached" hook. Do so now.
   164  		hookInfo.Kind = hooks.StorageDetaching
   165  	}
   166  
   167  	// Update the local state to reflect what we're about to report
   168  	// to a hook.
   169  	stateFile, err := readStateFile(s.storage.storageStateDir, tag)
   170  	if err != nil {
   171  		return nil, errors.Trace(err)
   172  	}
   173  	s.storage.storageAttachments[tag] = storageAttachment{
   174  		stateFile, &contextStorage{
   175  			tag:      tag,
   176  			kind:     storage.StorageKind(snap.Kind),
   177  			location: snap.Location,
   178  		},
   179  	}
   180  
   181  	return opFactory.NewRunHook(hookInfo)
   182  }