github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/worker/singular/flag.go (about) 1 // Copyright 2015-2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package singular 5 6 import ( 7 "time" 8 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" 13 ) 14 15 // Facade exposes the capabilities required by a FlagWorker. 16 type Facade interface { 17 Claim(duration time.Duration) error 18 Wait() error 19 } 20 21 // FlagConfig holds a FlagWorker's dependencies and resources. 22 type FlagConfig struct { 23 Clock clock.Clock 24 Facade Facade 25 Duration time.Duration 26 } 27 28 // Validate returns an error if the config cannot be expected to run a 29 // FlagWorker. 30 func (config FlagConfig) Validate() error { 31 if config.Clock == nil { 32 return errors.NotValidf("nil Clock") 33 } 34 if config.Facade == nil { 35 return errors.NotValidf("nil Facade") 36 } 37 if config.Duration <= 0 { 38 return errors.NotValidf("non-positive Duration") 39 } 40 return nil 41 } 42 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") 46 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 52 config FlagConfig 53 valid bool 54 } 55 56 func NewFlagWorker(config FlagConfig) (*FlagWorker, error) { 57 if err := config.Validate(); err != nil { 58 return nil, errors.Trace(err) 59 } 60 valid, err := claim(config) 61 if err != nil { 62 return nil, errors.Trace(err) 63 } 64 flag := &FlagWorker{ 65 config: config, 66 valid: valid, 67 } 68 err = catacomb.Invoke(catacomb.Plan{ 69 Site: &flag.catacomb, 70 Work: flag.run, 71 }) 72 if err != nil { 73 return nil, errors.Trace(err) 74 } 75 return flag, nil 76 } 77 78 // Kill is part of the worker.Worker interface. 79 func (flag *FlagWorker) Kill() { 80 flag.catacomb.Kill(nil) 81 } 82 83 // Wait is part of the worker.Worker interface. 84 func (flag *FlagWorker) Wait() error { 85 return flag.catacomb.Wait() 86 } 87 88 // Check is part of the util.Flag interface. 89 // 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). 93 // 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 96 // result. 97 func (flag *FlagWorker) Check() bool { 98 return flag.valid 99 } 100 101 // run invokes a suitable runFunc, depending on the value of .valid. 102 func (flag *FlagWorker) run() error { 103 runFunc := waitVacant 104 if flag.valid { 105 runFunc = keepOccupied 106 } 107 err := runFunc(flag.config, flag.catacomb.Dying()) 108 return errors.Trace(err) 109 } 110 111 // keepOccupied is a runFunc that tries to keep a flag valid. 112 func keepOccupied(config FlagConfig, abort <-chan struct{}) error { 113 for { 114 select { 115 case <-abort: 116 return nil 117 case <-sleep(config): 118 success, err := claim(config) 119 if err != nil { 120 return errors.Trace(err) 121 } 122 if !success { 123 return ErrRefresh 124 } 125 } 126 } 127 } 128 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) 134 switch cause { 135 case nil: 136 return true, nil 137 case lease.ErrClaimDenied: 138 return false, nil 139 } 140 return false, errors.Trace(err) 141 } 142 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) 146 } 147 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) 154 } 155 return ErrRefresh 156 }