1
// Copyright 2015-2016 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
9
"github.com/juju/errors"
10
"github.com/juju/juju/core/lease"
11
"github.com/juju/juju/worker/catacomb"
12
"github.com/juju/utils/clock"
15
// Facade exposes the capabilities required by a FlagWorker.
16
type Facade interface {
17
Claim(duration time.Duration) error
21
// FlagConfig holds a FlagWorker's dependencies and resources.
22
type FlagConfig struct {
25
Duration time.Duration
28
// Validate returns an error if the config cannot be expected to run a
30
func (config FlagConfig) Validate() error {
31
if config.Clock == nil {
32
return errors.NotValidf("nil Clock")
34
if config.Facade == nil {
35
return errors.NotValidf("nil Facade")
37
if config.Duration <= 0 {
38
return errors.NotValidf("non-positive Duration")
43
// ErrRefresh indicates that the flag's Check result is no longer valid,
44
// and a new FlagWorker must be started to get a valid result.
45
var ErrRefresh = errors.New("model responsibility unclear, please retry")
47
// FlagWorker implements worker.Worker and util.Flag, representing
48
// controller ownership of a model, such that the Flag's validity is tied
49
// to the Worker's lifetime.
50
type FlagWorker struct {
51
catacomb catacomb.Catacomb
56
func NewFlagWorker(config FlagConfig) (*FlagWorker, error) {
57
if err := config.Validate(); err != nil {
58
return nil, errors.Trace(err)
60
valid, err := claim(config)
62
return nil, errors.Trace(err)
68
err = catacomb.Invoke(catacomb.Plan{
73
return nil, errors.Trace(err)
78
// Kill is part of the worker.Worker interface.
79
func (flag *FlagWorker) Kill() {
80
flag.catacomb.Kill(nil)
83
// Wait is part of the worker.Worker interface.
84
func (flag *FlagWorker) Wait() error {
85
return flag.catacomb.Wait()
88
// Check is part of the util.Flag interface.
90
// Check returns true if the flag indicates that the configured Identity
91
// (i.e. this controller) has taken control of the configured Scope (i.e.
92
// the model we want to manage exclusively).
94
// The validity of this result is tied to the lifetime of the FlagWorker;
95
// once the worker has stopped, no inferences may be drawn from any Check
97
func (flag *FlagWorker) Check() bool {
101
// run invokes a suitable runFunc, depending on the value of .valid.
102
func (flag *FlagWorker) run() error {
103
runFunc := waitVacant
105
runFunc = keepOccupied
107
err := runFunc(flag.config, flag.catacomb.Dying())
108
return errors.Trace(err)
111
// keepOccupied is a runFunc that tries to keep a flag valid.
112
func keepOccupied(config FlagConfig, abort <-chan struct{}) error {
117
case <-sleep(config):
118
success, err := claim(config)
120
return errors.Trace(err)
129
// claim claims model ownership on behalf of a controller, and returns
130
// true if the attempt succeeded.
131
func claim(config FlagConfig) (bool, error) {
132
err := config.Facade.Claim(config.Duration)
133
cause := errors.Cause(err)
137
case lease.ErrClaimDenied:
140
return false, errors.Trace(err)
143
// sleep waits for half the duration of a (presumed) earlier successful claim.
144
func sleep(config FlagConfig) <-chan time.Time {
145
return config.Clock.After(config.Duration / 2)
148
// wait is a runFunc that ignores its abort chan and always returns an error;
149
// either because of a failed api call, or a successful one, which indicates
150
// that no lease is held; hence, that the worker should be bounced.
151
func waitVacant(config FlagConfig, _ <-chan struct{}) error {
152
if err := config.Facade.Wait(); err != nil {
153
return errors.Trace(err)