github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/raft/manifold.go (about)

     1  // Copyright 2018 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package raft
     5  
     6  import (
     7  	"path/filepath"
     8  
     9  	"github.com/hashicorp/raft"
    10  	"github.com/juju/clock"
    11  	"github.com/juju/errors"
    12  	"github.com/prometheus/client_golang/prometheus"
    13  	"gopkg.in/juju/worker.v1"
    14  	"gopkg.in/juju/worker.v1/dependency"
    15  
    16  	"github.com/juju/juju/agent"
    17  )
    18  
    19  // ManifoldConfig holds the information necessary to run a raft
    20  // worker in a dependency.Engine.
    21  type ManifoldConfig struct {
    22  	ClockName     string
    23  	AgentName     string
    24  	TransportName string
    25  
    26  	FSM                  raft.FSM
    27  	Logger               Logger
    28  	PrometheusRegisterer prometheus.Registerer
    29  	NewWorker            func(Config) (worker.Worker, error)
    30  }
    31  
    32  // Validate validates the manifold configuration.
    33  func (config ManifoldConfig) Validate() error {
    34  	if config.ClockName == "" {
    35  		return errors.NotValidf("empty ClockName")
    36  	}
    37  	if config.AgentName == "" {
    38  		return errors.NotValidf("empty AgentName")
    39  	}
    40  	if config.TransportName == "" {
    41  		return errors.NotValidf("empty TransportName")
    42  	}
    43  	if config.FSM == nil {
    44  		return errors.NotValidf("nil FSM")
    45  	}
    46  	if config.Logger == nil {
    47  		return errors.NotValidf("nil Logger")
    48  	}
    49  	if config.NewWorker == nil {
    50  		return errors.NotValidf("nil NewWorker")
    51  	}
    52  	return nil
    53  }
    54  
    55  // Manifold returns a dependency.Manifold that will run a raft worker.
    56  func Manifold(config ManifoldConfig) dependency.Manifold {
    57  	return dependency.Manifold{
    58  		Inputs: []string{
    59  			config.ClockName,
    60  			config.AgentName,
    61  			config.TransportName,
    62  		},
    63  		Start:  config.start,
    64  		Output: raftOutput,
    65  	}
    66  }
    67  
    68  // start is a method on ManifoldConfig because it's more readable than a closure.
    69  func (config ManifoldConfig) start(context dependency.Context) (worker.Worker, error) {
    70  	if err := config.Validate(); err != nil {
    71  		return nil, errors.Trace(err)
    72  	}
    73  
    74  	var clk clock.Clock
    75  	if err := context.Get(config.ClockName, &clk); err != nil {
    76  		return nil, errors.Trace(err)
    77  	}
    78  
    79  	var agent agent.Agent
    80  	if err := context.Get(config.AgentName, &agent); err != nil {
    81  		return nil, errors.Trace(err)
    82  	}
    83  
    84  	var transport raft.Transport
    85  	if err := context.Get(config.TransportName, &transport); err != nil {
    86  		return nil, errors.Trace(err)
    87  	}
    88  
    89  	// TODO(axw) make the directory path configurable, so we can
    90  	// potentially have multiple Rafts. The dqlite raft should go
    91  	// in <data-dir>/dqlite.
    92  	agentConfig := agent.CurrentConfig()
    93  	raftDir := filepath.Join(agentConfig.DataDir(), "raft")
    94  
    95  	return config.NewWorker(Config{
    96  		FSM:                  config.FSM,
    97  		Logger:               config.Logger,
    98  		StorageDir:           raftDir,
    99  		LocalID:              raft.ServerID(agentConfig.Tag().Id()),
   100  		Transport:            transport,
   101  		Clock:                clk,
   102  		PrometheusRegisterer: config.PrometheusRegisterer,
   103  	})
   104  }
   105  
   106  func raftOutput(in worker.Worker, out interface{}) error {
   107  	w, ok := in.(withRaftOutputs)
   108  	if !ok {
   109  		return errors.Errorf("expected input of type withRaftOutputs, got %T", in)
   110  	}
   111  	switch out := out.(type) {
   112  	case **raft.Raft:
   113  		r, err := w.Raft()
   114  		if err != nil {
   115  			return err
   116  		}
   117  		*out = r
   118  	case *raft.LogStore:
   119  		store, err := w.LogStore()
   120  		if err != nil {
   121  			return err
   122  		}
   123  		*out = store
   124  	default:
   125  		return errors.Errorf("expected output of **raft.Raft or *raft.LogStore, got %T", out)
   126  	}
   127  	return nil
   128  }
   129  
   130  type withRaftOutputs interface {
   131  	Raft() (*raft.Raft, error)
   132  	LogStore() (raft.LogStore, error)
   133  }