github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/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 }