~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/worker/uniter/operation/leader.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
}