open-nomad/nomad/job_endpoint_hook_connect_test.go
Tim Gross f61f801e77
provide RPCContext to all RPC handlers (#15430)
Upcoming work to instrument the rate of RPC requests by consumer (and eventually
rate limit) requires that we thread the `RPCContext` through all RPC
handlers so that we can access the underlying connection. This changeset adds
the context to everywhere we intend to initially support it and intentionally
excludes streaming RPCs and client RPCs.

To improve the ergonomics of adding the context everywhere its needed and to
clarify the requirements of dynamic vs static handlers, I've also done a good
bit of refactoring here:

* canonicalized the RPC handler fields so they're as close to identical as
  possible without introducing unused fields (i.e. I didn't add loggers if the
  handler doesn't use them already).
* canonicalized the imports in the handler files.
* added a `NewExampleEndpoint` function for each handler that ensures we're
  constructing the handlers with the required arguments.
* reordered the registration in server.go to match the order of the files (to
  make it easier to see if we've missed one), and added a bunch of commentary
  there as to what the difference between static and dynamic handlers is.
2022-12-01 10:05:15 -05:00

1004 lines
29 KiB
Go

package nomad
import (
"fmt"
"testing"
"time"
"github.com/hashicorp/nomad/ci"
"github.com/hashicorp/nomad/helper/pointer"
"github.com/hashicorp/nomad/helper/testlog"
"github.com/hashicorp/nomad/nomad/mock"
"github.com/hashicorp/nomad/nomad/structs"
"github.com/stretchr/testify/require"
)
func TestJobEndpointConnect_isSidecarForService(t *testing.T) {
ci.Parallel(t)
cases := []struct {
task *structs.Task
sidecar string
exp bool
}{
{
&structs.Task{},
"api",
false,
},
{
&structs.Task{
Kind: "connect-proxy:api",
},
"api",
true,
},
{
&structs.Task{
Kind: "connect-proxy:api",
},
"db",
false,
},
{
&structs.Task{
Kind: "api",
},
"api",
false,
},
}
for _, c := range cases {
require.Equal(t, c.exp, isSidecarForService(c.task, c.sidecar))
}
}
func TestJobEndpointConnect_groupConnectHook(t *testing.T) {
ci.Parallel(t)
// Test that connect-proxy task is inserted for backend service
job := mock.Job()
job.Meta = map[string]string{
"backend_name": "backend",
"admin_name": "admin",
}
job.TaskGroups[0] = &structs.TaskGroup{
Networks: structs.Networks{{
Mode: "bridge",
}},
Services: []*structs.Service{{
Name: "${NOMAD_META_backend_name}",
PortLabel: "8080",
Connect: &structs.ConsulConnect{
SidecarService: &structs.ConsulSidecarService{},
},
}, {
Name: "${NOMAD_META_admin_name}",
PortLabel: "9090",
Connect: &structs.ConsulConnect{
SidecarService: &structs.ConsulSidecarService{},
},
}},
}
// Expected tasks
tgExp := job.TaskGroups[0].Copy()
tgExp.Tasks = []*structs.Task{
newConnectSidecarTask("backend"),
newConnectSidecarTask("admin"),
}
tgExp.Services[0].Name = "backend"
tgExp.Services[1].Name = "admin"
// Expect sidecar tasks to be in canonical form.
tgExp.Tasks[0].Canonicalize(job, tgExp)
tgExp.Tasks[1].Canonicalize(job, tgExp)
tgExp.Networks[0].DynamicPorts = []structs.Port{{
Label: fmt.Sprintf("%s-%s", structs.ConnectProxyPrefix, "backend"),
To: -1,
}, {
Label: fmt.Sprintf("%s-%s", structs.ConnectProxyPrefix, "admin"),
To: -1,
}}
tgExp.Networks[0].Canonicalize()
require.NoError(t, groupConnectHook(job, job.TaskGroups[0]))
require.Exactly(t, tgExp, job.TaskGroups[0])
// Test that hook is idempotent
require.NoError(t, groupConnectHook(job, job.TaskGroups[0]))
require.Exactly(t, tgExp, job.TaskGroups[0])
}
func TestJobEndpointConnect_groupConnectHook_IngressGateway_BridgeNetwork(t *testing.T) {
ci.Parallel(t)
// Test that the connect ingress gateway task is inserted if a gateway service
// exists and since this is a bridge network, will rewrite the default gateway proxy
// block with correct configuration.
job := mock.ConnectIngressGatewayJob("bridge", false)
job.Meta = map[string]string{
"gateway_name": "my-gateway",
}
job.TaskGroups[0].Services[0].Name = "${NOMAD_META_gateway_name}"
job.TaskGroups[0].Services[0].Connect.Gateway.Ingress.TLS = &structs.ConsulGatewayTLSConfig{
Enabled: true,
TLSMinVersion: "TLSv1_2",
}
// setup expectations
expTG := job.TaskGroups[0].Copy()
expTG.Tasks = []*structs.Task{
// inject the gateway task
newConnectGatewayTask(structs.ConnectIngressPrefix, "my-gateway", false, true),
}
expTG.Services[0].Name = "my-gateway"
expTG.Tasks[0].Canonicalize(job, expTG)
expTG.Networks[0].Canonicalize()
// rewrite the service gateway proxy configuration
expTG.Services[0].Connect.Gateway.Proxy = gatewayProxy(expTG.Services[0].Connect.Gateway, "bridge")
require.NoError(t, groupConnectHook(job, job.TaskGroups[0]))
require.Exactly(t, expTG, job.TaskGroups[0])
// Test that the hook is idempotent
require.NoError(t, groupConnectHook(job, job.TaskGroups[0]))
require.Exactly(t, expTG, job.TaskGroups[0])
// Test that the hook populates the correct constraint for customized tls
require.Contains(t, job.TaskGroups[0].Tasks[0].Constraints, &structs.Constraint{
LTarget: "${attr.consul.version}",
RTarget: ">= 1.11.2",
Operand: structs.ConstraintSemver,
})
}
func TestJobEndpointConnect_groupConnectHook_IngressGateway_HostNetwork(t *testing.T) {
ci.Parallel(t)
// Test that the connect ingress gateway task is inserted if a gateway service
// exists. In host network mode, the default values are used.
job := mock.ConnectIngressGatewayJob("host", false)
job.Meta = map[string]string{
"gateway_name": "my-gateway",
}
job.TaskGroups[0].Services[0].Name = "${NOMAD_META_gateway_name}"
// setup expectations
expTG := job.TaskGroups[0].Copy()
expTG.Tasks = []*structs.Task{
// inject the gateway task
newConnectGatewayTask(structs.ConnectIngressPrefix, "my-gateway", true, false),
}
expTG.Services[0].Name = "my-gateway"
expTG.Tasks[0].Canonicalize(job, expTG)
expTG.Networks[0].Canonicalize()
// rewrite the service gateway proxy configuration
expTG.Services[0].Connect.Gateway.Proxy = gatewayProxy(expTG.Services[0].Connect.Gateway, "host")
require.NoError(t, groupConnectHook(job, job.TaskGroups[0]))
require.Exactly(t, expTG, job.TaskGroups[0])
// Test that the hook is idempotent
require.NoError(t, groupConnectHook(job, job.TaskGroups[0]))
require.Exactly(t, expTG, job.TaskGroups[0])
}
func TestJobEndpointConnect_groupConnectHook_IngressGateway_CustomTask(t *testing.T) {
ci.Parallel(t)
// Test that the connect gateway task is inserted if a gateway service exists
// and since this is a bridge network, will rewrite the default gateway proxy
// block with correct configuration.
job := mock.ConnectIngressGatewayJob("bridge", false)
job.Meta = map[string]string{
"gateway_name": "my-gateway",
}
job.TaskGroups[0].Services[0].Name = "${NOMAD_META_gateway_name}"
job.TaskGroups[0].Services[0].Connect.SidecarTask = &structs.SidecarTask{
Driver: "raw_exec",
User: "sidecars",
Config: map[string]interface{}{
"command": "/bin/sidecar",
"args": []string{"a", "b"},
},
Resources: &structs.Resources{
CPU: 400,
// Memory: inherit 128
},
KillSignal: "SIGHUP",
}
// setup expectations
expTG := job.TaskGroups[0].Copy()
expTG.Tasks = []*structs.Task{
// inject merged gateway task
{
Name: "connect-ingress-my-gateway",
Kind: structs.NewTaskKind(structs.ConnectIngressPrefix, "my-gateway"),
Driver: "raw_exec",
User: "sidecars",
Config: map[string]interface{}{
"command": "/bin/sidecar",
"args": []string{"a", "b"},
},
Resources: &structs.Resources{
CPU: 400,
MemoryMB: 128,
},
LogConfig: &structs.LogConfig{
MaxFiles: 2,
MaxFileSizeMB: 2,
},
ShutdownDelay: 5 * time.Second,
KillSignal: "SIGHUP",
Constraints: structs.Constraints{
connectGatewayVersionConstraint(),
connectListenerConstraint(),
},
},
}
expTG.Services[0].Name = "my-gateway"
expTG.Tasks[0].Canonicalize(job, expTG)
expTG.Networks[0].Canonicalize()
// rewrite the service gateway proxy configuration
expTG.Services[0].Connect.Gateway.Proxy = gatewayProxy(expTG.Services[0].Connect.Gateway, "bridge")
require.NoError(t, groupConnectHook(job, job.TaskGroups[0]))
require.Exactly(t, expTG, job.TaskGroups[0])
// Test that the hook is idempotent
require.NoError(t, groupConnectHook(job, job.TaskGroups[0]))
require.Exactly(t, expTG, job.TaskGroups[0])
}
func TestJobEndpointConnect_groupConnectHook_TerminatingGateway(t *testing.T) {
ci.Parallel(t)
// Tests that the connect terminating gateway task is inserted if a gateway
// service exists and since this is a bridge network, will rewrite the default
// gateway proxy block with correct configuration.
job := mock.ConnectTerminatingGatewayJob("bridge", false)
job.Meta = map[string]string{
"gateway_name": "my-gateway",
}
job.TaskGroups[0].Services[0].Name = "${NOMAD_META_gateway_name}"
// setup expectations
expTG := job.TaskGroups[0].Copy()
expTG.Tasks = []*structs.Task{
// inject the gateway task
newConnectGatewayTask(structs.ConnectTerminatingPrefix, "my-gateway", false, false),
}
expTG.Services[0].Name = "my-gateway"
expTG.Tasks[0].Canonicalize(job, expTG)
expTG.Networks[0].Canonicalize()
// rewrite the service gateway proxy configuration
expTG.Services[0].Connect.Gateway.Proxy = gatewayProxy(expTG.Services[0].Connect.Gateway, "bridge")
require.NoError(t, groupConnectHook(job, job.TaskGroups[0]))
require.Exactly(t, expTG, job.TaskGroups[0])
// Test that the hook is idempotent
require.NoError(t, groupConnectHook(job, job.TaskGroups[0]))
require.Exactly(t, expTG, job.TaskGroups[0])
}
func TestJobEndpointConnect_groupConnectHook_MeshGateway(t *testing.T) {
ci.Parallel(t)
// Test that the connect mesh gateway task is inserted if a gateway service
// exists and since this is a bridge network, will rewrite the default gateway
// proxy block with correct configuration, injecting a dynamic port for use
// by the envoy lan listener.
job := mock.ConnectMeshGatewayJob("bridge", false)
job.Meta = map[string]string{
"gateway_name": "my-gateway",
}
job.TaskGroups[0].Services[0].Name = "${NOMAD_META_gateway_name}"
// setup expectations
expTG := job.TaskGroups[0].Copy()
expTG.Tasks = []*structs.Task{
// inject the gateway task
newConnectGatewayTask(structs.ConnectMeshPrefix, "my-gateway", false, false),
}
expTG.Services[0].Name = "my-gateway"
expTG.Services[0].PortLabel = "public_port"
expTG.Networks[0].DynamicPorts = []structs.Port{{
Label: "connect-mesh-my-gateway-lan",
Value: 0,
To: -1,
HostNetwork: "default",
}}
expTG.Tasks[0].Canonicalize(job, expTG)
expTG.Networks[0].Canonicalize()
// rewrite the service gateway proxy configuration
expTG.Services[0].Connect.Gateway.Proxy = gatewayProxy(expTG.Services[0].Connect.Gateway, "bridge")
require.NoError(t, groupConnectHook(job, job.TaskGroups[0]))
require.Exactly(t, expTG, job.TaskGroups[0])
// Test that the hook is idempotent
require.NoError(t, groupConnectHook(job, job.TaskGroups[0]))
require.Exactly(t, expTG, job.TaskGroups[0])
}
// TestJobEndpoint_ConnectInterpolation asserts that when a Connect sidecar
// proxy task is being created for a group service with an interpolated name,
// the service name is interpolated *before the task is created.
//
// See https://github.com/hashicorp/nomad/issues/6853
func TestJobEndpointConnect_ConnectInterpolation(t *testing.T) {
ci.Parallel(t)
server := &Server{logger: testlog.HCLogger(t)}
jobEndpoint := NewJobEndpoints(server, nil)
j := mock.ConnectJob()
j.TaskGroups[0].Services[0].Name = "${JOB}-api"
j, warnings, err := jobEndpoint.admissionMutators(j)
require.NoError(t, err)
require.Nil(t, warnings)
require.Len(t, j.TaskGroups[0].Tasks, 2)
require.Equal(t, "connect-proxy-my-job-api", j.TaskGroups[0].Tasks[1].Name)
}
func TestJobEndpointConnect_groupConnectSidecarValidate(t *testing.T) {
ci.Parallel(t)
// network validation
makeService := func(name string) *structs.Service {
return &structs.Service{Name: name, Connect: &structs.ConsulConnect{
SidecarService: new(structs.ConsulSidecarService),
}}
}
t.Run("sidecar 0 networks", func(t *testing.T) {
require.EqualError(t, groupConnectSidecarValidate(&structs.TaskGroup{
Name: "g1",
Networks: nil,
}, makeService("connect-service")), `Consul Connect sidecars require exactly 1 network, found 0 in group "g1"`)
})
t.Run("sidecar non bridge", func(t *testing.T) {
require.EqualError(t, groupConnectSidecarValidate(&structs.TaskGroup{
Name: "g2",
Networks: structs.Networks{{
Mode: "host",
}},
}, makeService("connect-service")), `Consul Connect sidecar requires bridge network, found "host" in group "g2"`)
})
t.Run("sidecar okay", func(t *testing.T) {
require.NoError(t, groupConnectSidecarValidate(&structs.TaskGroup{
Name: "g3",
Networks: structs.Networks{{
Mode: "bridge",
}},
}, makeService("connect-service")))
})
// group and service name validation
t.Run("non-connect service contains uppercase characters", func(t *testing.T) {
err := groupConnectValidate(&structs.TaskGroup{
Name: "group",
Networks: structs.Networks{{Mode: "bridge"}},
Services: []*structs.Service{{
Name: "Other-Service",
}},
})
require.NoError(t, err)
})
t.Run("connect service contains uppercase characters", func(t *testing.T) {
err := groupConnectValidate(&structs.TaskGroup{
Name: "group",
Networks: structs.Networks{{Mode: "bridge"}},
Services: []*structs.Service{{
Name: "Other-Service",
}, makeService("Connect-Service")},
})
require.EqualError(t, err, `Consul Connect service name "Connect-Service" in group "group" must not contain uppercase characters`)
})
t.Run("non-connect group contains uppercase characters", func(t *testing.T) {
err := groupConnectValidate(&structs.TaskGroup{
Name: "Other-Group",
Networks: structs.Networks{{Mode: "bridge"}},
Services: []*structs.Service{{
Name: "other-service",
}},
})
require.NoError(t, err)
})
t.Run("connect-group contains uppercase characters", func(t *testing.T) {
err := groupConnectValidate(&structs.TaskGroup{
Name: "Connect-Group",
Networks: structs.Networks{{Mode: "bridge"}},
Services: []*structs.Service{{
Name: "other-service",
}, makeService("connect-service")},
})
require.EqualError(t, err, `Consul Connect group "Connect-Group" with service "connect-service" must not contain uppercase characters`)
})
t.Run("connect group and service lowercase", func(t *testing.T) {
err := groupConnectValidate(&structs.TaskGroup{
Name: "connect-group",
Networks: structs.Networks{{Mode: "bridge"}},
Services: []*structs.Service{{
Name: "other-service",
}, makeService("connect-service")},
})
require.NoError(t, err)
})
t.Run("connect group overlap upstreams", func(t *testing.T) {
s1 := makeService("s1")
s2 := makeService("s2")
s1.Connect.SidecarService.Proxy = &structs.ConsulProxy{
Upstreams: []structs.ConsulUpstream{{
LocalBindPort: 8999,
}},
}
s2.Connect.SidecarService.Proxy = &structs.ConsulProxy{
Upstreams: []structs.ConsulUpstream{{
LocalBindPort: 8999,
}},
}
err := groupConnectValidate(&structs.TaskGroup{
Name: "connect-group",
Networks: structs.Networks{{Mode: "bridge"}},
Services: []*structs.Service{s1, s2},
})
require.EqualError(t, err, `Consul Connect services "s2" and "s1" in group "connect-group" using same address for upstreams (:8999)`)
})
}
func TestJobEndpointConnect_groupConnectUpstreamsValidate(t *testing.T) {
ci.Parallel(t)
t.Run("no connect services", func(t *testing.T) {
err := groupConnectUpstreamsValidate("group",
[]*structs.Service{{Name: "s1"}, {Name: "s2"}})
require.NoError(t, err)
})
t.Run("connect services no overlap", func(t *testing.T) {
err := groupConnectUpstreamsValidate("group",
[]*structs.Service{
{
Name: "s1",
Connect: &structs.ConsulConnect{
SidecarService: &structs.ConsulSidecarService{
Proxy: &structs.ConsulProxy{
Upstreams: []structs.ConsulUpstream{{
LocalBindAddress: "127.0.0.1",
LocalBindPort: 9001,
}, {
LocalBindAddress: "127.0.0.1",
LocalBindPort: 9002,
}},
},
},
},
},
{
Name: "s2",
Connect: &structs.ConsulConnect{
SidecarService: &structs.ConsulSidecarService{
Proxy: &structs.ConsulProxy{
Upstreams: []structs.ConsulUpstream{{
LocalBindAddress: "10.0.0.1",
LocalBindPort: 9001,
}, {
LocalBindAddress: "127.0.0.1",
LocalBindPort: 9003,
}},
},
},
},
},
})
require.NoError(t, err)
})
t.Run("connect services overlap port", func(t *testing.T) {
err := groupConnectUpstreamsValidate("group",
[]*structs.Service{
{
Name: "s1",
Connect: &structs.ConsulConnect{
SidecarService: &structs.ConsulSidecarService{
Proxy: &structs.ConsulProxy{
Upstreams: []structs.ConsulUpstream{{
LocalBindAddress: "127.0.0.1",
LocalBindPort: 9001,
}, {
LocalBindAddress: "127.0.0.1",
LocalBindPort: 9002,
}},
},
},
},
},
{
Name: "s2",
Connect: &structs.ConsulConnect{
SidecarService: &structs.ConsulSidecarService{
Proxy: &structs.ConsulProxy{
Upstreams: []structs.ConsulUpstream{{
LocalBindAddress: "127.0.0.1",
LocalBindPort: 9002,
}, {
LocalBindAddress: "127.0.0.1",
LocalBindPort: 9003,
}},
},
},
},
},
})
require.EqualError(t, err, `Consul Connect services "s2" and "s1" in group "group" using same address for upstreams (127.0.0.1:9002)`)
})
}
func TestJobEndpointConnect_getNamedTaskForNativeService(t *testing.T) {
ci.Parallel(t)
t.Run("named exists", func(t *testing.T) {
task, err := getNamedTaskForNativeService(&structs.TaskGroup{
Name: "g1",
Tasks: []*structs.Task{{Name: "t1"}, {Name: "t2"}},
}, "s1", "t2")
require.NoError(t, err)
require.Equal(t, "t2", task.Name)
})
t.Run("infer exists", func(t *testing.T) {
task, err := getNamedTaskForNativeService(&structs.TaskGroup{
Name: "g1",
Tasks: []*structs.Task{{Name: "t2"}},
}, "s1", "")
require.NoError(t, err)
require.Equal(t, "t2", task.Name)
})
t.Run("infer ambiguous", func(t *testing.T) {
task, err := getNamedTaskForNativeService(&structs.TaskGroup{
Name: "g1",
Tasks: []*structs.Task{{Name: "t1"}, {Name: "t2"}},
}, "s1", "")
require.EqualError(t, err, "task for Consul Connect Native service g1->s1 is ambiguous and must be set")
require.Nil(t, task)
})
t.Run("named absent", func(t *testing.T) {
task, err := getNamedTaskForNativeService(&structs.TaskGroup{
Name: "g1",
Tasks: []*structs.Task{{Name: "t1"}, {Name: "t2"}},
}, "s1", "t3")
require.EqualError(t, err, "task t3 named by Consul Connect Native service g1->s1 does not exist")
require.Nil(t, task)
})
}
func TestJobEndpointConnect_groupConnectGatewayValidate(t *testing.T) {
ci.Parallel(t)
t.Run("no group network", func(t *testing.T) {
err := groupConnectGatewayValidate(&structs.TaskGroup{
Name: "g1",
Networks: nil,
})
require.EqualError(t, err, `Consul Connect gateways require exactly 1 network, found 0 in group "g1"`)
})
t.Run("bad network mode", func(t *testing.T) {
err := groupConnectGatewayValidate(&structs.TaskGroup{
Name: "g1",
Networks: structs.Networks{{
Mode: "",
}},
})
require.EqualError(t, err, `Consul Connect Gateway service requires Task Group with network mode of type "bridge" or "host"`)
})
}
func TestJobEndpointConnect_newConnectGatewayTask_host(t *testing.T) {
ci.Parallel(t)
t.Run("ingress", func(t *testing.T) {
task := newConnectGatewayTask(structs.ConnectIngressPrefix, "foo", true, false)
require.Equal(t, "connect-ingress-foo", task.Name)
require.Equal(t, "connect-ingress:foo", string(task.Kind))
require.Equal(t, ">= 1.8.0", task.Constraints[0].RTarget)
require.Equal(t, "host", task.Config["network_mode"])
require.Nil(t, task.Lifecycle)
})
t.Run("terminating", func(t *testing.T) {
task := newConnectGatewayTask(structs.ConnectTerminatingPrefix, "bar", true, false)
require.Equal(t, "connect-terminating-bar", task.Name)
require.Equal(t, "connect-terminating:bar", string(task.Kind))
require.Equal(t, ">= 1.8.0", task.Constraints[0].RTarget)
require.Equal(t, "host", task.Config["network_mode"])
require.Nil(t, task.Lifecycle)
})
}
func TestJobEndpointConnect_newConnectGatewayTask_bridge(t *testing.T) {
ci.Parallel(t)
task := newConnectGatewayTask(structs.ConnectIngressPrefix, "service1", false, false)
require.NotContains(t, task.Config, "network_mode")
}
func TestJobEndpointConnect_hasGatewayTaskForService(t *testing.T) {
ci.Parallel(t)
t.Run("no gateway task", func(t *testing.T) {
result := hasGatewayTaskForService(&structs.TaskGroup{
Name: "group",
Tasks: []*structs.Task{{
Name: "task1",
Kind: "",
}},
}, "my-service")
require.False(t, result)
})
t.Run("has ingress task", func(t *testing.T) {
result := hasGatewayTaskForService(&structs.TaskGroup{
Name: "group",
Tasks: []*structs.Task{{
Name: "ingress-gateway-my-service",
Kind: structs.NewTaskKind(structs.ConnectIngressPrefix, "my-service"),
}},
}, "my-service")
require.True(t, result)
})
t.Run("has terminating task", func(t *testing.T) {
result := hasGatewayTaskForService(&structs.TaskGroup{
Name: "group",
Tasks: []*structs.Task{{
Name: "terminating-gateway-my-service",
Kind: structs.NewTaskKind(structs.ConnectTerminatingPrefix, "my-service"),
}},
}, "my-service")
require.True(t, result)
})
t.Run("has mesh task", func(t *testing.T) {
result := hasGatewayTaskForService(&structs.TaskGroup{
Name: "group",
Tasks: []*structs.Task{{
Name: "mesh-gateway-my-service",
Kind: structs.NewTaskKind(structs.ConnectMeshPrefix, "my-service"),
}},
}, "my-service")
require.True(t, result)
})
}
func TestJobEndpointConnect_gatewayProxyIsDefault(t *testing.T) {
ci.Parallel(t)
t.Run("nil", func(t *testing.T) {
result := gatewayProxyIsDefault(nil)
require.True(t, result)
})
t.Run("unrelated fields set", func(t *testing.T) {
result := gatewayProxyIsDefault(&structs.ConsulGatewayProxy{
ConnectTimeout: pointer.Of(2 * time.Second),
Config: map[string]interface{}{"foo": 1},
})
require.True(t, result)
})
t.Run("no-bind set", func(t *testing.T) {
result := gatewayProxyIsDefault(&structs.ConsulGatewayProxy{
EnvoyGatewayNoDefaultBind: true,
})
require.False(t, result)
})
t.Run("bind-tagged set", func(t *testing.T) {
result := gatewayProxyIsDefault(&structs.ConsulGatewayProxy{
EnvoyGatewayBindTaggedAddresses: true,
})
require.False(t, result)
})
t.Run("bind-addresses set", func(t *testing.T) {
result := gatewayProxyIsDefault(&structs.ConsulGatewayProxy{
EnvoyGatewayBindAddresses: map[string]*structs.ConsulGatewayBindAddress{
"listener1": {
Address: "1.1.1.1",
Port: 9000,
},
},
})
require.False(t, result)
})
}
func TestJobEndpointConnect_gatewayBindAddressesForBridge(t *testing.T) {
ci.Parallel(t)
t.Run("nil", func(t *testing.T) {
result := gatewayBindAddressesIngressForBridge(nil)
require.Empty(t, result)
})
t.Run("no listeners", func(t *testing.T) {
result := gatewayBindAddressesIngressForBridge(&structs.ConsulIngressConfigEntry{Listeners: nil})
require.Empty(t, result)
})
t.Run("simple", func(t *testing.T) {
result := gatewayBindAddressesIngressForBridge(&structs.ConsulIngressConfigEntry{
Listeners: []*structs.ConsulIngressListener{{
Port: 3000,
Protocol: "tcp",
Services: []*structs.ConsulIngressService{{
Name: "service1",
}},
}},
})
require.Equal(t, map[string]*structs.ConsulGatewayBindAddress{
"service1": {
Address: "0.0.0.0",
Port: 3000,
},
}, result)
})
t.Run("complex", func(t *testing.T) {
result := gatewayBindAddressesIngressForBridge(&structs.ConsulIngressConfigEntry{
Listeners: []*structs.ConsulIngressListener{{
Port: 3000,
Protocol: "tcp",
Services: []*structs.ConsulIngressService{{
Name: "service1",
}, {
Name: "service2",
}},
}, {
Port: 3001,
Protocol: "http",
Services: []*structs.ConsulIngressService{{
Name: "service3",
}},
}},
})
require.Equal(t, map[string]*structs.ConsulGatewayBindAddress{
"service1": {
Address: "0.0.0.0",
Port: 3000,
},
"service2": {
Address: "0.0.0.0",
Port: 3000,
},
"service3": {
Address: "0.0.0.0",
Port: 3001,
},
}, result)
})
}
func TestJobEndpointConnect_gatewayProxy(t *testing.T) {
ci.Parallel(t)
t.Run("nil", func(t *testing.T) {
result := gatewayProxy(nil, "bridge")
require.Nil(t, result)
})
t.Run("nil proxy", func(t *testing.T) {
result := gatewayProxy(&structs.ConsulGateway{
Ingress: &structs.ConsulIngressConfigEntry{
Listeners: []*structs.ConsulIngressListener{{
Port: 3000,
Protocol: "tcp",
Services: []*structs.ConsulIngressService{{
Name: "service1",
}},
}},
},
}, "bridge")
require.Equal(t, &structs.ConsulGatewayProxy{
ConnectTimeout: pointer.Of(defaultConnectTimeout),
EnvoyGatewayNoDefaultBind: true,
EnvoyGatewayBindTaggedAddresses: false,
EnvoyGatewayBindAddresses: map[string]*structs.ConsulGatewayBindAddress{
"service1": {
Address: "0.0.0.0",
Port: 3000,
}},
}, result)
})
t.Run("ingress set defaults", func(t *testing.T) {
result := gatewayProxy(&structs.ConsulGateway{
Proxy: &structs.ConsulGatewayProxy{
ConnectTimeout: pointer.Of(2 * time.Second),
Config: map[string]interface{}{"foo": 1},
},
Ingress: &structs.ConsulIngressConfigEntry{
Listeners: []*structs.ConsulIngressListener{{
Port: 3000,
Protocol: "tcp",
Services: []*structs.ConsulIngressService{{
Name: "service1",
}},
}},
},
}, "bridge")
require.Equal(t, &structs.ConsulGatewayProxy{
ConnectTimeout: pointer.Of(2 * time.Second),
Config: map[string]interface{}{"foo": 1},
EnvoyGatewayNoDefaultBind: true,
EnvoyGatewayBindTaggedAddresses: false,
EnvoyGatewayBindAddresses: map[string]*structs.ConsulGatewayBindAddress{
"service1": {
Address: "0.0.0.0",
Port: 3000,
}},
}, result)
})
t.Run("ingress leave as-is", func(t *testing.T) {
result := gatewayProxy(&structs.ConsulGateway{
Proxy: &structs.ConsulGatewayProxy{
Config: map[string]interface{}{"foo": 1},
EnvoyGatewayBindTaggedAddresses: true,
},
Ingress: &structs.ConsulIngressConfigEntry{
Listeners: []*structs.ConsulIngressListener{{
Port: 3000,
Protocol: "tcp",
Services: []*structs.ConsulIngressService{{
Name: "service1",
}},
}},
},
}, "bridge")
require.Equal(t, &structs.ConsulGatewayProxy{
ConnectTimeout: nil,
Config: map[string]interface{}{"foo": 1},
EnvoyGatewayNoDefaultBind: false,
EnvoyGatewayBindTaggedAddresses: true,
EnvoyGatewayBindAddresses: nil,
}, result)
})
t.Run("terminating set defaults", func(t *testing.T) {
result := gatewayProxy(&structs.ConsulGateway{
Proxy: &structs.ConsulGatewayProxy{
ConnectTimeout: pointer.Of(2 * time.Second),
EnvoyDNSDiscoveryType: "STRICT_DNS",
},
Terminating: &structs.ConsulTerminatingConfigEntry{
Services: []*structs.ConsulLinkedService{{
Name: "service1",
CAFile: "/cafile.pem",
CertFile: "/certfile.pem",
KeyFile: "/keyfile.pem",
SNI: "",
}},
},
}, "bridge")
require.Equal(t, &structs.ConsulGatewayProxy{
ConnectTimeout: pointer.Of(2 * time.Second),
EnvoyGatewayNoDefaultBind: true,
EnvoyGatewayBindTaggedAddresses: false,
EnvoyDNSDiscoveryType: "STRICT_DNS",
EnvoyGatewayBindAddresses: map[string]*structs.ConsulGatewayBindAddress{
"default": {
Address: "0.0.0.0",
Port: -1,
},
},
}, result)
})
t.Run("terminating leave as-is", func(t *testing.T) {
result := gatewayProxy(&structs.ConsulGateway{
Proxy: &structs.ConsulGatewayProxy{
Config: map[string]interface{}{"foo": 1},
EnvoyGatewayBindTaggedAddresses: true,
},
Terminating: &structs.ConsulTerminatingConfigEntry{
Services: []*structs.ConsulLinkedService{{
Name: "service1",
}},
},
}, "bridge")
require.Equal(t, &structs.ConsulGatewayProxy{
ConnectTimeout: nil,
Config: map[string]interface{}{"foo": 1},
EnvoyGatewayNoDefaultBind: false,
EnvoyGatewayBindTaggedAddresses: true,
EnvoyGatewayBindAddresses: nil,
}, result)
})
t.Run("mesh set defaults in bridge", func(t *testing.T) {
result := gatewayProxy(&structs.ConsulGateway{
Proxy: &structs.ConsulGatewayProxy{
ConnectTimeout: pointer.Of(2 * time.Second),
},
Mesh: &structs.ConsulMeshConfigEntry{
// nothing
},
}, "bridge")
require.Equal(t, &structs.ConsulGatewayProxy{
ConnectTimeout: pointer.Of(2 * time.Second),
EnvoyGatewayNoDefaultBind: true,
EnvoyGatewayBindTaggedAddresses: false,
EnvoyGatewayBindAddresses: map[string]*structs.ConsulGatewayBindAddress{
"lan": {
Address: "0.0.0.0",
Port: -1,
},
"wan": {
Address: "0.0.0.0",
Port: -1,
},
},
}, result)
})
t.Run("mesh set defaults in host", func(t *testing.T) {
result := gatewayProxy(&structs.ConsulGateway{
Proxy: &structs.ConsulGatewayProxy{
ConnectTimeout: pointer.Of(2 * time.Second),
},
Mesh: &structs.ConsulMeshConfigEntry{
// nothing
},
}, "host")
require.Equal(t, &structs.ConsulGatewayProxy{
ConnectTimeout: pointer.Of(2 * time.Second),
}, result)
})
t.Run("mesh leave as-is", func(t *testing.T) {
result := gatewayProxy(&structs.ConsulGateway{
Proxy: &structs.ConsulGatewayProxy{
Config: map[string]interface{}{"foo": 1},
EnvoyGatewayBindTaggedAddresses: true,
},
Mesh: &structs.ConsulMeshConfigEntry{
// nothing
},
}, "bridge")
require.Equal(t, &structs.ConsulGatewayProxy{
ConnectTimeout: nil,
Config: map[string]interface{}{"foo": 1},
EnvoyGatewayNoDefaultBind: false,
EnvoyGatewayBindTaggedAddresses: true,
EnvoyGatewayBindAddresses: nil,
}, result)
})
}