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

     1  // Copyright 2018 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package rafttransport
     5  
     6  import (
     7  	"time"
     8  
     9  	"github.com/hashicorp/raft"
    10  	"github.com/juju/clock"
    11  	"github.com/juju/errors"
    12  	"github.com/juju/pubsub"
    13  	"gopkg.in/juju/worker.v1"
    14  	"gopkg.in/juju/worker.v1/dependency"
    15  
    16  	"github.com/juju/juju/agent"
    17  	"github.com/juju/juju/api"
    18  	"github.com/juju/juju/apiserver/apiserverhttp"
    19  	"github.com/juju/juju/apiserver/httpcontext"
    20  )
    21  
    22  // raftNetworkTimeout is how long the transport should wait before
    23  // failing a network interaction.
    24  const raftNetworkTimeout = 30 * time.Second
    25  
    26  // ManifoldConfig holds the information necessary to run an apiserver-based
    27  // raft transport worker in a dependency.Engine.
    28  type ManifoldConfig struct {
    29  	ClockName         string
    30  	AgentName         string
    31  	AuthenticatorName string
    32  	HubName           string
    33  	MuxName           string
    34  
    35  	DialConn  DialConnFunc
    36  	NewWorker func(Config) (worker.Worker, error)
    37  
    38  	// Path is the path of the raft HTTP endpoint.
    39  	Path string
    40  }
    41  
    42  // Validate validates the manifold configuration.
    43  func (config ManifoldConfig) Validate() error {
    44  	if config.AgentName == "" {
    45  		return errors.NotValidf("empty AgentName")
    46  	}
    47  	if config.AuthenticatorName == "" {
    48  		return errors.NotValidf("empty AuthenticatorName")
    49  	}
    50  	if config.HubName == "" {
    51  		return errors.NotValidf("empty HubName")
    52  	}
    53  	if config.MuxName == "" {
    54  		return errors.NotValidf("empty MuxName")
    55  	}
    56  	if config.DialConn == nil {
    57  		return errors.NotValidf("nil DialConn")
    58  	}
    59  	if config.NewWorker == nil {
    60  		return errors.NotValidf("nil NewWorker")
    61  	}
    62  	if config.Path == "" {
    63  		return errors.NotValidf("empty Path")
    64  	}
    65  	return nil
    66  }
    67  
    68  // Manifold returns a dependency.Manifold that will run an apiserver-based
    69  // raft transport worker.
    70  func Manifold(config ManifoldConfig) dependency.Manifold {
    71  	return dependency.Manifold{
    72  		Inputs: []string{
    73  			config.ClockName,
    74  			config.AgentName,
    75  			config.AuthenticatorName,
    76  			config.HubName,
    77  			config.MuxName,
    78  		},
    79  		Start:  config.start,
    80  		Output: transportOutput,
    81  	}
    82  }
    83  
    84  // start is a method on ManifoldConfig because it's more readable than a closure.
    85  func (config ManifoldConfig) start(context dependency.Context) (worker.Worker, error) {
    86  	if err := config.Validate(); err != nil {
    87  		return nil, errors.Trace(err)
    88  	}
    89  
    90  	var clk clock.Clock
    91  	if err := context.Get(config.ClockName, &clk); err != nil {
    92  		return nil, errors.Trace(err)
    93  	}
    94  
    95  	var agent agent.Agent
    96  	if err := context.Get(config.AgentName, &agent); err != nil {
    97  		return nil, errors.Trace(err)
    98  	}
    99  
   100  	var authenticator httpcontext.Authenticator
   101  	if err := context.Get(config.AuthenticatorName, &authenticator); err != nil {
   102  		return nil, errors.Trace(err)
   103  	}
   104  
   105  	var hub *pubsub.StructuredHub
   106  	if err := context.Get(config.HubName, &hub); err != nil {
   107  		return nil, errors.Trace(err)
   108  	}
   109  
   110  	var mux *apiserverhttp.Mux
   111  	if err := context.Get(config.MuxName, &mux); err != nil {
   112  		return nil, errors.Trace(err)
   113  	}
   114  
   115  	apiInfo, ok := agent.CurrentConfig().APIInfo()
   116  	if !ok {
   117  		return nil, dependency.ErrMissing
   118  	}
   119  	certPool, err := api.CreateCertPool(apiInfo.CACert)
   120  	if err != nil {
   121  		return nil, errors.Trace(err)
   122  	}
   123  
   124  	return config.NewWorker(Config{
   125  		APIInfo:       apiInfo,
   126  		Authenticator: authenticator,
   127  		DialConn:      config.DialConn,
   128  		Hub:           hub,
   129  		Mux:           mux,
   130  		Path:          config.Path,
   131  		LocalID:       raft.ServerID(agent.CurrentConfig().Tag().Id()),
   132  		TLSConfig:     api.NewTLSConfig(certPool),
   133  		Clock:         clk,
   134  		Timeout:       raftNetworkTimeout,
   135  	})
   136  }
   137  
   138  func transportOutput(in worker.Worker, out interface{}) error {
   139  	t, ok := in.(raft.Transport)
   140  	if !ok {
   141  		return errors.Errorf("expected input of type %T, got %T", t, in)
   142  	}
   143  	tout, ok := out.(*raft.Transport)
   144  	if ok {
   145  		*tout = t
   146  		return nil
   147  	}
   148  	return errors.Errorf("expected output of type %T, got %T", tout, out)
   149  }