// NextOp implements the resolver.Resolver interface. func (r *actionsResolver) NextOp( localState resolver.LocalState, remoteState remotestate.Snapshot, opFactory operation.Factory, ) (operation.Operation, error) { nextAction, err := nextAction(remoteState.Actions, localState.CompletedActions) if err != nil { return nil, err } switch localState.Kind { case operation.RunHook: // We can still run actions if the unit is in a hook error state. if localState.Step == operation.Pending { return opFactory.NewAction(nextAction) } case operation.RunAction: if localState.Hook != nil { logger.Infof("found incomplete action %q; ignoring", localState.ActionId) logger.Infof("recommitting prior %q hook", localState.Hook.Kind) return opFactory.NewSkipHook(*localState.Hook) } else { logger.Infof("%q hook is nil", operation.RunAction) return opFactory.NewFailAction(*localState.ActionId) } case operation.Continue: return opFactory.NewAction(nextAction) } return nil, resolver.ErrNoOperation }
// NextOp implements the resolver.Resolver interface. func (r *actionsResolver) NextOp( localState resolver.LocalState, remoteState remotestate.Snapshot, opFactory operation.Factory, ) (operation.Operation, error) { nextAction, err := nextAction(remoteState.Actions, localState.CompletedActions) if err != nil { return nil, err } switch localState.Kind { case operation.RunHook: // We can still run actions if the unit is in a hook error state. if localState.Step == operation.Pending { return opFactory.NewAction(nextAction) } case operation.RunAction: // TODO(fwereade): we *should* handle interrupted actions, and make sure // they're marked as failed, but that's not for now. if localState.Hook != nil { logger.Infof("found incomplete action %q; ignoring", localState.ActionId) logger.Infof("recommitting prior %q hook", localState.Hook.Kind) return opFactory.NewSkipHook(*localState.Hook) } else { logger.Infof("%q hook is nil", operation.RunAction) } case operation.Continue: return opFactory.NewAction(nextAction) } return nil, resolver.ErrNoOperation }
func (s *uniterResolver) nextOpHookError( localState resolver.LocalState, remoteState remotestate.Snapshot, opFactory operation.Factory, ) (operation.Operation, error) { // Report the hook error. if err := s.reportHookError(*localState.Hook); err != nil { return nil, errors.Trace(err) } if remoteState.ForceCharmUpgrade && *localState.CharmURL != *remoteState.CharmURL { logger.Debugf("upgrade from %v to %v", localState.CharmURL, remoteState.CharmURL) return opFactory.NewUpgrade(remoteState.CharmURL) } switch remoteState.ResolvedMode { case params.ResolvedNone: return nil, resolver.ErrNoOperation case params.ResolvedRetryHooks: if err := s.clearResolved(); err != nil { return nil, errors.Trace(err) } return opFactory.NewRunHook(*localState.Hook) case params.ResolvedNoHooks: if err := s.clearResolved(); err != nil { return nil, errors.Trace(err) } return opFactory.NewSkipHook(*localState.Hook) default: return nil, errors.Errorf( "unknown resolved mode %q", remoteState.ResolvedMode, ) } }
func (s *uniterResolver) nextOpHookError( localState resolver.LocalState, remoteState remotestate.Snapshot, opFactory operation.Factory, ) (operation.Operation, error) { // Report the hook error. if err := s.config.ReportHookError(*localState.Hook); err != nil { return nil, errors.Trace(err) } if remoteState.ForceCharmUpgrade && *localState.CharmURL != *remoteState.CharmURL { logger.Debugf("upgrade from %v to %v", localState.CharmURL, remoteState.CharmURL) return opFactory.NewUpgrade(remoteState.CharmURL) } switch remoteState.ResolvedMode { case params.ResolvedNone: if remoteState.RetryHookVersion > localState.RetryHookVersion { // We've been asked to retry: clear the hook timer // started state so we'll restart it if this fails. // // If the hook fails again, we'll re-enter this method // with the retry hook versions equal and restart the // timer. If the hook succeeds, we'll enter nextOp // and stop the timer. s.retryHookTimerStarted = false return opFactory.NewRunHook(*localState.Hook) } if !s.retryHookTimerStarted { // We haven't yet started a retry timer, so start one // now. If we retry and fail, retryHookTimerStarted is // cleared so that we'll still start it again. s.config.StartRetryHookTimer() s.retryHookTimerStarted = true } return nil, resolver.ErrNoOperation case params.ResolvedRetryHooks: s.config.StopRetryHookTimer() s.retryHookTimerStarted = false if err := s.config.ClearResolved(); err != nil { return nil, errors.Trace(err) } return opFactory.NewRunHook(*localState.Hook) case params.ResolvedNoHooks: s.config.StopRetryHookTimer() s.retryHookTimerStarted = false if err := s.config.ClearResolved(); err != nil { return nil, errors.Trace(err) } return opFactory.NewSkipHook(*localState.Hook) default: return nil, errors.Errorf( "unknown resolved mode %q", remoteState.ResolvedMode, ) } }
func (s *uniterResolver) NextOp( localState resolver.LocalState, remoteState remotestate.Snapshot, opFactory operation.Factory, ) (operation.Operation, error) { if remoteState.Life == params.Dead || localState.Stopped { return nil, resolver.ErrTerminate } if localState.Kind == operation.Upgrade { if localState.Conflicted { return s.nextOpConflicted(localState, remoteState, opFactory) } logger.Infof("resuming charm upgrade") return opFactory.NewUpgrade(localState.CharmURL) } if localState.Restart { // We've just run the upgrade op, which will change the // unit's charm URL. We need to restart the resolver // loop so that we start watching the correct events. return nil, resolver.ErrRestart } if s.retryHookTimerStarted && (localState.Kind != operation.RunHook || localState.Step != operation.Pending) { // The hook-retry timer is running, but there is no pending // hook operation. We're not in an error state, so stop the // timer now to reset the backoff state. s.config.StopRetryHookTimer() s.retryHookTimerStarted = false } op, err := s.config.Leadership.NextOp(localState, remoteState, opFactory) if errors.Cause(err) != resolver.ErrNoOperation { return op, err } op, err = s.config.Actions.NextOp(localState, remoteState, opFactory) if errors.Cause(err) != resolver.ErrNoOperation { return op, err } op, err = s.config.Commands.NextOp(localState, remoteState, opFactory) if errors.Cause(err) != resolver.ErrNoOperation { return op, err } op, err = s.config.Storage.NextOp(localState, remoteState, opFactory) if errors.Cause(err) != resolver.ErrNoOperation { return op, err } switch localState.Kind { case operation.RunHook: switch localState.Step { case operation.Pending: logger.Infof("awaiting error resolution for %q hook", localState.Hook.Kind) return s.nextOpHookError(localState, remoteState, opFactory) case operation.Queued: logger.Infof("found queued %q hook", localState.Hook.Kind) if localState.Hook.Kind == hooks.Install { // Special case: handle install in nextOp, // so we do nothing when the unit is dying. return s.nextOp(localState, remoteState, opFactory) } return opFactory.NewRunHook(*localState.Hook) case operation.Done: logger.Infof("committing %q hook", localState.Hook.Kind) return opFactory.NewSkipHook(*localState.Hook) default: return nil, errors.Errorf("unknown operation step %v", localState.Step) } case operation.Continue: logger.Infof("no operations in progress; waiting for changes") return s.nextOp(localState, remoteState, opFactory) default: return nil, errors.Errorf("unknown operation kind %v", localState.Kind) } }
func (s *uniterResolver) NextOp( localState resolver.LocalState, remoteState remotestate.Snapshot, opFactory operation.Factory, ) (operation.Operation, error) { if remoteState.Life == params.Dead || localState.Stopped { return nil, resolver.ErrTerminate } if localState.Kind == operation.Upgrade { if localState.Conflicted { return s.nextOpConflicted(localState, remoteState, opFactory) } logger.Infof("resuming charm upgrade") return opFactory.NewUpgrade(localState.CharmURL) } if localState.Restart { // We've just run the upgrade op, which will change the // unit's charm URL. We need to restart the resolver // loop so that we start watching the correct events. return nil, resolver.ErrRestart } if localState.Kind == operation.Continue { if err := s.fixDeployer(); err != nil { return nil, errors.Trace(err) } } op, err := s.leadershipResolver.NextOp(localState, remoteState, opFactory) if errors.Cause(err) != resolver.ErrNoOperation { return op, err } op, err = s.actionsResolver.NextOp(localState, remoteState, opFactory) if errors.Cause(err) != resolver.ErrNoOperation { return op, err } op, err = s.storageResolver.NextOp(localState, remoteState, opFactory) if errors.Cause(err) != resolver.ErrNoOperation { return op, err } switch localState.Kind { case operation.RunHook: switch localState.Step { case operation.Pending: logger.Infof("awaiting error resolution for %q hook", localState.Hook.Kind) return s.nextOpHookError(localState, remoteState, opFactory) case operation.Queued: logger.Infof("found queued %q hook", localState.Hook.Kind) if localState.Hook.Kind == hooks.Install { // Special case: handle install in nextOp, // so we do nothing when the unit is dying. return s.nextOp(localState, remoteState, opFactory) } return opFactory.NewRunHook(*localState.Hook) case operation.Done: logger.Infof("committing %q hook", localState.Hook.Kind) return opFactory.NewSkipHook(*localState.Hook) default: return nil, errors.Errorf("unknown operation step %v", localState.Step) } case operation.Continue: logger.Infof("no operations in progress; waiting for changes") return s.nextOp(localState, remoteState, opFactory) default: return nil, errors.Errorf("unknown operation kind %v", localState.Kind) } }