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  }