mirror of
https://github.com/kemko/nomad.git
synced 2026-01-06 18:35:44 +03:00
consul/connect: Add support for Connect terminating gateways
This PR implements Nomad built-in support for running Consul Connect terminating gateways. Such a gateway can be used by services running inside the service mesh to access "legacy" services running outside the service mesh while still making use of Consul's service identity based networking and ACL policies. https://www.consul.io/docs/connect/gateways/terminating-gateway These gateways are declared as part of a task group level service definition within the connect stanza. service { connect { gateway { proxy { // envoy proxy configuration } terminating { // terminating-gateway configuration entry } } } } Currently Envoy is the only supported gateway implementation in Consul. The gateay task can be customized by configuring the connect.sidecar_task block. When the gateway.terminating field is set, Nomad will write/update the Configuration Entry into Consul on job submission. Because CEs are global in scope and there may be more than one Nomad cluster communicating with Consul, there is an assumption that any terminating gateway defined in Nomad for a particular service will be the same among Nomad clusters. Gateways require Consul 1.8.0+, checked by a node constraint. Closes #9445
This commit is contained in:
@@ -302,8 +302,8 @@ type ConsulGateway struct {
|
||||
// Ingress represents the Consul Configuration Entry for an Ingress Gateway.
|
||||
Ingress *ConsulIngressConfigEntry `hcl:"ingress,block"`
|
||||
|
||||
// Terminating is not yet supported.
|
||||
// Terminating *ConsulTerminatingConfigEntry
|
||||
// Terminating represents the Consul Configuration Entry for a Terminating Gateway.
|
||||
Terminating *ConsulTerminatingConfigEntry `hcl:"terminating,block"`
|
||||
|
||||
// Mesh is not yet supported.
|
||||
// Mesh *ConsulMeshConfigEntry
|
||||
@@ -315,6 +315,7 @@ func (g *ConsulGateway) Canonicalize() {
|
||||
}
|
||||
g.Proxy.Canonicalize()
|
||||
g.Ingress.Canonicalize()
|
||||
g.Terminating.Canonicalize()
|
||||
}
|
||||
|
||||
func (g *ConsulGateway) Copy() *ConsulGateway {
|
||||
@@ -323,8 +324,9 @@ func (g *ConsulGateway) Copy() *ConsulGateway {
|
||||
}
|
||||
|
||||
return &ConsulGateway{
|
||||
Proxy: g.Proxy.Copy(),
|
||||
Ingress: g.Ingress.Copy(),
|
||||
Proxy: g.Proxy.Copy(),
|
||||
Ingress: g.Ingress.Copy(),
|
||||
Terminating: g.Terminating.Copy(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,8 +337,8 @@ type ConsulGatewayBindAddress struct {
|
||||
}
|
||||
|
||||
var (
|
||||
// defaultConnectTimeout is the default amount of time a connect gateway will
|
||||
// wait for a response from an upstream service (same as consul)
|
||||
// defaultGatewayConnectTimeout is the default amount of time connections to
|
||||
// upstreams are allowed before timing out.
|
||||
defaultGatewayConnectTimeout = 5 * time.Second
|
||||
)
|
||||
|
||||
@@ -349,6 +351,7 @@ type ConsulGatewayProxy struct {
|
||||
EnvoyGatewayBindTaggedAddresses bool `mapstructure:"envoy_gateway_bind_tagged_addresses" hcl:"envoy_gateway_bind_tagged_addresses,optional"`
|
||||
EnvoyGatewayBindAddresses map[string]*ConsulGatewayBindAddress `mapstructure:"envoy_gateway_bind_addresses" hcl:"envoy_gateway_bind_addresses,block"`
|
||||
EnvoyGatewayNoDefaultBind bool `mapstructure:"envoy_gateway_no_default_bind" hcl:"envoy_gateway_no_default_bind,optional"`
|
||||
EnvoyDNSDiscoveryType string `mapstructure:"envoy_dns_discovery_type" hcl:"envoy_dns_discovery_type,optional"`
|
||||
Config map[string]interface{} `hcl:"config,block"` // escape hatch envoy config
|
||||
}
|
||||
|
||||
@@ -397,6 +400,7 @@ func (p *ConsulGatewayProxy) Copy() *ConsulGatewayProxy {
|
||||
EnvoyGatewayBindTaggedAddresses: p.EnvoyGatewayBindTaggedAddresses,
|
||||
EnvoyGatewayBindAddresses: binds,
|
||||
EnvoyGatewayNoDefaultBind: p.EnvoyGatewayNoDefaultBind,
|
||||
EnvoyDNSDiscoveryType: p.EnvoyDNSDiscoveryType,
|
||||
Config: config,
|
||||
}
|
||||
}
|
||||
@@ -549,9 +553,74 @@ func (e *ConsulIngressConfigEntry) Copy() *ConsulIngressConfigEntry {
|
||||
}
|
||||
}
|
||||
|
||||
// ConsulTerminatingConfigEntry is not yet supported.
|
||||
// type ConsulTerminatingConfigEntry struct {
|
||||
// }
|
||||
type ConsulLinkedService struct {
|
||||
Name string `hcl:"name,optional"`
|
||||
CAFile string `hcl:"ca_file,optional"`
|
||||
CertFile string `hcl:"cert_file,optional"`
|
||||
KeyFile string `hcl:"key_file,optional"`
|
||||
SNI string `hcl:"sni,optional"`
|
||||
}
|
||||
|
||||
func (s *ConsulLinkedService) Canonicalize() {
|
||||
// nothing to do for now
|
||||
}
|
||||
|
||||
func (s *ConsulLinkedService) Copy() *ConsulLinkedService {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &ConsulLinkedService{
|
||||
Name: s.Name,
|
||||
CAFile: s.CAFile,
|
||||
CertFile: s.CertFile,
|
||||
KeyFile: s.KeyFile,
|
||||
SNI: s.SNI,
|
||||
}
|
||||
}
|
||||
|
||||
// ConsulTerminatingConfigEntry represents the Consul Configuration Entry type
|
||||
// for a Terminating Gateway.
|
||||
//
|
||||
// https://www.consul.io/docs/agent/config-entries/terminating-gateway#available-fields
|
||||
type ConsulTerminatingConfigEntry struct {
|
||||
// Namespace is not yet supported.
|
||||
// Namespace string
|
||||
|
||||
Services []*ConsulLinkedService `hcl:"service,block"`
|
||||
}
|
||||
|
||||
func (e *ConsulTerminatingConfigEntry) Canonicalize() {
|
||||
if e == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(e.Services) == 0 {
|
||||
e.Services = nil
|
||||
}
|
||||
|
||||
for _, service := range e.Services {
|
||||
service.Canonicalize()
|
||||
}
|
||||
}
|
||||
|
||||
func (e *ConsulTerminatingConfigEntry) Copy() *ConsulTerminatingConfigEntry {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var services []*ConsulLinkedService = nil
|
||||
if n := len(e.Services); n > 0 {
|
||||
services = make([]*ConsulLinkedService, n)
|
||||
for i := 0; i < n; i++ {
|
||||
services[i] = e.Services[i].Copy()
|
||||
}
|
||||
}
|
||||
|
||||
return &ConsulTerminatingConfigEntry{
|
||||
Services: services,
|
||||
}
|
||||
}
|
||||
|
||||
// ConsulMeshConfigEntry is not yet supported.
|
||||
// type ConsulMeshConfigEntry struct {
|
||||
|
||||
@@ -291,7 +291,10 @@ func TestService_ConsulGateway_Canonicalize(t *testing.T) {
|
||||
}
|
||||
cg.Canonicalize()
|
||||
require.Equal(t, timeToPtr(5*time.Second), cg.Proxy.ConnectTimeout)
|
||||
require.True(t, cg.Proxy.EnvoyGatewayBindTaggedAddresses)
|
||||
require.Nil(t, cg.Proxy.EnvoyGatewayBindAddresses)
|
||||
require.True(t, cg.Proxy.EnvoyGatewayNoDefaultBind)
|
||||
require.Empty(t, cg.Proxy.EnvoyDNSDiscoveryType)
|
||||
require.Nil(t, cg.Proxy.Config)
|
||||
require.Nil(t, cg.Ingress.Listeners)
|
||||
})
|
||||
@@ -314,6 +317,7 @@ func TestService_ConsulGateway_Copy(t *testing.T) {
|
||||
"listener2": {Address: "10.0.0.1", Port: 2001},
|
||||
},
|
||||
EnvoyGatewayNoDefaultBind: true,
|
||||
EnvoyDNSDiscoveryType: "STRICT_DNS",
|
||||
Config: map[string]interface{}{
|
||||
"foo": "bar",
|
||||
"baz": 3,
|
||||
@@ -334,6 +338,11 @@ func TestService_ConsulGateway_Copy(t *testing.T) {
|
||||
}},
|
||||
},
|
||||
},
|
||||
Terminating: &ConsulTerminatingConfigEntry{
|
||||
Services: []*ConsulLinkedService{{
|
||||
Name: "linked-service1",
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("complete", func(t *testing.T) {
|
||||
@@ -418,3 +427,47 @@ func TestService_ConsulIngressConfigEntry_Copy(t *testing.T) {
|
||||
require.Equal(t, entry, result)
|
||||
})
|
||||
}
|
||||
|
||||
func TestService_ConsulTerminatingConfigEntry_Canonicalize(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("nil", func(t *testing.T) {
|
||||
c := (*ConsulTerminatingConfigEntry)(nil)
|
||||
c.Canonicalize()
|
||||
require.Nil(t, c)
|
||||
})
|
||||
|
||||
t.Run("empty services", func(t *testing.T) {
|
||||
c := &ConsulTerminatingConfigEntry{
|
||||
Services: []*ConsulLinkedService{},
|
||||
}
|
||||
c.Canonicalize()
|
||||
require.Nil(t, c.Services)
|
||||
})
|
||||
}
|
||||
|
||||
func TestService_ConsulTerminatingConfigEntry_Copy(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("nil", func(t *testing.T) {
|
||||
result := (*ConsulIngressConfigEntry)(nil).Copy()
|
||||
require.Nil(t, result)
|
||||
})
|
||||
|
||||
entry := &ConsulTerminatingConfigEntry{
|
||||
Services: []*ConsulLinkedService{{
|
||||
Name: "servic1",
|
||||
}, {
|
||||
Name: "service2",
|
||||
CAFile: "ca_file.pem",
|
||||
CertFile: "cert_file.pem",
|
||||
KeyFile: "key_file.pem",
|
||||
SNI: "sni.terminating.consul",
|
||||
}},
|
||||
}
|
||||
|
||||
t.Run("complete", func(t *testing.T) {
|
||||
result := entry.Copy()
|
||||
require.Equal(t, entry, result)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user