github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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"
    10  	"gopkg.in/juju/charm.v6/hooks"
    11  
    12  	"github.com/juju/juju/core/lxdprofile"
    13  	"github.com/juju/juju/core/model"
    14  	"github.com/juju/juju/worker/uniter/hook"
    15  	"github.com/juju/juju/worker/uniter/operation"
    16  	"github.com/juju/juju/worker/uniter/remotestate"
    17  )
    18  
    19  var logger = loggo.GetLogger("juju.worker.uniter.resolver")
    20  
    21  // resolverOpFactory wraps an operation.Factory such that skips that affect
    22  // local state will, when committed, update the embedded LocalState struct
    23  // to reflect the change made by the operation.
    24  //
    25  // The wrapped operations embed information specific to the remote state
    26  // snapshot that was used to create the operation. Thus, remote state changes
    27  // observed between the time the operation was created and committed do not
    28  // affect the operation; and the local state change will not prevent further
    29  // operations from being enqueued to achieve the new remote state.
    30  type resolverOpFactory struct {
    31  	operation.Factory
    32  
    33  	LocalState  *LocalState
    34  	RemoteState remotestate.Snapshot
    35  }
    36  
    37  func (s *resolverOpFactory) NewRunHook(info hook.Info) (operation.Operation, error) {
    38  	op, err := s.Factory.NewRunHook(info)
    39  	if err != nil {
    40  		return nil, errors.Trace(err)
    41  	}
    42  	return s.wrapHookOp(op, info), nil
    43  }
    44  
    45  func (s *resolverOpFactory) NewSkipHook(info hook.Info) (operation.Operation, error) {
    46  	op, err := s.Factory.NewSkipHook(info)
    47  	if err != nil {
    48  		return nil, errors.Trace(err)
    49  	}
    50  	return s.wrapHookOp(op, info), nil
    51  }
    52  
    53  func (s *resolverOpFactory) NewNoOpFinishUpgradeSeries() (operation.Operation, error) {
    54  	op, err := s.Factory.NewNoOpFinishUpgradeSeries()
    55  	if err != nil {
    56  		return nil, errors.Trace(err)
    57  	}
    58  	f := func(*operation.State) {
    59  		s.LocalState.UpgradeSeriesStatus = model.UpgradeSeriesNotStarted
    60  	}
    61  	op = onCommitWrapper{op, f}
    62  	return op, nil
    63  }
    64  
    65  // NewFinishUpgradeCharmProfile completes the process of a charm profile, by
    66  // setting the local state to a not known state.
    67  func (s *resolverOpFactory) NewFinishUpgradeCharmProfile(charmURL *charm.URL) (operation.Operation, error) {
    68  	op, err := s.Factory.NewFinishUpgradeCharmProfile(charmURL)
    69  	if err != nil {
    70  		return nil, errors.Trace(err)
    71  	}
    72  	f := func(*operation.State) {
    73  		s.LocalState.UpgradeCharmProfileStatus = lxdprofile.NotKnownStatus
    74  	}
    75  	op = onCommitWrapper{op, f}
    76  	return op, nil
    77  }
    78  
    79  func (s *resolverOpFactory) NewUpgrade(charmURL *charm.URL) (operation.Operation, error) {
    80  	op, err := s.Factory.NewUpgrade(charmURL)
    81  	if err != nil {
    82  		return nil, errors.Trace(err)
    83  	}
    84  	return s.wrapUpgradeOp(op, charmURL), nil
    85  }
    86  
    87  func (s *resolverOpFactory) NewNoOpUpgrade(charmURL *charm.URL) (operation.Operation, error) {
    88  	op, err := s.Factory.NewNoOpUpgrade(charmURL)
    89  	if err != nil {
    90  		return nil, errors.Trace(err)
    91  	}
    92  	return s.wrapUpgradeOp(op, charmURL), nil
    93  }
    94  
    95  func (s *resolverOpFactory) NewRevertUpgrade(charmURL *charm.URL) (operation.Operation, error) {
    96  	op, err := s.Factory.NewRevertUpgrade(charmURL)
    97  	if err != nil {
    98  		return nil, errors.Trace(err)
    99  	}
   100  	return s.wrapUpgradeOp(op, charmURL), nil
   101  }
   102  
   103  func (s *resolverOpFactory) NewResolvedUpgrade(charmURL *charm.URL) (operation.Operation, error) {
   104  	op, err := s.Factory.NewResolvedUpgrade(charmURL)
   105  	if err != nil {
   106  		return nil, errors.Trace(err)
   107  	}
   108  	return s.wrapUpgradeOp(op, charmURL), nil
   109  }
   110  
   111  func (s *resolverOpFactory) NewAction(id string) (operation.Operation, error) {
   112  	op, err := s.Factory.NewAction(id)
   113  	if err != nil {
   114  		return nil, errors.Trace(err)
   115  	}
   116  	f := func(*operation.State) {
   117  		if s.LocalState.CompletedActions == nil {
   118  			s.LocalState.CompletedActions = make(map[string]struct{})
   119  		}
   120  		s.LocalState.CompletedActions[id] = struct{}{}
   121  		s.LocalState.CompletedActions = trimCompletedActions(s.RemoteState.Actions, s.LocalState.CompletedActions)
   122  	}
   123  	op = onCommitWrapper{op, f}
   124  	return op, nil
   125  }
   126  
   127  func trimCompletedActions(pendingActions []string, completedActions map[string]struct{}) map[string]struct{} {
   128  	newCompletedActions := map[string]struct{}{}
   129  	for _, pendingAction := range pendingActions {
   130  		if _, ok := completedActions[pendingAction]; ok {
   131  			newCompletedActions[pendingAction] = struct{}{}
   132  		}
   133  	}
   134  	return newCompletedActions
   135  }
   136  
   137  func (s *resolverOpFactory) wrapUpgradeOp(op operation.Operation, charmURL *charm.URL) operation.Operation {
   138  	charmModifiedVersion := s.RemoteState.CharmModifiedVersion
   139  	return onCommitWrapper{op, func(*operation.State) {
   140  		s.LocalState.CharmURL = charmURL
   141  		s.LocalState.Restart = true
   142  		s.LocalState.Conflicted = false
   143  		s.LocalState.CharmModifiedVersion = charmModifiedVersion
   144  		s.LocalState.UpgradeCharmProfileStatus = lxdprofile.EmptyStatus
   145  	}}
   146  }
   147  
   148  func (s *resolverOpFactory) wrapHookOp(op operation.Operation, info hook.Info) operation.Operation {
   149  	switch info.Kind {
   150  	case hooks.PreSeriesUpgrade:
   151  		op = onPrepareWrapper{op, func() {
   152  			//on prepare the local status should be made to reflect
   153  			//that the upgrade process for this united has started.
   154  			s.LocalState.UpgradeSeriesStatus = s.RemoteState.UpgradeSeriesStatus
   155  		}}
   156  		op = onCommitWrapper{op, func(*operation.State) {
   157  			// on commit, the local status should indicate the hook
   158  			// has completed. The remote status should already
   159  			// indicate completion. We sync the states here.
   160  			s.LocalState.UpgradeSeriesStatus = model.UpgradeSeriesPrepareCompleted
   161  		}}
   162  	case hooks.PostSeriesUpgrade:
   163  		op = onPrepareWrapper{op, func() {
   164  			s.LocalState.UpgradeSeriesStatus = s.RemoteState.UpgradeSeriesStatus
   165  		}}
   166  		op = onCommitWrapper{op, func(*operation.State) {
   167  			s.LocalState.UpgradeSeriesStatus = model.UpgradeSeriesCompleted
   168  		}}
   169  	case hooks.ConfigChanged:
   170  		configHash := s.RemoteState.ConfigHash
   171  		trustHash := s.RemoteState.TrustHash
   172  		addressesHash := s.RemoteState.AddressesHash
   173  		op = onCommitWrapper{op, func(state *operation.State) {
   174  			if state != nil {
   175  				// Assign these on the operation.State so it gets
   176  				// written into the state file on disk.
   177  				state.ConfigHash = configHash
   178  				state.TrustHash = trustHash
   179  				state.AddressesHash = addressesHash
   180  			}
   181  		}}
   182  	case hooks.LeaderSettingsChanged:
   183  		v := s.RemoteState.LeaderSettingsVersion
   184  		op = onCommitWrapper{op, func(*operation.State) {
   185  			s.LocalState.LeaderSettingsVersion = v
   186  		}}
   187  	}
   188  
   189  	charmModifiedVersion := s.RemoteState.CharmModifiedVersion
   190  	updateStatusVersion := s.RemoteState.UpdateStatusVersion
   191  	op = onCommitWrapper{op, func(*operation.State) {
   192  		// Update UpdateStatusVersion so that the update-status
   193  		// hook only fires after the next timer.
   194  		s.LocalState.UpdateStatusVersion = updateStatusVersion
   195  		s.LocalState.CharmModifiedVersion = charmModifiedVersion
   196  	}}
   197  
   198  	retryHookVersion := s.RemoteState.RetryHookVersion
   199  	op = onPrepareWrapper{op, func() {
   200  		// Update RetryHookVersion so that we don't attempt to
   201  		// retry a hook more than once between timers signals.
   202  		//
   203  		// We need to do this in Prepare, rather than Commit,
   204  		// in case the retried hook fails.
   205  		s.LocalState.RetryHookVersion = retryHookVersion
   206  	}}
   207  	return op
   208  }
   209  
   210  type onCommitWrapper struct {
   211  	operation.Operation
   212  	onCommit func(*operation.State)
   213  }
   214  
   215  func (op onCommitWrapper) Commit(state operation.State) (*operation.State, error) {
   216  	st, err := op.Operation.Commit(state)
   217  	if err != nil {
   218  		return nil, err
   219  	}
   220  	op.onCommit(st)
   221  	return st, nil
   222  }
   223  
   224  type onPrepareWrapper struct {
   225  	operation.Operation
   226  	onPrepare func()
   227  }
   228  
   229  func (op onPrepareWrapper) Prepare(state operation.State) (*operation.State, error) {
   230  	st, err := op.Operation.Prepare(state)
   231  	if err != nil {
   232  		return nil, err
   233  	}
   234  	op.onPrepare()
   235  	return st, nil
   236  }