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

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package resolver
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/loggo"
     9  	"gopkg.in/juju/charm.v6-unstable"
    10  	"gopkg.in/juju/charm.v6-unstable/hooks"
    11  
    12  	"github.com/juju/juju/worker/uniter/hook"
    13  	"github.com/juju/juju/worker/uniter/operation"
    14  	"github.com/juju/juju/worker/uniter/remotestate"
    15  )
    16  
    17  var logger = loggo.GetLogger("juju.worker.uniter.resolver")
    18  
    19  // resolverOpFactory wraps an operation.Factory such that skips that affect
    20  // local state will, when committed, update the embedded LocalState struct
    21  // to reflect the change made by the operation.
    22  //
    23  // The wrapped operations embed information specific to the remote state
    24  // snapshot that was used to create the operation. Thus, remote state changes
    25  // observed between the time the operation was created and committed do not
    26  // affect the operation; and the local state change will not prevent further
    27  // operations from being enqueued to achieve the new remote state.
    28  type resolverOpFactory struct {
    29  	operation.Factory
    30  
    31  	LocalState  *LocalState
    32  	RemoteState remotestate.Snapshot
    33  }
    34  
    35  func (s *resolverOpFactory) NewRunHook(info hook.Info) (operation.Operation, error) {
    36  	op, err := s.Factory.NewRunHook(info)
    37  	if err != nil {
    38  		return nil, errors.Trace(err)
    39  	}
    40  	return s.wrapHookOp(op, info), nil
    41  }
    42  
    43  func (s *resolverOpFactory) NewSkipHook(info hook.Info) (operation.Operation, error) {
    44  	op, err := s.Factory.NewSkipHook(info)
    45  	if err != nil {
    46  		return nil, errors.Trace(err)
    47  	}
    48  	return s.wrapHookOp(op, info), nil
    49  }
    50  
    51  func (s *resolverOpFactory) NewUpgrade(charmURL *charm.URL) (operation.Operation, error) {
    52  	op, err := s.Factory.NewUpgrade(charmURL)
    53  	if err != nil {
    54  		return nil, errors.Trace(err)
    55  	}
    56  	return s.wrapUpgradeOp(op, charmURL), nil
    57  }
    58  
    59  func (s *resolverOpFactory) NewRevertUpgrade(charmURL *charm.URL) (operation.Operation, error) {
    60  	op, err := s.Factory.NewRevertUpgrade(charmURL)
    61  	if err != nil {
    62  		return nil, errors.Trace(err)
    63  	}
    64  	return s.wrapUpgradeOp(op, charmURL), nil
    65  }
    66  
    67  func (s *resolverOpFactory) NewResolvedUpgrade(charmURL *charm.URL) (operation.Operation, error) {
    68  	op, err := s.Factory.NewResolvedUpgrade(charmURL)
    69  	if err != nil {
    70  		return nil, errors.Trace(err)
    71  	}
    72  	return s.wrapUpgradeOp(op, charmURL), nil
    73  }
    74  
    75  func (s *resolverOpFactory) NewAction(id string) (operation.Operation, error) {
    76  	op, err := s.Factory.NewAction(id)
    77  	if err != nil {
    78  		return nil, errors.Trace(err)
    79  	}
    80  	f := func() {
    81  		if s.LocalState.CompletedActions == nil {
    82  			s.LocalState.CompletedActions = make(map[string]struct{})
    83  		}
    84  		s.LocalState.CompletedActions[id] = struct{}{}
    85  		s.LocalState.CompletedActions = trimCompletedActions(s.RemoteState.Actions, s.LocalState.CompletedActions)
    86  	}
    87  	op = onCommitWrapper{op, f}
    88  	return op, nil
    89  }
    90  
    91  func trimCompletedActions(pendingActions []string, completedActions map[string]struct{}) map[string]struct{} {
    92  	newCompletedActions := map[string]struct{}{}
    93  	for _, pendingAction := range pendingActions {
    94  		if _, ok := completedActions[pendingAction]; ok {
    95  			newCompletedActions[pendingAction] = struct{}{}
    96  		}
    97  	}
    98  	return newCompletedActions
    99  }
   100  
   101  func (s *resolverOpFactory) wrapUpgradeOp(op operation.Operation, charmURL *charm.URL) operation.Operation {
   102  	charmModifiedVersion := s.RemoteState.CharmModifiedVersion
   103  	return onCommitWrapper{op, func() {
   104  		s.LocalState.CharmURL = charmURL
   105  		s.LocalState.Restart = true
   106  		s.LocalState.Conflicted = false
   107  		s.LocalState.CharmModifiedVersion = charmModifiedVersion
   108  	}}
   109  }
   110  
   111  func (s *resolverOpFactory) wrapHookOp(op operation.Operation, info hook.Info) operation.Operation {
   112  	switch info.Kind {
   113  	case hooks.ConfigChanged:
   114  		v := s.RemoteState.ConfigVersion
   115  		op = onCommitWrapper{op, func() {
   116  			s.LocalState.ConfigVersion = v
   117  		}}
   118  	case hooks.LeaderSettingsChanged:
   119  		v := s.RemoteState.LeaderSettingsVersion
   120  		op = onCommitWrapper{op, func() {
   121  			s.LocalState.LeaderSettingsVersion = v
   122  		}}
   123  	}
   124  
   125  	charmModifiedVersion := s.RemoteState.CharmModifiedVersion
   126  	updateStatusVersion := s.RemoteState.UpdateStatusVersion
   127  	op = onCommitWrapper{op, func() {
   128  		// Update UpdateStatusVersion so that the update-status
   129  		// hook only fires after the next timer.
   130  		s.LocalState.UpdateStatusVersion = updateStatusVersion
   131  		s.LocalState.CharmModifiedVersion = charmModifiedVersion
   132  	}}
   133  
   134  	retryHookVersion := s.RemoteState.RetryHookVersion
   135  	op = onPrepareWrapper{op, func() {
   136  		// Update RetryHookVersion so that we don't attempt to
   137  		// retry a hook more than once between timers signals.
   138  		//
   139  		// We need to do this in Prepare, rather than Commit,
   140  		// in case the retried hook fails.
   141  		s.LocalState.RetryHookVersion = retryHookVersion
   142  	}}
   143  	return op
   144  }
   145  
   146  type onCommitWrapper struct {
   147  	operation.Operation
   148  	onCommit func()
   149  }
   150  
   151  func (op onCommitWrapper) Commit(state operation.State) (*operation.State, error) {
   152  	st, err := op.Operation.Commit(state)
   153  	if err != nil {
   154  		return nil, err
   155  	}
   156  	op.onCommit()
   157  	return st, nil
   158  }
   159  
   160  type onPrepareWrapper struct {
   161  	operation.Operation
   162  	onPrepare func()
   163  }
   164  
   165  func (op onPrepareWrapper) Prepare(state operation.State) (*operation.State, error) {
   166  	st, err := op.Operation.Prepare(state)
   167  	if err != nil {
   168  		return nil, err
   169  	}
   170  	op.onPrepare()
   171  	return st, nil
   172  }