github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/worker/uniter/leadership/resolver.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package leadership
     5  
     6  import (
     7  	"github.com/juju/charm/v12/hooks"
     8  
     9  	"github.com/juju/juju/core/life"
    10  	"github.com/juju/juju/worker/uniter/hook"
    11  	"github.com/juju/juju/worker/uniter/operation"
    12  	"github.com/juju/juju/worker/uniter/remotestate"
    13  	"github.com/juju/juju/worker/uniter/resolver"
    14  )
    15  
    16  // Logger is here to stop the desire of creating a package level Logger.
    17  // Don't do this, instead use the one passed into the NewResolver as needed.
    18  type logger interface{}
    19  
    20  var _ logger = struct{}{}
    21  
    22  // Logger defines the logging methods used by the leadership package.
    23  type Logger interface {
    24  	Tracef(string, ...interface{})
    25  }
    26  
    27  type leadershipResolver struct {
    28  	logger Logger
    29  }
    30  
    31  // NewResolver returns a new leadership resolver.
    32  func NewResolver(logger Logger) resolver.Resolver {
    33  	return &leadershipResolver{logger: logger}
    34  }
    35  
    36  // NextOp is defined on the Resolver interface.
    37  func (l *leadershipResolver) NextOp(
    38  	localState resolver.LocalState,
    39  	remoteState remotestate.Snapshot,
    40  	opFactory operation.Factory,
    41  ) (operation.Operation, error) {
    42  
    43  	// TODO(wallyworld) - maybe this can occur before install
    44  	if !localState.Installed {
    45  		return nil, resolver.ErrNoOperation
    46  	}
    47  
    48  	// Check for any leadership change, and enact it if possible.
    49  	l.logger.Tracef("checking leadership status")
    50  
    51  	// If we've already accepted leadership, we don't need to do it again.
    52  	canAcceptLeader := !localState.Leader
    53  	if remoteState.Life == life.Dying {
    54  		canAcceptLeader = false
    55  	} else {
    56  		// If we're in an unexpected mode (eg pending hook) we shouldn't try either.
    57  		if localState.Kind != operation.Continue {
    58  			canAcceptLeader = false
    59  		}
    60  	}
    61  
    62  	switch {
    63  	case remoteState.Leader && canAcceptLeader:
    64  		return opFactory.NewAcceptLeadership()
    65  
    66  	// If we're the leader but should not be any longer, or
    67  	// if the unit is dying, we should resign leadership.
    68  	case localState.Leader && (!remoteState.Leader || remoteState.Life == life.Dying):
    69  		return opFactory.NewResignLeadership()
    70  	}
    71  
    72  	if localState.Kind == operation.Continue {
    73  		// We want to run the leader settings hook if we're
    74  		// not the leader and the settings have changed.
    75  		// Note though that if we are dying, we may have already executed "resign leadership".
    76  		// In this case, as far as the unit agent is concerned, we are not the leader any more
    77  		// but we don't want to run the leader settings hook as the transition away from leadership
    78  		// has only been recorded locally, and the Juju model still has us as a leader that is dying.
    79  		if !localState.Leader && remoteState.Life != life.Dying && localState.LeaderSettingsVersion != remoteState.LeaderSettingsVersion {
    80  			return opFactory.NewRunHook(hook.Info{Kind: hooks.LeaderSettingsChanged})
    81  		}
    82  	}
    83  
    84  	l.logger.Tracef("leadership status is up-to-date")
    85  	return nil, resolver.ErrNoOperation
    86  }