github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/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  	"github.com/juju/names"
     9  	"gopkg.in/juju/charm.v6-unstable/hooks"
    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  	var changed []names.StorageTag
    48  	for tag, storage := range remoteState.Storage {
    49  		life, ok := s.life[tag]
    50  		if !ok || life != storage.Life {
    51  			s.life[tag] = storage.Life
    52  			changed = append(changed, tag)
    53  		}
    54  	}
    55  	for tag := range s.life {
    56  		if _, ok := remoteState.Storage[tag]; !ok {
    57  			changed = append(changed, tag)
    58  			delete(s.life, tag)
    59  		}
    60  	}
    61  	if len(changed) > 0 {
    62  		return opFactory.NewUpdateStorage(changed)
    63  	}
    64  	if !localState.Installed && s.storage.Pending() == 0 {
    65  		logger.Infof("initial storage attachments ready")
    66  	}
    67  	return s.nextOp(localState, remoteState, opFactory)
    68  }
    69  
    70  func (s *storageResolver) nextOp(
    71  	localState resolver.LocalState,
    72  	remoteState remotestate.Snapshot,
    73  	opFactory operation.Factory,
    74  ) (operation.Operation, error) {
    75  	if remoteState.Life == params.Dying {
    76  		if !s.dying {
    77  			if err := s.storage.SetDying(); err != nil {
    78  				return nil, errors.Trace(err)
    79  			}
    80  			s.dying = true
    81  		}
    82  		for tag, snap := range remoteState.Storage {
    83  			snap.Life = params.Dying
    84  			remoteState.Storage[tag] = snap
    85  		}
    86  	}
    87  
    88  	var runStorageHooks bool
    89  	switch {
    90  	case localState.Kind == operation.Continue:
    91  		// There's nothing in progress.
    92  		runStorageHooks = true
    93  	case !localState.Installed && localState.Kind == operation.RunHook && localState.Step == operation.Queued:
    94  		// The install operation completed, and there's an install
    95  		// hook queued. Run storage-attached hooks first.
    96  		runStorageHooks = true
    97  	}
    98  
    99  	if runStorageHooks {
   100  		for tag, snap := range remoteState.Storage {
   101  			op, err := s.nextHookOp(tag, snap, opFactory)
   102  			if errors.Cause(err) == resolver.ErrNoOperation {
   103  				continue
   104  			}
   105  			return op, err
   106  		}
   107  		if s.storage.Pending() > 0 {
   108  			logger.Debugf("still pending %v", s.storage.pending)
   109  			if !localState.Installed {
   110  				return nil, resolver.ErrWaiting
   111  			}
   112  		}
   113  	}
   114  	return nil, resolver.ErrNoOperation
   115  }
   116  
   117  func (s *storageResolver) nextHookOp(
   118  	tag names.StorageTag,
   119  	snap remotestate.StorageSnapshot,
   120  	opFactory operation.Factory,
   121  ) (operation.Operation, error) {
   122  	logger.Debugf("next hook op for %v: %+v", tag, snap)
   123  	if !snap.Attached {
   124  		return nil, resolver.ErrNoOperation
   125  	}
   126  	storageAttachment, ok := s.storage.storageAttachments[tag]
   127  	if !ok {
   128  		return nil, resolver.ErrNoOperation
   129  	}
   130  	switch snap.Life {
   131  	case params.Alive:
   132  		if storageAttachment.attached {
   133  			// Storage attachments currently do not change
   134  			// (apart from lifecycle) after being provisioned.
   135  			// We don't process unprovisioned storage here,
   136  			// so there's nothing to do.
   137  			return nil, resolver.ErrNoOperation
   138  		}
   139  	case params.Dying:
   140  		if !storageAttachment.attached {
   141  			// Nothing to do: attachment is dying, but
   142  			// the storage-attached hook has not been
   143  			// consumed.
   144  			return nil, resolver.ErrNoOperation
   145  		}
   146  	case params.Dead:
   147  		// Storage must have been Dying to become Dead;
   148  		// no further action is required.
   149  		return nil, resolver.ErrNoOperation
   150  	}
   151  
   152  	hookInfo := hook.Info{
   153  		StorageId: tag.Id(),
   154  	}
   155  	if snap.Life == params.Alive {
   156  		hookInfo.Kind = hooks.StorageAttached
   157  	} else {
   158  		hookInfo.Kind = hooks.StorageDetaching
   159  	}
   160  	context := &contextStorage{
   161  		tag:      tag,
   162  		kind:     storage.StorageKind(snap.Kind),
   163  		location: snap.Location,
   164  	}
   165  	storageAttachment.ContextStorageAttachment = context
   166  	s.storage.storageAttachments[tag] = storageAttachment
   167  
   168  	logger.Debugf("queued hook: %v", hookInfo)
   169  	return opFactory.NewRunHook(hookInfo)
   170  }