github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/command/agent/consul/connect.go (about)

     1  package consul
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"strconv"
     7  
     8  	"github.com/hashicorp/consul/api"
     9  	"github.com/hashicorp/nomad/helper"
    10  	"github.com/hashicorp/nomad/nomad/structs"
    11  )
    12  
    13  // newConnect creates a new Consul AgentServiceConnect struct based on a Nomad
    14  // Connect struct. If the nomad Connect struct is nil, nil will be returned to
    15  // disable Connect for this service.
    16  func newConnect(serviceId string, serviceName string, nc *structs.ConsulConnect, networks structs.Networks, ports structs.AllocatedPorts) (*api.AgentServiceConnect, error) {
    17  	switch {
    18  	case nc == nil:
    19  		// no connect stanza means there is no connect service to register
    20  		return nil, nil
    21  
    22  	case nc.IsGateway():
    23  		// gateway settings are configured on the service block on the consul side
    24  		return nil, nil
    25  
    26  	case nc.IsNative():
    27  		// the service is connect native
    28  		return &api.AgentServiceConnect{Native: true}, nil
    29  
    30  	case nc.HasSidecar():
    31  		// must register the sidecar for this service
    32  		if nc.SidecarService.Port == "" {
    33  			nc.SidecarService.Port = fmt.Sprintf("%s-%s", structs.ConnectProxyPrefix, serviceName)
    34  		}
    35  		sidecarReg, err := connectSidecarRegistration(serviceId, nc.SidecarService, networks, ports)
    36  		if err != nil {
    37  			return nil, err
    38  		}
    39  		return &api.AgentServiceConnect{SidecarService: sidecarReg}, nil
    40  
    41  	default:
    42  		// a non-nil but empty connect block makes no sense
    43  		return nil, fmt.Errorf("Connect configuration empty for service %s", serviceName)
    44  	}
    45  }
    46  
    47  // newConnectGateway creates a new Consul AgentServiceConnectProxyConfig struct based on
    48  // a Nomad Connect struct. If the Nomad Connect struct does not contain a gateway, nil
    49  // will be returned as this service is not a gateway.
    50  func newConnectGateway(serviceName string, connect *structs.ConsulConnect) *api.AgentServiceConnectProxyConfig {
    51  	if !connect.IsGateway() {
    52  		return nil
    53  	}
    54  
    55  	var envoyConfig map[string]interface{}
    56  
    57  	// Populate the envoy configuration from the gateway.proxy stanza, if
    58  	// such configuration is provided.
    59  	if proxy := connect.Gateway.Proxy; proxy != nil {
    60  		envoyConfig = make(map[string]interface{})
    61  
    62  		if len(proxy.EnvoyGatewayBindAddresses) > 0 {
    63  			envoyConfig["envoy_gateway_bind_addresses"] = proxy.EnvoyGatewayBindAddresses
    64  		}
    65  
    66  		if proxy.EnvoyGatewayNoDefaultBind {
    67  			envoyConfig["envoy_gateway_no_default_bind"] = true
    68  		}
    69  
    70  		if proxy.EnvoyGatewayBindTaggedAddresses {
    71  			envoyConfig["envoy_gateway_bind_tagged_addresses"] = true
    72  		}
    73  
    74  		if proxy.EnvoyDNSDiscoveryType != "" {
    75  			envoyConfig["envoy_dns_discovery_type"] = proxy.EnvoyDNSDiscoveryType
    76  		}
    77  
    78  		if proxy.ConnectTimeout != nil {
    79  			envoyConfig["connect_timeout_ms"] = proxy.ConnectTimeout.Milliseconds()
    80  		}
    81  
    82  		if len(proxy.Config) > 0 {
    83  			for k, v := range proxy.Config {
    84  				envoyConfig[k] = v
    85  			}
    86  		}
    87  	}
    88  
    89  	return &api.AgentServiceConnectProxyConfig{Config: envoyConfig}
    90  }
    91  
    92  func connectSidecarRegistration(serviceId string, css *structs.ConsulSidecarService, networks structs.Networks, ports structs.AllocatedPorts) (*api.AgentServiceRegistration, error) {
    93  	if css == nil {
    94  		// no sidecar stanza means there is no sidecar service to register
    95  		return nil, nil
    96  	}
    97  
    98  	cMapping, err := connectPort(css.Port, networks, ports)
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  
   103  	proxy, err := connectSidecarProxy(css.Proxy, cMapping.To, networks)
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  
   108  	return &api.AgentServiceRegistration{
   109  		Tags:    helper.CopySliceString(css.Tags),
   110  		Port:    cMapping.Value,
   111  		Address: cMapping.HostIP,
   112  		Proxy:   proxy,
   113  		Checks: api.AgentServiceChecks{
   114  			{
   115  				Name:     "Connect Sidecar Listening",
   116  				TCP:      net.JoinHostPort(cMapping.HostIP, strconv.Itoa(cMapping.Value)),
   117  				Interval: "10s",
   118  			},
   119  			{
   120  				Name:         "Connect Sidecar Aliasing " + serviceId,
   121  				AliasService: serviceId,
   122  			},
   123  		},
   124  	}, nil
   125  }
   126  
   127  func connectSidecarProxy(proxy *structs.ConsulProxy, cPort int, networks structs.Networks) (*api.AgentServiceConnectProxyConfig, error) {
   128  	if proxy == nil {
   129  		proxy = new(structs.ConsulProxy)
   130  	}
   131  
   132  	expose, err := connectProxyExpose(proxy.Expose, networks)
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  
   137  	return &api.AgentServiceConnectProxyConfig{
   138  		LocalServiceAddress: proxy.LocalServiceAddress,
   139  		LocalServicePort:    proxy.LocalServicePort,
   140  		Config:              connectProxyConfig(proxy.Config, cPort),
   141  		Upstreams:           connectUpstreams(proxy.Upstreams),
   142  		Expose:              expose,
   143  	}, nil
   144  }
   145  
   146  func connectProxyExpose(expose *structs.ConsulExposeConfig, networks structs.Networks) (api.ExposeConfig, error) {
   147  	if expose == nil {
   148  		return api.ExposeConfig{}, nil
   149  	}
   150  
   151  	paths, err := connectProxyExposePaths(expose.Paths, networks)
   152  	if err != nil {
   153  		return api.ExposeConfig{}, err
   154  	}
   155  
   156  	return api.ExposeConfig{
   157  		Checks: false,
   158  		Paths:  paths,
   159  	}, nil
   160  }
   161  
   162  func connectProxyExposePaths(in []structs.ConsulExposePath, networks structs.Networks) ([]api.ExposePath, error) {
   163  	if len(in) == 0 {
   164  		return nil, nil
   165  	}
   166  
   167  	paths := make([]api.ExposePath, len(in))
   168  	for i, path := range in {
   169  		if _, exposedPort, err := connectExposePathPort(path.ListenerPort, networks); err != nil {
   170  			return nil, err
   171  		} else {
   172  			paths[i] = api.ExposePath{
   173  				ListenerPort:    exposedPort,
   174  				Path:            path.Path,
   175  				LocalPathPort:   path.LocalPathPort,
   176  				Protocol:        path.Protocol,
   177  				ParsedFromCheck: false,
   178  			}
   179  		}
   180  	}
   181  	return paths, nil
   182  }
   183  
   184  func connectUpstreams(in []structs.ConsulUpstream) []api.Upstream {
   185  	if len(in) == 0 {
   186  		return nil
   187  	}
   188  
   189  	upstreams := make([]api.Upstream, len(in))
   190  	for i, upstream := range in {
   191  		upstreams[i] = api.Upstream{
   192  			DestinationName:  upstream.DestinationName,
   193  			LocalBindPort:    upstream.LocalBindPort,
   194  			Datacenter:       upstream.Datacenter,
   195  			LocalBindAddress: upstream.LocalBindAddress,
   196  		}
   197  	}
   198  	return upstreams
   199  }
   200  
   201  func connectProxyConfig(cfg map[string]interface{}, port int) map[string]interface{} {
   202  	if cfg == nil {
   203  		cfg = make(map[string]interface{})
   204  	}
   205  	cfg["bind_address"] = "0.0.0.0"
   206  	cfg["bind_port"] = port
   207  	return cfg
   208  }
   209  
   210  func connectNetworkInvariants(networks structs.Networks) error {
   211  	if n := len(networks); n != 1 {
   212  		return fmt.Errorf("Connect only supported with exactly 1 network (found %d)", n)
   213  	}
   214  	return nil
   215  }
   216  
   217  // connectPort returns the network and port for the Connect proxy sidecar
   218  // defined for this service. An error is returned if the network and port
   219  // cannot be determined.
   220  func connectPort(portLabel string, networks structs.Networks, ports structs.AllocatedPorts) (structs.AllocatedPortMapping, error) {
   221  	if err := connectNetworkInvariants(networks); err != nil {
   222  		return structs.AllocatedPortMapping{}, err
   223  	}
   224  	mapping, ok := ports.Get(portLabel)
   225  	if !ok {
   226  		mapping = networks.Port(portLabel)
   227  		if mapping.Value > 0 {
   228  			return mapping, nil
   229  		}
   230  		return structs.AllocatedPortMapping{}, fmt.Errorf("No port of label %q defined", portLabel)
   231  	}
   232  	return mapping, nil
   233  }
   234  
   235  // connectExposePathPort returns the port for the exposed path for the exposed
   236  // proxy path.
   237  func connectExposePathPort(portLabel string, networks structs.Networks) (string, int, error) {
   238  	if err := connectNetworkInvariants(networks); err != nil {
   239  		return "", 0, err
   240  	}
   241  
   242  	mapping := networks.Port(portLabel)
   243  	if mapping.Value == 0 {
   244  		return "", 0, fmt.Errorf("No port of label %q defined", portLabel)
   245  	}
   246  
   247  	return mapping.HostIP, mapping.Value, nil
   248  }