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 }