e81e9223ef
Before, upstreams could only be defined using the default datacenter. Now, the `datacenter` field can be set in a connect upstream definition, informing consul of the desire for an instance of the upstream service in the specified datacenter. The field is optional and continues to default to the local datacenter. Closes #8964
467 lines
12 KiB
Go
467 lines
12 KiB
Go
package consul
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/hashicorp/consul/api"
|
|
"github.com/hashicorp/nomad/helper"
|
|
"github.com/hashicorp/nomad/nomad/structs"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
var (
|
|
testConnectNetwork = structs.Networks{{
|
|
Mode: "bridge",
|
|
Device: "eth0",
|
|
IP: "192.168.30.1",
|
|
DynamicPorts: []structs.Port{
|
|
{Label: "healthPort", Value: 23100, To: 23100},
|
|
{Label: "metricsPort", Value: 23200, To: 23200},
|
|
{Label: "connect-proxy-redis", Value: 3000, To: 3000},
|
|
},
|
|
}}
|
|
)
|
|
|
|
func TestConnect_newConnect(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("nil", func(t *testing.T) {
|
|
asr, err := newConnect("", nil, nil)
|
|
require.NoError(t, err)
|
|
require.Nil(t, asr)
|
|
})
|
|
|
|
t.Run("native", func(t *testing.T) {
|
|
asr, err := newConnect("", &structs.ConsulConnect{
|
|
Native: true,
|
|
}, nil)
|
|
require.NoError(t, err)
|
|
require.True(t, asr.Native)
|
|
require.Nil(t, asr.SidecarService)
|
|
})
|
|
|
|
t.Run("with sidecar", func(t *testing.T) {
|
|
asr, err := newConnect("redis", &structs.ConsulConnect{
|
|
Native: false,
|
|
SidecarService: &structs.ConsulSidecarService{
|
|
Tags: []string{"foo", "bar"},
|
|
Port: "sidecarPort",
|
|
},
|
|
}, testConnectNetwork)
|
|
require.NoError(t, err)
|
|
require.Equal(t, &api.AgentServiceRegistration{
|
|
Tags: []string{"foo", "bar"},
|
|
Port: 3000,
|
|
Address: "192.168.30.1",
|
|
Proxy: &api.AgentServiceConnectProxyConfig{
|
|
Config: map[string]interface{}{
|
|
"bind_address": "0.0.0.0",
|
|
"bind_port": 3000,
|
|
},
|
|
},
|
|
}, asr.SidecarService)
|
|
})
|
|
}
|
|
|
|
func TestConnect_connectSidecarRegistration(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("nil", func(t *testing.T) {
|
|
sidecarReg, err := connectSidecarRegistration("", nil, testConnectNetwork)
|
|
require.NoError(t, err)
|
|
require.Nil(t, sidecarReg)
|
|
})
|
|
|
|
t.Run("no service port", func(t *testing.T) {
|
|
_, err := connectSidecarRegistration("unknown", &structs.ConsulSidecarService{
|
|
// irrelevant
|
|
}, testConnectNetwork)
|
|
require.EqualError(t, err, `No Connect port defined for service "unknown"`)
|
|
})
|
|
|
|
t.Run("bad proxy", func(t *testing.T) {
|
|
_, err := connectSidecarRegistration("redis", &structs.ConsulSidecarService{
|
|
Proxy: &structs.ConsulProxy{
|
|
Expose: &structs.ConsulExposeConfig{
|
|
Paths: []structs.ConsulExposePath{{
|
|
ListenerPort: "badPort",
|
|
}},
|
|
},
|
|
},
|
|
}, testConnectNetwork)
|
|
require.EqualError(t, err, `No port of label "badPort" defined`)
|
|
})
|
|
|
|
t.Run("normal", func(t *testing.T) {
|
|
proxy, err := connectSidecarRegistration("redis", &structs.ConsulSidecarService{
|
|
Tags: []string{"foo", "bar"},
|
|
Port: "sidecarPort",
|
|
}, testConnectNetwork)
|
|
require.NoError(t, err)
|
|
require.Equal(t, &api.AgentServiceRegistration{
|
|
Tags: []string{"foo", "bar"},
|
|
Port: 3000,
|
|
Address: "192.168.30.1",
|
|
Proxy: &api.AgentServiceConnectProxyConfig{
|
|
Config: map[string]interface{}{
|
|
"bind_address": "0.0.0.0",
|
|
"bind_port": 3000,
|
|
},
|
|
},
|
|
}, proxy)
|
|
})
|
|
}
|
|
|
|
func TestConnect_connectProxy(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// If the input proxy is nil, we expect the output to be a proxy with its
|
|
// config set to default values.
|
|
t.Run("nil proxy", func(t *testing.T) {
|
|
proxy, err := connectProxy(nil, 2000, testConnectNetwork)
|
|
require.NoError(t, err)
|
|
require.Equal(t, &api.AgentServiceConnectProxyConfig{
|
|
LocalServiceAddress: "",
|
|
LocalServicePort: 0,
|
|
Upstreams: nil,
|
|
Expose: api.ExposeConfig{},
|
|
Config: map[string]interface{}{
|
|
"bind_address": "0.0.0.0",
|
|
"bind_port": 2000,
|
|
},
|
|
}, proxy)
|
|
})
|
|
|
|
t.Run("bad proxy", func(t *testing.T) {
|
|
_, err := connectProxy(&structs.ConsulProxy{
|
|
LocalServiceAddress: "0.0.0.0",
|
|
LocalServicePort: 2000,
|
|
Upstreams: nil,
|
|
Expose: &structs.ConsulExposeConfig{
|
|
Paths: []structs.ConsulExposePath{{
|
|
ListenerPort: "badPort",
|
|
}},
|
|
},
|
|
Config: nil,
|
|
}, 2000, testConnectNetwork)
|
|
require.EqualError(t, err, `No port of label "badPort" defined`)
|
|
})
|
|
|
|
t.Run("normal", func(t *testing.T) {
|
|
proxy, err := connectProxy(&structs.ConsulProxy{
|
|
LocalServiceAddress: "0.0.0.0",
|
|
LocalServicePort: 2000,
|
|
Upstreams: nil,
|
|
Expose: &structs.ConsulExposeConfig{
|
|
Paths: []structs.ConsulExposePath{{
|
|
Path: "/health",
|
|
Protocol: "http",
|
|
LocalPathPort: 8000,
|
|
ListenerPort: "healthPort",
|
|
}},
|
|
},
|
|
Config: nil,
|
|
}, 2000, testConnectNetwork)
|
|
require.NoError(t, err)
|
|
require.Equal(t, &api.AgentServiceConnectProxyConfig{
|
|
LocalServiceAddress: "0.0.0.0",
|
|
LocalServicePort: 2000,
|
|
Upstreams: nil,
|
|
Expose: api.ExposeConfig{
|
|
Paths: []api.ExposePath{{
|
|
Path: "/health",
|
|
Protocol: "http",
|
|
LocalPathPort: 8000,
|
|
ListenerPort: 23100,
|
|
}},
|
|
},
|
|
Config: map[string]interface{}{
|
|
"bind_address": "0.0.0.0",
|
|
"bind_port": 2000,
|
|
},
|
|
}, proxy)
|
|
})
|
|
}
|
|
|
|
func TestConnect_connectProxyExpose(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("nil", func(t *testing.T) {
|
|
exposeConfig, err := connectProxyExpose(nil, nil)
|
|
require.NoError(t, err)
|
|
require.Equal(t, api.ExposeConfig{}, exposeConfig)
|
|
})
|
|
|
|
t.Run("bad port", func(t *testing.T) {
|
|
_, err := connectProxyExpose(&structs.ConsulExposeConfig{
|
|
Paths: []structs.ConsulExposePath{{
|
|
ListenerPort: "badPort",
|
|
}},
|
|
}, testConnectNetwork)
|
|
require.EqualError(t, err, `No port of label "badPort" defined`)
|
|
})
|
|
|
|
t.Run("normal", func(t *testing.T) {
|
|
expose, err := connectProxyExpose(&structs.ConsulExposeConfig{
|
|
Paths: []structs.ConsulExposePath{{
|
|
Path: "/health",
|
|
Protocol: "http",
|
|
LocalPathPort: 8000,
|
|
ListenerPort: "healthPort",
|
|
}},
|
|
}, testConnectNetwork)
|
|
require.NoError(t, err)
|
|
require.Equal(t, api.ExposeConfig{
|
|
Checks: false,
|
|
Paths: []api.ExposePath{{
|
|
Path: "/health",
|
|
ListenerPort: 23100,
|
|
LocalPathPort: 8000,
|
|
Protocol: "http",
|
|
ParsedFromCheck: false,
|
|
}},
|
|
}, expose)
|
|
})
|
|
}
|
|
|
|
func TestConnect_connectProxyExposePaths(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("nil", func(t *testing.T) {
|
|
upstreams, err := connectProxyExposePaths(nil, nil)
|
|
require.NoError(t, err)
|
|
require.Empty(t, upstreams)
|
|
})
|
|
|
|
t.Run("no network", func(t *testing.T) {
|
|
original := []structs.ConsulExposePath{{Path: "/path"}}
|
|
_, err := connectProxyExposePaths(original, nil)
|
|
require.EqualError(t, err, `Connect only supported with exactly 1 network (found 0)`)
|
|
})
|
|
|
|
t.Run("normal", func(t *testing.T) {
|
|
original := []structs.ConsulExposePath{{
|
|
Path: "/health",
|
|
Protocol: "http",
|
|
LocalPathPort: 8000,
|
|
ListenerPort: "healthPort",
|
|
}, {
|
|
Path: "/metrics",
|
|
Protocol: "grpc",
|
|
LocalPathPort: 9500,
|
|
ListenerPort: "metricsPort",
|
|
}}
|
|
exposePaths, err := connectProxyExposePaths(original, testConnectNetwork)
|
|
require.NoError(t, err)
|
|
require.Equal(t, []api.ExposePath{
|
|
{
|
|
Path: "/health",
|
|
Protocol: "http",
|
|
LocalPathPort: 8000,
|
|
ListenerPort: 23100,
|
|
ParsedFromCheck: false,
|
|
},
|
|
{
|
|
Path: "/metrics",
|
|
Protocol: "grpc",
|
|
LocalPathPort: 9500,
|
|
ListenerPort: 23200,
|
|
ParsedFromCheck: false,
|
|
},
|
|
}, exposePaths)
|
|
})
|
|
}
|
|
|
|
func TestConnect_connectUpstreams(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("nil", func(t *testing.T) {
|
|
require.Nil(t, connectUpstreams(nil))
|
|
})
|
|
|
|
t.Run("not empty", func(t *testing.T) {
|
|
require.Equal(t,
|
|
[]api.Upstream{{
|
|
DestinationName: "foo",
|
|
LocalBindPort: 8000,
|
|
}, {
|
|
DestinationName: "bar",
|
|
LocalBindPort: 9000,
|
|
Datacenter: "dc2",
|
|
}},
|
|
connectUpstreams([]structs.ConsulUpstream{{
|
|
DestinationName: "foo",
|
|
LocalBindPort: 8000,
|
|
}, {
|
|
DestinationName: "bar",
|
|
LocalBindPort: 9000,
|
|
Datacenter: "dc2",
|
|
}}),
|
|
)
|
|
})
|
|
}
|
|
|
|
func TestConnect_connectProxyConfig(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("nil map", func(t *testing.T) {
|
|
require.Equal(t, map[string]interface{}{
|
|
"bind_address": "0.0.0.0",
|
|
"bind_port": 42,
|
|
}, connectProxyConfig(nil, 42))
|
|
})
|
|
|
|
t.Run("pre-existing map", func(t *testing.T) {
|
|
require.Equal(t, map[string]interface{}{
|
|
"bind_address": "0.0.0.0",
|
|
"bind_port": 42,
|
|
"foo": "bar",
|
|
}, connectProxyConfig(map[string]interface{}{
|
|
"foo": "bar",
|
|
}, 42))
|
|
})
|
|
}
|
|
|
|
func TestConnect_getConnectPort(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
networks := structs.Networks{{
|
|
IP: "192.168.30.1",
|
|
DynamicPorts: []structs.Port{{
|
|
Label: "connect-proxy-foo",
|
|
Value: 23456,
|
|
To: 23456,
|
|
}}}}
|
|
|
|
t.Run("normal", func(t *testing.T) {
|
|
nr, port, err := connectPort("foo", networks)
|
|
require.NoError(t, err)
|
|
require.Equal(t, structs.Port{
|
|
Label: "connect-proxy-foo",
|
|
Value: 23456,
|
|
To: 23456,
|
|
}, port)
|
|
require.Equal(t, "192.168.30.1", nr.IP)
|
|
})
|
|
|
|
t.Run("no such service", func(t *testing.T) {
|
|
_, _, err := connectPort("other", networks)
|
|
require.EqualError(t, err, `No Connect port defined for service "other"`)
|
|
})
|
|
|
|
t.Run("no network", func(t *testing.T) {
|
|
_, _, err := connectPort("foo", nil)
|
|
require.EqualError(t, err, "Connect only supported with exactly 1 network (found 0)")
|
|
})
|
|
|
|
t.Run("multi network", func(t *testing.T) {
|
|
_, _, err := connectPort("foo", append(networks, &structs.NetworkResource{
|
|
Device: "eth1",
|
|
IP: "10.0.10.0",
|
|
}))
|
|
require.EqualError(t, err, "Connect only supported with exactly 1 network (found 2)")
|
|
})
|
|
}
|
|
|
|
func TestConnect_getExposePathPort(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
networks := structs.Networks{{
|
|
Device: "eth0",
|
|
IP: "192.168.30.1",
|
|
DynamicPorts: []structs.Port{{
|
|
Label: "myPort",
|
|
Value: 23456,
|
|
To: 23456,
|
|
}}}}
|
|
|
|
t.Run("normal", func(t *testing.T) {
|
|
ip, port, err := connectExposePathPort("myPort", networks)
|
|
require.NoError(t, err)
|
|
require.Equal(t, ip, "192.168.30.1")
|
|
require.Equal(t, 23456, port)
|
|
})
|
|
|
|
t.Run("no such port label", func(t *testing.T) {
|
|
_, _, err := connectExposePathPort("otherPort", networks)
|
|
require.EqualError(t, err, `No port of label "otherPort" defined`)
|
|
})
|
|
|
|
t.Run("no network", func(t *testing.T) {
|
|
_, _, err := connectExposePathPort("myPort", nil)
|
|
require.EqualError(t, err, "Connect only supported with exactly 1 network (found 0)")
|
|
})
|
|
|
|
t.Run("multi network", func(t *testing.T) {
|
|
_, _, err := connectExposePathPort("myPort", append(networks, &structs.NetworkResource{
|
|
Device: "eth1",
|
|
IP: "10.0.10.0",
|
|
}))
|
|
require.EqualError(t, err, "Connect only supported with exactly 1 network (found 2)")
|
|
})
|
|
}
|
|
|
|
func TestConnect_newConnectGateway(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("not a gateway", func(t *testing.T) {
|
|
result := newConnectGateway("s1", &structs.ConsulConnect{Native: true})
|
|
require.Nil(t, result)
|
|
})
|
|
|
|
t.Run("canonical empty", func(t *testing.T) {
|
|
result := newConnectGateway("s1", &structs.ConsulConnect{
|
|
Gateway: &structs.ConsulGateway{
|
|
Proxy: &structs.ConsulGatewayProxy{
|
|
ConnectTimeout: helper.TimeToPtr(1 * time.Second),
|
|
EnvoyGatewayBindTaggedAddresses: false,
|
|
EnvoyGatewayBindAddresses: nil,
|
|
EnvoyGatewayNoDefaultBind: false,
|
|
Config: nil,
|
|
},
|
|
},
|
|
})
|
|
require.Equal(t, &api.AgentServiceConnectProxyConfig{
|
|
Config: map[string]interface{}{
|
|
"connect_timeout_ms": int64(1000),
|
|
},
|
|
}, result)
|
|
})
|
|
|
|
t.Run("full", func(t *testing.T) {
|
|
result := newConnectGateway("s1", &structs.ConsulConnect{
|
|
Gateway: &structs.ConsulGateway{
|
|
Proxy: &structs.ConsulGatewayProxy{
|
|
ConnectTimeout: helper.TimeToPtr(1 * time.Second),
|
|
EnvoyGatewayBindTaggedAddresses: true,
|
|
EnvoyGatewayBindAddresses: map[string]*structs.ConsulGatewayBindAddress{
|
|
"service1": &structs.ConsulGatewayBindAddress{
|
|
Address: "10.0.0.1",
|
|
Port: 2000,
|
|
},
|
|
},
|
|
EnvoyGatewayNoDefaultBind: true,
|
|
Config: map[string]interface{}{
|
|
"foo": 1,
|
|
},
|
|
},
|
|
},
|
|
})
|
|
require.Equal(t, &api.AgentServiceConnectProxyConfig{
|
|
Config: map[string]interface{}{
|
|
"connect_timeout_ms": int64(1000),
|
|
"envoy_gateway_bind_tagged_addresses": true,
|
|
"envoy_gateway_bind_addresses": map[string]*structs.ConsulGatewayBindAddress{
|
|
"service1": &structs.ConsulGatewayBindAddress{
|
|
Address: "10.0.0.1",
|
|
Port: 2000,
|
|
},
|
|
},
|
|
"envoy_gateway_no_default_bind": true,
|
|
"foo": 1,
|
|
},
|
|
}, result)
|
|
})
|
|
}
|