github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/raft/raftflag/flag.go (about) 1 // Copyright 2018 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package raftflag 5 6 import ( 7 "github.com/hashicorp/raft" 8 "github.com/juju/errors" 9 "github.com/juju/loggo" 10 "gopkg.in/juju/worker.v1" 11 "gopkg.in/juju/worker.v1/catacomb" 12 ) 13 14 var logger = loggo.GetLogger("juju.worker.raft.raftflag") 15 16 // Config holds a raftflag.Worker's dependencies and resources. 17 type Config struct { 18 Raft *raft.Raft 19 } 20 21 // Validate returns an error if the config cannot be expected to run a 22 // raftflag.Worker. 23 func (config Config) Validate() error { 24 if config.Raft == nil { 25 return errors.NotValidf("nil Raft") 26 } 27 return nil 28 } 29 30 // ErrRefresh indicates that the flag's Check result is no longer valid, 31 // and a new raftflag.Worker must be started to get a valid result. 32 var ErrRefresh = errors.New("raft leadership changed, restart worker") 33 34 // Worker implements worker.Worker and util.Flag, representing 35 // controller ownership of a model, such that the Flag's validity is 36 // tied to the Worker's lifetime. 37 type Worker struct { 38 catacomb catacomb.Catacomb 39 config Config 40 leader bool 41 } 42 43 func NewWorker(config Config) (worker.Worker, error) { 44 if err := config.Validate(); err != nil { 45 return nil, errors.Trace(err) 46 } 47 flag := &Worker{ 48 config: config, 49 leader: check(config.Raft), 50 } 51 if err := catacomb.Invoke(catacomb.Plan{ 52 Site: &flag.catacomb, 53 Work: flag.loop, 54 }); err != nil { 55 return nil, errors.Trace(err) 56 } 57 return flag, nil 58 } 59 60 // Kill is part of the worker.Worker interface. 61 func (flag *Worker) Kill() { 62 flag.catacomb.Kill(nil) 63 } 64 65 // Wait is part of the worker.Worker interface. 66 func (flag *Worker) Wait() error { 67 return flag.catacomb.Wait() 68 } 69 70 // Check is part of the util.Flag interface. 71 // 72 // Check returns true if the flag indicates that the controller agent is 73 // the current raft leader. 74 // 75 // The validity of this result is tied to the lifetime of the Worker; 76 // once the worker has stopped, no inferences may be drawn from any Check 77 // result. 78 func (flag *Worker) Check() bool { 79 return flag.leader 80 } 81 82 func (flag *Worker) loop() error { 83 ch := make(chan raft.Observation, 1) 84 or := raft.NewObserver(ch, false, func(o *raft.Observation) bool { 85 _, ok := o.Data.(raft.RaftState) 86 return ok 87 }) 88 flag.config.Raft.RegisterObserver(or) 89 defer flag.config.Raft.DeregisterObserver(or) 90 91 for { 92 select { 93 case <-flag.catacomb.Dying(): 94 return flag.catacomb.ErrDying() 95 case <-ch: 96 logger.Debugf("raft state changed: %s", flag.config.Raft.State()) 97 if check(flag.config.Raft) != flag.leader { 98 return ErrRefresh 99 } 100 } 101 } 102 } 103 104 func check(r *raft.Raft) bool { 105 return r.State() == raft.Leader 106 }