github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/worker/uniter/operation/leader.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package operation
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  
     9  	"github.com/juju/juju/worker/uniter/hook"
    10  )
    11  
    12  type acceptLeadership struct {
    13  	DoesNotRequireMachineLock
    14  }
    15  
    16  // String is part of the Operation interface.
    17  func (al *acceptLeadership) String() string {
    18  	return "accept leadership"
    19  }
    20  
    21  // Prepare is part of the Operation interface.
    22  func (al *acceptLeadership) Prepare(state State) (*State, error) {
    23  	if err := al.checkState(state); err != nil {
    24  		return nil, err
    25  	}
    26  	return nil, ErrSkipExecute
    27  }
    28  
    29  // Execute is part of the Operation interface.
    30  func (al *acceptLeadership) Execute(state State) (*State, error) {
    31  	return nil, errors.New("prepare always errors; Execute is never valid")
    32  }
    33  
    34  // Commit is part of the Operation interface.
    35  func (al *acceptLeadership) Commit(state State) (*State, error) {
    36  	if err := al.checkState(state); err != nil {
    37  		return nil, err
    38  	}
    39  	if state.Leader {
    40  		// Nothing needs to be done -- leader is only set when queueing a
    41  		// leader-elected hook. Therefore, if leader is true, the appropriate
    42  		// hook must be either queued or already run.
    43  		return nil, nil
    44  	}
    45  	newState := stateChange{
    46  		Kind: RunHook,
    47  		Step: Queued,
    48  		Hook: &hook.Info{Kind: hook.LeaderElected},
    49  	}.apply(state)
    50  	newState.Leader = true
    51  	return newState, nil
    52  }
    53  
    54  func (al *acceptLeadership) checkState(state State) error {
    55  	if state.Kind != Continue {
    56  		// We'll need to queue up a hook, and we can't do that without
    57  		// stomping on existing state.
    58  		return ErrCannotAcceptLeadership
    59  	}
    60  	return nil
    61  }
    62  
    63  type resignLeadership struct {
    64  	DoesNotRequireMachineLock
    65  }
    66  
    67  // String is part of the Operation interface.
    68  func (rl *resignLeadership) String() string {
    69  	return "resign leadership"
    70  }
    71  
    72  // Prepare is part of the Operation interface.
    73  func (rl *resignLeadership) Prepare(state State) (*State, error) {
    74  	if !state.Leader {
    75  		// Nothing needs to be done -- state.Leader should only be set to
    76  		// false when committing the leader-deposed hook. This code is not
    77  		// helpful while Execute is a no-op, but it will become so.
    78  		return nil, ErrSkipExecute
    79  	}
    80  	return nil, nil
    81  }
    82  
    83  // Execute is part of the Operation interface.
    84  func (rl *resignLeadership) Execute(state State) (*State, error) {
    85  	// TODO(fwereade): this hits a lot of interestingly intersecting problems.
    86  	//
    87  	// 1) we can't yet create a sufficiently dumbed-down hook context for a
    88  	//    leader-deposed hook to run as specced. (This is the proximate issue,
    89  	//    and is sufficient to prevent us from implementing this op right.)
    90  	// 2) we want to write a state-file change, so this has to be an operation
    91  	//    (or, at least, it has to be serialized with all other operations).
    92  	//      * note that the change we write must *not* include the RunHook
    93  	//        operation for leader-deposed -- we want to run this at high
    94  	//        priority, in any possible state, and not to disturn what's
    95  	//        there other than to note that we no longer think we're leader.
    96  	// 3) the hook execution itself *might* not need to be serialized with
    97  	//    other operations, which is moot until we consider that:
    98  	// 4) we want to invoke this behaviour from elsewhere (ie when we don't
    99  	//    have an api connection available), but:
   100  	// 5) we can't get around the serialization requirement in (2).
   101  	//
   102  	// So. I *think* that the right approach is to implement a no-api uniter
   103  	// variant, that we run *instead of* the normal uniter when the API is
   104  	// unavailable, and replace with a real uniter when appropriate; this
   105  	// implies that we need to take care not to allow the implementations to
   106  	// diverge, but implementing them both as "uniters" is probably the best
   107  	// way to encourage logic-sharing and prevent that problem.
   108  	//
   109  	// In the short term, though, we can just run leader-deposed as soon as we
   110  	// can build the right environment. Not sure whether this particular type
   111  	// will still be justified, or whether it'll just be a plain old RunHook --
   112  	// I *think* it will stay, because the state-writing behaviour will stay
   113  	// very different (ie just write `.Leader = false` and don't step on pre-
   114  	// queued hooks).
   115  	logger.Warningf("we should run a leader-deposed hook here, but we can't yet")
   116  	return nil, nil
   117  }
   118  
   119  // Commit is part of the Operation interface.
   120  func (rl *resignLeadership) Commit(state State) (*State, error) {
   121  	state.Leader = false
   122  	return &state, nil
   123  }