Skip to content

Commit

Permalink
config: peers: Fix names and interfaces for peer config
Browse files Browse the repository at this point in the history
  • Loading branch information
kriskowal committed Apr 25, 2017
1 parent 2a55352 commit f2e5753
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 149 deletions.
68 changes: 35 additions & 33 deletions x/config/configurator.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,23 +40,23 @@ import (
// the different transports and their configuration parameters using the
// RegisterTransport function.
type Configurator struct {
knownTransports map[string]*compiledTransportSpec
knownChoosers map[string]*compiledChooserSpec
knownBinders map[string]*compiledBinderSpec
resolver interpolate.VariableResolver
knownTransports map[string]*compiledTransportSpec
knownPeerLists map[string]*compiledPeerListSpec
knownPeerListUpdaters map[string]*compiledPeerListUpdaterSpec
resolver interpolate.VariableResolver
}

// New sets up a new empty Configurator. The returned Configurator does not
// know about any Transports, peer Chooser lists, or peer list Binders.
// Individual TransportSpecs, ChooserSpecs, and BinderSpecs must be registered
// against it using the RegisterTransport, RegisterChooser, and RegisterBinder
// know about any Transports, peer lists, or peer list updaters.
// Individual TransportSpecs, PeerListSpecs, and PeerListUpdaterSpecs must be registered
// against it using the RegisterTransport, RegisterPeerList, and RegisterPeerListUpdater
// functions.
func New(opts ...Option) *Configurator {
c := &Configurator{
knownTransports: make(map[string]*compiledTransportSpec),
knownChoosers: make(map[string]*compiledChooserSpec),
knownBinders: make(map[string]*compiledBinderSpec),
resolver: os.LookupEnv,
knownTransports: make(map[string]*compiledTransportSpec),
knownPeerLists: make(map[string]*compiledPeerListSpec),
knownPeerListUpdaters: make(map[string]*compiledPeerListUpdaterSpec),
resolver: os.LookupEnv,
}

for _, opt := range opts {
Expand Down Expand Up @@ -97,61 +97,63 @@ func (c *Configurator) MustRegisterTransport(t TransportSpec) {
}
}

// RegisterChooser registers a ChooserSpec with the given Configurator. Returns
// an error if the ChooserSpec is invalid.
// RegisterPeerList registers a PeerListSpec with the given Configurator. Returns
// an error if the PeerListSpec is invalid.
//
// If a chooser with the same name already exists, it will be replaced.
//
// Use MustRegisterChooser to panic in the case of registration failure.
func (c *Configurator) RegisterChooser(s ChooserSpec) error {
// Use MustRegisterPeerList to panic in the case of registration failure.
func (c *Configurator) RegisterPeerList(s PeerListSpec) error {
if s.Name == "" {
return errors.New("name is required")
}

spec, err := compileChooserSpec(&s)
spec, err := compilePeerListSpec(&s)
if err != nil {
return fmt.Errorf("invalid ChooserSpec for %q: %v", s.Name, err)
return fmt.Errorf("invalid PeerListSpec for %q: %v", s.Name, err)
}

c.knownChoosers[s.Name] = spec
c.knownPeerLists[s.Name] = spec
return nil
}

// MustRegisterChooser registers the given ChooserSpec with the Configurator.
// This function panics if the ChooserSpec is invalid.
func (c *Configurator) MustRegisterChooser(s ChooserSpec) {
if err := c.RegisterChooser(s); err != nil {
// MustRegisterPeerList registers the given PeerListSpec with the Configurator.
// This function panics if the PeerListSpec is invalid.
func (c *Configurator) MustRegisterPeerList(s PeerListSpec) {
if err := c.RegisterPeerList(s); err != nil {
panic(err)
}
}

// RegisterBinder registers a BinderSpec with the given Configurator. Returns
// an error if the BinderSpec is invalid.
// RegisterPeerListUpdater registers a PeerListUpdaterSpec with the given
// Configurator.
// Returns an error if the PeerListUpdaterSpec is invalid.
//
// A binder enables custom peer list bindings, like DNS with SRV + A records or
// a task list file watcher.
//
// If a binder with the same name already exists, it will be replaced.
//
// Use MustRegisterBinder to panic if the registration fails.
func (c *Configurator) RegisterBinder(s BinderSpec) error {
// Use MustRegisterPeerListUpdater to panic if the registration fails.
func (c *Configurator) RegisterPeerListUpdater(s PeerListUpdaterSpec) error {
if s.Name == "" {
return errors.New("name is required")
}

spec, err := compileBinderSpec(&s)
spec, err := compilePeerListUpdaterSpec(&s)
if err != nil {
return fmt.Errorf("invalid BinderSpec for %q: %v", s.Name, err)
return fmt.Errorf("invalid PeerListUpdaterSpec for %q: %v", s.Name, err)
}

c.knownBinders[s.Name] = spec
c.knownPeerListUpdaters[s.Name] = spec
return nil
}

// MustRegisterBinder registers the given BinderSpec with the Configurator.
// This function panics if the BinderSpec is invalid.
func (c *Configurator) MustRegisterBinder(s BinderSpec) {
if err := c.RegisterBinder(s); err != nil {
// MustRegisterPeerListUpdater registers the given PeerListUpdaterSpec with the
// Configurator.
// This function panics if the PeerListUpdaterSpec is invalid.
func (c *Configurator) MustRegisterPeerListUpdater(s PeerListUpdaterSpec) {
if err := c.RegisterPeerListUpdater(s); err != nil {
panic(err)
}
}
Expand Down
14 changes: 7 additions & 7 deletions x/config/kit.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ package config

import "reflect"

// Kit carries internal dependencies for building peer choosers.
// Kit carries internal dependencies for building peer lists.
// The kit gets threaded through transport, outbound, and inbound builders
// so they can thread the kit through functions like BuildChooser on a
// ChooserConfig.
// so they can thread the kit through functions like BuildPeerList on a
// PeerListConfig.
type Kit struct {
c *Configurator

Expand All @@ -38,10 +38,10 @@ func (k *Kit) ServiceName() string { return k.name }

var _typeOfKit = reflect.TypeOf((*Kit)(nil))

func (k *Kit) binder(name string) *compiledBinderSpec {
return k.c.knownBinders[name]
func (k *Kit) peerListSpec(name string) *compiledPeerListSpec {
return k.c.knownPeerLists[name]
}

func (k *Kit) chooser(name string) *compiledChooserSpec {
return k.c.knownChoosers[name]
func (k *Kit) peerListUpdaterSpec(name string) *compiledPeerListUpdaterSpec {
return k.c.knownPeerListUpdaters[name]
}
85 changes: 45 additions & 40 deletions x/config/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ import (
// Configuration structs can use standard Go primitive types, time.Duration,
// maps, slices, and other similar structs. For example only, an outbound might
// accept a config containing an array of host:port structs (In practice, an
// outbound would use a ChooserConfig to build a peer.Chooser).
// outbound would use a PeerListConfig to build a peer.ChooserList).
//
// type Peer struct {
// Host string
Expand Down Expand Up @@ -217,7 +217,7 @@ type TransportSpec struct {
// the first thing inside their BuildInbound.
}

// ChooserSpec specifies the configuration parameters for an outbound peer
// PeerListSpec specifies the configuration parameters for an outbound peer
// chooser (load balancer or sharding). These specifications are registered
// against a Configurator to teach it how to parse the configuration for that
// peer chooser and build instances of it.
Expand All @@ -232,7 +232,7 @@ type TransportSpec struct {
// with: dns-srv
// choose: random
// service: fortune.yarpc.io
type ChooserSpec struct {
type PeerListSpec struct {
Name string

// A function in the shape,
Expand All @@ -242,16 +242,16 @@ type ChooserSpec struct {
// Where C is a struct or pointer to a struct defining the configuration
// parameters accepted by this peer chooser.
//
// BuildChooser is required.
BuildChooser interface{}
// BuildPeerList is required.
BuildPeerList interface{}
}

// BinderSpec specifies the configuration parameters for an outbound peer
// PeerListUpdaterSpec specifies the configuration parameters for an outbound peer
// binding (like DNS). These specifications are registered against a
// Configurator to teach it how to parse the configuration for that peer binder
// and build instances of it.
//
// Every BinderSpec MUST have a BuildBinder function.
// Every PeerListUpdaterSpec MUST have a BuildPeerListUpdater function.
//
// For example, if we register a "dns-srv" peer list binder and a "random" peer
// chooser, we can use "with: dns-srv" and "choose: random" to select a random
Expand All @@ -263,7 +263,7 @@ type ChooserSpec struct {
// with: dns-srv
// choose: random
// service: fortune.yarpc.io
type BinderSpec struct {
type PeerListUpdaterSpec struct {
// Name of the peer selection strategy
Name string

Expand All @@ -280,10 +280,10 @@ type BinderSpec struct {
// For example, the HTTP and TChannel outbound configurations embed a peer
// chooser configuration. Peer choosers support a single peer or arrays of
// peers. Using the "with" property, an outbound can use an alternate peer
// chooser registered by name on a YARPC Configurator using a ChooserSpec.
// chooser registered by name on a YARPC Configurator using a PeerListSpec.
//
// BuildBinder is required.
BuildBinder interface{}
// BuildPeerListUpdater is required.
BuildPeerListUpdater interface{}
}

var (
Expand All @@ -292,7 +292,8 @@ var (
_typeOfInbound = reflect.TypeOf((*transport.Inbound)(nil)).Elem()
_typeOfUnaryOutbound = reflect.TypeOf((*transport.UnaryOutbound)(nil)).Elem()
_typeOfOnewayOutbound = reflect.TypeOf((*transport.OnewayOutbound)(nil)).Elem()
_typeOfChooser = reflect.TypeOf((*peer.ChooserList)(nil)).Elem()
_typeOfPeerTransport = reflect.TypeOf((*peer.Transport)(nil)).Elem()
_typeOfPeerList = reflect.TypeOf((*peer.ChooserList)(nil)).Elem()
_typeOfBinder = reflect.TypeOf((*peer.Binder)(nil)).Elem()
)

Expand Down Expand Up @@ -456,87 +457,89 @@ func validateConfigFunc(t reflect.Type, outputType reflect.Type) error {
return nil
}

// Compiled internal representation of a user-specified ChooserSpec.
type compiledChooserSpec struct {
Name string
Chooser *configSpec
// Compiled internal representation of a user-specified PeerListSpec.
type compiledPeerListSpec struct {
Name string
PeerList *configSpec
}

func compileChooserSpec(spec *ChooserSpec) (*compiledChooserSpec, error) {
out := compiledChooserSpec{Name: spec.Name}
func compilePeerListSpec(spec *PeerListSpec) (*compiledPeerListSpec, error) {
out := compiledPeerListSpec{Name: spec.Name}

if spec.Name == "" {
return nil, errors.New("Name is required")
}

if spec.BuildChooser == nil {
return nil, errors.New("BuildChooser is required")
if spec.BuildPeerList == nil {
return nil, errors.New("BuildPeerList is required")
}

buildChooser, err := compileChooserConfig(spec.BuildChooser)
buildPeerList, err := compilePeerListConfig(spec.BuildPeerList)
if err != nil {
return nil, err
}
out.Chooser = buildChooser
out.PeerList = buildPeerList

return &out, nil
}

func compileChooserConfig(build interface{}) (*configSpec, error) {
func compilePeerListConfig(build interface{}) (*configSpec, error) {
v := reflect.ValueOf(build)
t := v.Type()

var err error
switch {
case t.Kind() != reflect.Func:
err = errors.New("must be a function")
case t.NumIn() != 2:
err = fmt.Errorf("must accept exactly two arguments, found %v", t.NumIn())
case t.NumIn() != 3:
err = fmt.Errorf("must accept exactly three arguments, found %v", t.NumIn())
case !isDecodable(t.In(0)):
err = fmt.Errorf("must accept a struct or struct pointer as its first argument, found %v", t.In(0))
case t.In(1) != _typeOfKit:
err = fmt.Errorf("must accept a %v as its second argument, found %v", _typeOfKit, t.In(1))
case t.In(1) != _typeOfPeerTransport:
err = fmt.Errorf("must accept a %v as its second argument, found %v", _typeOfPeerTransport, t.In(1))
case t.In(2) != _typeOfKit:
err = fmt.Errorf("must accept a %v as its third argument, found %v", _typeOfKit, t.In(2))
case t.NumOut() != 2:
err = fmt.Errorf("must return exactly two results, found %v", t.NumOut())
case t.Out(0) != _typeOfChooser:
case t.Out(0) != _typeOfPeerList:
err = fmt.Errorf("must return a peer.ChooserList as its first result, found %v", t.Out(0))
case t.Out(1) != _typeOfError:
err = fmt.Errorf("must return an error as its second result, found %v", t.Out(1))
}

if err != nil {
return nil, fmt.Errorf("invalid BuildChooser %v: %v", t, err)
return nil, fmt.Errorf("invalid BuildPeerList %v: %v", t, err)
}

return &configSpec{inputType: t.In(0), factory: v}, nil
}

type compiledBinderSpec struct {
Name string
Binder *configSpec
type compiledPeerListUpdaterSpec struct {
Name string
PeerListUpdater *configSpec
}

func compileBinderSpec(spec *BinderSpec) (*compiledBinderSpec, error) {
out := compiledBinderSpec{Name: spec.Name}
func compilePeerListUpdaterSpec(spec *PeerListUpdaterSpec) (*compiledPeerListUpdaterSpec, error) {
out := compiledPeerListUpdaterSpec{Name: spec.Name}

if spec.Name == "" {
return nil, errors.New("Name is required")
}

if spec.BuildBinder == nil {
return nil, errors.New("BuildBinder is required")
if spec.BuildPeerListUpdater == nil {
return nil, errors.New("BuildPeerListUpdater is required")
}

buildBinder, err := compileBinderConfig(spec.BuildBinder)
buildPeerListUpdater, err := compilePeerListUpdaterConfig(spec.BuildPeerListUpdater)
if err != nil {
return nil, err
}
out.Binder = buildBinder
out.PeerListUpdater = buildPeerListUpdater

return &out, nil
}

func compileBinderConfig(build interface{}) (*configSpec, error) {
func compilePeerListUpdaterConfig(build interface{}) (*configSpec, error) {
v := reflect.ValueOf(build)
t := v.Type()

Expand All @@ -548,6 +551,8 @@ func compileBinderConfig(build interface{}) (*configSpec, error) {
err = fmt.Errorf("must accept exactly two arguments, found %v", t.NumIn())
case !isDecodable(t.In(0)):
err = fmt.Errorf("must accept a struct or struct pointer as its first argument, found %v", t.In(0))
// TODO additionally, the peer list updater config struct must have a field
// with the name of the peer list updater.
case t.In(1) != _typeOfKit:
err = fmt.Errorf("must accept a %v as its second argument, found %v", _typeOfKit, t.In(1))
case t.NumOut() != 2:
Expand All @@ -559,7 +564,7 @@ func compileBinderConfig(build interface{}) (*configSpec, error) {
}

if err != nil {
return nil, fmt.Errorf("invalid BuildBinder %v: %v", t, err)
return nil, fmt.Errorf("invalid BuildPeerListUpdater %v: %v", t, err)
}

return &configSpec{inputType: t.In(0), factory: v}, nil
Expand Down

0 comments on commit f2e5753

Please sign in to comment.