github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/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  	return onCommitWrapper{op, func() {
   103  		s.LocalState.CharmURL = charmURL
   104  		s.LocalState.Restart = true
   105  		s.LocalState.Conflicted = false
   106  	}}
   107  }
   108  
   109  func (s *resolverOpFactory) wrapHookOp(op operation.Operation, info hook.Info) operation.Operation {
   110  	switch info.Kind {
   111  	case hooks.ConfigChanged:
   112  		v := s.RemoteState.ConfigVersion
   113  		op = onCommitWrapper{op, func() {
   114  			s.LocalState.ConfigVersion = v
   115  		}}
   116  	case hooks.LeaderSettingsChanged:
   117  		v := s.RemoteState.LeaderSettingsVersion
   118  		op = onCommitWrapper{op, func() {
   119  			s.LocalState.LeaderSettingsVersion = v
   120  		}}
   121  	}
   122  	// No matter what has finished running, we reset the UpdateStatusVersion so that
   123  	// the update-status hook only fires after the next timer.
   124  	v := s.RemoteState.UpdateStatusVersion
   125  	return onCommitWrapper{op, func() {
   126  		s.LocalState.UpdateStatusVersion = v
   127  	}}
   128  }
   129  
   130  type onCommitWrapper struct {
   131  	operation.Operation
   132  	f func()
   133  }
   134  
   135  func (op onCommitWrapper) Commit(state operation.State) (*operation.State, error) {
   136  	st, err := op.Operation.Commit(state)
   137  	if err != nil {
   138  		return nil, err
   139  	}
   140  	onCommit(op)
   141  	return st, nil
   142  }
   143  
   144  func onCommit(op operation.Operation) {
   145  	if wrapper, ok := op.(onCommitWrapper); ok {
   146  		wrapper.f()
   147  		onCommit(wrapper.Operation)
   148  	}
   149  }