go.uber.org/yarpc@v1.72.1/transport/tchannel/config.go (about)

     1  // Copyright (c) 2022 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package tchannel
    22  
    23  import (
    24  	"errors"
    25  	"fmt"
    26  	"time"
    27  
    28  	"go.uber.org/yarpc/api/peer"
    29  	"go.uber.org/yarpc/api/transport"
    30  	yarpctls "go.uber.org/yarpc/api/transport/tls"
    31  	"go.uber.org/yarpc/peer/hostport"
    32  	"go.uber.org/yarpc/yarpcconfig"
    33  )
    34  
    35  // TransportConfig configures a shared TChannel transport. This is shared
    36  // between all TChannel outbounds and inbounds of a Dispatcher.
    37  //
    38  //  transports:
    39  //    tchannel:
    40  //      connTimeout: 500ms
    41  //      connBackoff:
    42  //        exponential:
    43  //          first: 10ms
    44  //          max: 30s
    45  type TransportConfig struct {
    46  	ConnTimeout time.Duration       `config:"connTimeout"`
    47  	ConnBackoff yarpcconfig.Backoff `config:"connBackoff"`
    48  }
    49  
    50  // InboundConfig configures a TChannel inbound.
    51  //
    52  // 	inbounds:
    53  // 	  tchannel:
    54  // 	    address: :4040
    55  //      tls:
    56  //        mode: permissive
    57  //
    58  // At most one TChannel inbound may be defined in a single YARPC service.
    59  type InboundConfig struct {
    60  	// Address to listen on. Defaults to ":0" (all network interfaces and a
    61  	// random OS-assigned port).
    62  	Address string `config:"address,interpolate"`
    63  	// TLS configuration of the inbound.
    64  	TLS InboundTLSConfig `config:"tls"`
    65  }
    66  
    67  // InboundTLSConfig specifies the TLS configuration of the tchannel inbound.
    68  type InboundTLSConfig struct {
    69  	// Mode when set to Permissive or Enforced enables TLS inbound and
    70  	// TLS configuration must be passed as an inbound option.
    71  	Mode yarpctls.Mode `config:"mode,interpolate"`
    72  }
    73  
    74  // OutboundConfig configures a TChannel outbound.
    75  //
    76  // 	outbounds:
    77  // 	  myservice:
    78  // 	    tchannel:
    79  // 	      peer: 127.0.0.1:4040
    80  //
    81  type OutboundConfig struct {
    82  	yarpcconfig.PeerChooser
    83  	// TLS config enables TLS outbound.
    84  	//
    85  	//  tchannel:
    86  	//    peer: 127.0.0.1:4040
    87  	//    tls:
    88  	//      mode: enforced
    89  	//      spiffe-ids:
    90  	//        - destination-id
    91  	TLS OutboundTLSConfig `config:"tls"`
    92  
    93  	// EnableBufferReuse config controls usage of a buffer pool
    94  	// to read the response body.
    95  	EnableBufferReuse bool `config:"enable-buffer-reuse"`
    96  }
    97  
    98  // OutboundTLSConfig configures TLS for a TChannel outbound.
    99  type OutboundTLSConfig struct {
   100  	// Mode when set to Enforced enables TLS outbound and
   101  	// outbound TLS configuration provider will be used for fetching tls.Config.
   102  	Mode yarpctls.Mode `config:"mode,interpolate"`
   103  	// SpiffeIDs is a list of the accepted server spiffe IDs.
   104  	SpiffeIDs []string `config:"spiffe-ids"`
   105  }
   106  
   107  // getPeerTransport returns peer transport to be used in peer chooser creation.
   108  func (c OutboundConfig) getPeerTransport(transport *Transport, destName string) (peer.Transport, error) {
   109  	if c.TLS.Mode == yarpctls.Disabled {
   110  		return transport, nil
   111  	}
   112  
   113  	if c.TLS.Mode == yarpctls.Permissive {
   114  		return nil, errors.New("outbound does not support permissive TLS mode")
   115  	}
   116  
   117  	if transport.outboundTLSConfigProvider == nil {
   118  		return nil, errors.New("outbound TLS enforced but outbound TLS config provider is nil")
   119  	}
   120  
   121  	config, err := transport.outboundTLSConfigProvider.ClientTLSConfig(c.TLS.SpiffeIDs)
   122  	if err != nil {
   123  		return nil, err
   124  	}
   125  
   126  	return transport.CreateTLSOutboundChannel(config, destName)
   127  }
   128  
   129  // TransportSpec returns a TransportSpec for the TChannel unary transport.
   130  func TransportSpec(opts ...Option) yarpcconfig.TransportSpec {
   131  	var ts transportSpec
   132  	for _, o := range opts {
   133  		switch opt := o.(type) {
   134  		case TransportOption:
   135  			ts.transportOptions = append(ts.transportOptions, opt)
   136  		default:
   137  			panic(fmt.Sprintf("unknown option of type %T: %v", o, o))
   138  		}
   139  	}
   140  	return ts.Spec()
   141  }
   142  
   143  // transportSpec holds the configurable parts of the TChannel TransportSpec.
   144  //
   145  // These are usually runtime dependencies that cannot be parsed from
   146  // configuration.
   147  type transportSpec struct {
   148  	transportOptions []TransportOption
   149  }
   150  
   151  func (ts *transportSpec) Spec() yarpcconfig.TransportSpec {
   152  	return yarpcconfig.TransportSpec{
   153  		Name:               TransportName,
   154  		BuildTransport:     ts.buildTransport,
   155  		BuildInbound:       ts.buildInbound,
   156  		BuildUnaryOutbound: ts.buildUnaryOutbound,
   157  	}
   158  }
   159  
   160  func (ts *transportSpec) buildTransport(tc *TransportConfig, k *yarpcconfig.Kit) (transport.Transport, error) {
   161  	options := newTransportOptions()
   162  
   163  	for _, opt := range ts.transportOptions {
   164  		opt(&options)
   165  	}
   166  
   167  	if tc.ConnTimeout != 0 {
   168  		options.connTimeout = tc.ConnTimeout
   169  	}
   170  
   171  	strategy, err := tc.ConnBackoff.Strategy()
   172  	if err != nil {
   173  		return nil, err
   174  	}
   175  	options.connBackoffStrategy = strategy
   176  
   177  	if options.name != "" {
   178  		return nil, fmt.Errorf("TChannel TransportSpec does not accept ServiceName")
   179  	}
   180  
   181  	if options.addr != "" {
   182  		return nil, fmt.Errorf("TChannel TransportSpec does not accept ListenAddr")
   183  	}
   184  
   185  	if options.ch != nil {
   186  		return nil, fmt.Errorf("TChannel TransportSpec does not accept WithChannel")
   187  	}
   188  
   189  	options.name = k.ServiceName()
   190  	return options.newTransport(), nil
   191  }
   192  
   193  func (ts *transportSpec) buildInbound(c *InboundConfig, t transport.Transport, k *yarpcconfig.Kit) (transport.Inbound, error) {
   194  	if c.Address == "" {
   195  		return nil, fmt.Errorf("inbound address is required")
   196  	}
   197  
   198  	trans := t.(*Transport)
   199  	if trans.addr != "" {
   200  		// We ensure that trans.addr is empty when buildTransport is called,
   201  		// so if the string is non-empty right now, another TChannel inbound
   202  		// already filled it with a value.
   203  		return nil, fmt.Errorf("at most one TChannel inbound may be specified")
   204  	}
   205  
   206  	trans.addr = c.Address
   207  	// Override inbound TLS mode when not set by an option.
   208  	if trans.inboundTLSMode == nil {
   209  		trans.inboundTLSMode = &c.TLS.Mode
   210  	}
   211  	return trans.NewInbound(), nil
   212  }
   213  
   214  func (ts *transportSpec) buildUnaryOutbound(oc *OutboundConfig, t transport.Transport, k *yarpcconfig.Kit) (transport.UnaryOutbound, error) {
   215  	x := t.(*Transport)
   216  	pt, err := oc.getPeerTransport(x, k.OutboundServiceName())
   217  	if err != nil {
   218  		return nil, err
   219  	}
   220  	chooser, err := oc.BuildPeerChooser(pt, hostport.Identify, k)
   221  	if err != nil {
   222  		return nil, err
   223  	}
   224  	return x.NewOutbound(chooser, WithReuseBuffer(oc.EnableBufferReuse)), nil
   225  }