d026ff1f66
This PR implements first-class support for Nomad running Consul Connect Mesh Gateways. Mesh gateways enable services in the Connect mesh to make cross-DC connections via gateways, where each datacenter may not have full node interconnectivity. Consul docs with more information: https://www.consul.io/docs/connect/gateways/mesh-gateway The following group level service block can be used to establish a Connect mesh gateway. service { connect { gateway { mesh { // no configuration } } } } Services can make use of a mesh gateway by configuring so in their upstream blocks, e.g. service { connect { sidecar_service { proxy { upstreams { destination_name = "<service>" local_bind_port = <port> datacenter = "<datacenter>" mesh_gateway { mode = "<mode>" } } } } } } Typical use of a mesh gateway is to create a bridge between datacenters. A mesh gateway should then be configured with a service port that is mapped from a host_network configured on a WAN interface in Nomad agent config, e.g. client { host_network "public" { interface = "eth1" } } Create a port mapping in the group.network block for use by the mesh gateway service from the public host_network, e.g. network { mode = "bridge" port "mesh_wan" { host_network = "public" } } Use this port label for the service.port of the mesh gateway, e.g. service { name = "mesh-gateway" port = "mesh_wan" connect { gateway { mesh {} } } } Currently Envoy is the only supported gateway implementation in Consul. By default Nomad client will run the latest official Envoy docker image supported by the local Consul agent. The Envoy task can be customized by setting `meta.connect.gateway_image` in agent config or by setting the `connect.sidecar_task` block. Gateways require Consul 1.8.0+, enforced by the Nomad scheduler. Closes #9446
1475 lines
38 KiB
Go
1475 lines
38 KiB
Go
package structs
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/hashicorp/nomad/helper"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestServiceCheck_Hash(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
original := &ServiceCheck{
|
|
Name: "check",
|
|
SuccessBeforePassing: 3,
|
|
FailuresBeforeCritical: 4,
|
|
}
|
|
|
|
type sc = ServiceCheck
|
|
type tweaker = func(check *sc)
|
|
|
|
hash := func(c *sc) string {
|
|
return c.Hash("ServiceID")
|
|
}
|
|
|
|
t.Run("reflexive", func(t *testing.T) {
|
|
require.Equal(t, hash(original), hash(original))
|
|
})
|
|
|
|
// these tests use tweaker to modify 1 field and make the false assertion
|
|
// on comparing the resulting hash output
|
|
|
|
try := func(t *testing.T, tweak tweaker) {
|
|
originalHash := hash(original)
|
|
modifiable := original.Copy()
|
|
tweak(modifiable)
|
|
tweakedHash := hash(modifiable)
|
|
require.NotEqual(t, originalHash, tweakedHash)
|
|
}
|
|
|
|
t.Run("name", func(t *testing.T) {
|
|
try(t, func(s *sc) { s.Name = "newName" })
|
|
})
|
|
|
|
t.Run("success_before_passing", func(t *testing.T) {
|
|
try(t, func(s *sc) { s.SuccessBeforePassing = 99 })
|
|
})
|
|
|
|
t.Run("failures_before_critical", func(t *testing.T) {
|
|
try(t, func(s *sc) { s.FailuresBeforeCritical = 99 })
|
|
})
|
|
}
|
|
|
|
func TestServiceCheck_validate_PassingTypes(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("valid", func(t *testing.T) {
|
|
for _, checkType := range []string{"tcp", "http", "grpc"} {
|
|
err := (&ServiceCheck{
|
|
Name: "check",
|
|
Type: checkType,
|
|
Path: "/path",
|
|
Interval: 1 * time.Second,
|
|
Timeout: 2 * time.Second,
|
|
SuccessBeforePassing: 3,
|
|
}).validate()
|
|
require.NoError(t, err)
|
|
}
|
|
})
|
|
|
|
t.Run("invalid", func(t *testing.T) {
|
|
err := (&ServiceCheck{
|
|
Name: "check",
|
|
Type: "script",
|
|
Command: "/nothing",
|
|
Interval: 1 * time.Second,
|
|
Timeout: 2 * time.Second,
|
|
SuccessBeforePassing: 3,
|
|
}).validate()
|
|
require.EqualError(t, err, `success_before_passing not supported for check of type "script"`)
|
|
})
|
|
}
|
|
|
|
func TestServiceCheck_validate_FailingTypes(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("valid", func(t *testing.T) {
|
|
for _, checkType := range []string{"tcp", "http", "grpc"} {
|
|
err := (&ServiceCheck{
|
|
Name: "check",
|
|
Type: checkType,
|
|
Path: "/path",
|
|
Interval: 1 * time.Second,
|
|
Timeout: 2 * time.Second,
|
|
FailuresBeforeCritical: 3,
|
|
}).validate()
|
|
require.NoError(t, err)
|
|
}
|
|
})
|
|
|
|
t.Run("invalid", func(t *testing.T) {
|
|
err := (&ServiceCheck{
|
|
Name: "check",
|
|
Type: "script",
|
|
Command: "/nothing",
|
|
Interval: 1 * time.Second,
|
|
Timeout: 2 * time.Second,
|
|
SuccessBeforePassing: 0,
|
|
FailuresBeforeCritical: 3,
|
|
}).validate()
|
|
require.EqualError(t, err, `failures_before_critical not supported for check of type "script"`)
|
|
})
|
|
}
|
|
|
|
func TestServiceCheck_validate_PassFailZero_on_scripts(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("invalid", func(t *testing.T) {
|
|
err := (&ServiceCheck{
|
|
Name: "check",
|
|
Type: "script",
|
|
Command: "/nothing",
|
|
Interval: 1 * time.Second,
|
|
Timeout: 2 * time.Second,
|
|
SuccessBeforePassing: 0, // script checks should still pass validation
|
|
FailuresBeforeCritical: 0, // script checks should still pass validation
|
|
}).validate()
|
|
require.NoError(t, err)
|
|
})
|
|
}
|
|
|
|
func TestServiceCheck_validate_OnUpdate_CheckRestart_Conflict(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("invalid", func(t *testing.T) {
|
|
err := (&ServiceCheck{
|
|
Name: "check",
|
|
Type: "script",
|
|
Command: "/nothing",
|
|
Interval: 1 * time.Second,
|
|
Timeout: 2 * time.Second,
|
|
CheckRestart: &CheckRestart{
|
|
IgnoreWarnings: false,
|
|
Limit: 3,
|
|
Grace: 5 * time.Second,
|
|
},
|
|
OnUpdate: "ignore_warnings",
|
|
}).validate()
|
|
require.EqualError(t, err, `on_update value "ignore_warnings" not supported with check_restart ignore_warnings value "false"`)
|
|
})
|
|
|
|
t.Run("invalid", func(t *testing.T) {
|
|
err := (&ServiceCheck{
|
|
Name: "check",
|
|
Type: "script",
|
|
Command: "/nothing",
|
|
Interval: 1 * time.Second,
|
|
Timeout: 2 * time.Second,
|
|
CheckRestart: &CheckRestart{
|
|
IgnoreWarnings: false,
|
|
Limit: 3,
|
|
Grace: 5 * time.Second,
|
|
},
|
|
OnUpdate: "ignore",
|
|
}).validate()
|
|
require.EqualError(t, err, `on_update value "ignore" is not compatible with check_restart`)
|
|
})
|
|
|
|
t.Run("valid", func(t *testing.T) {
|
|
err := (&ServiceCheck{
|
|
Name: "check",
|
|
Type: "script",
|
|
Command: "/nothing",
|
|
Interval: 1 * time.Second,
|
|
Timeout: 2 * time.Second,
|
|
CheckRestart: &CheckRestart{
|
|
IgnoreWarnings: true,
|
|
Limit: 3,
|
|
Grace: 5 * time.Second,
|
|
},
|
|
OnUpdate: "ignore_warnings",
|
|
}).validate()
|
|
require.NoError(t, err)
|
|
})
|
|
}
|
|
|
|
func TestService_Hash(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
original := &Service{
|
|
Name: "myService",
|
|
PortLabel: "portLabel",
|
|
// AddressMode: "bridge", // not hashed (used internally by Nomad)
|
|
Tags: []string{"original", "tags"},
|
|
CanaryTags: []string{"canary", "tags"},
|
|
// Checks: nil, // not hashed (managed independently)
|
|
Connect: &ConsulConnect{
|
|
// Native: false, // not hashed
|
|
SidecarService: &ConsulSidecarService{
|
|
Tags: []string{"original", "sidecar", "tags"},
|
|
Port: "9000",
|
|
Proxy: &ConsulProxy{
|
|
LocalServiceAddress: "127.0.0.1",
|
|
LocalServicePort: 24000,
|
|
Config: map[string]interface{}{"foo": "bar"},
|
|
Upstreams: []ConsulUpstream{{
|
|
DestinationName: "upstream1",
|
|
LocalBindPort: 29000,
|
|
}},
|
|
},
|
|
},
|
|
// SidecarTask: nil // not hashed
|
|
}}
|
|
|
|
type svc = Service
|
|
type tweaker = func(service *svc)
|
|
|
|
hash := func(s *svc, canary bool) string {
|
|
return s.Hash("AllocID", "TaskName", canary)
|
|
}
|
|
|
|
t.Run("matching and is canary", func(t *testing.T) {
|
|
require.Equal(t, hash(original, true), hash(original, true))
|
|
})
|
|
|
|
t.Run("matching and is not canary", func(t *testing.T) {
|
|
require.Equal(t, hash(original, false), hash(original, false))
|
|
})
|
|
|
|
t.Run("matching mod canary", func(t *testing.T) {
|
|
require.NotEqual(t, hash(original, true), hash(original, false))
|
|
})
|
|
|
|
try := func(t *testing.T, tweak tweaker) {
|
|
originalHash := hash(original, true)
|
|
modifiable := original.Copy()
|
|
tweak(modifiable)
|
|
tweakedHash := hash(modifiable, true)
|
|
require.NotEqual(t, originalHash, tweakedHash)
|
|
}
|
|
|
|
// these tests use tweaker to modify 1 field and make the false assertion
|
|
// on comparing the resulting hash output
|
|
|
|
t.Run("mod name", func(t *testing.T) {
|
|
try(t, func(s *svc) { s.Name = "newName" })
|
|
})
|
|
|
|
t.Run("mod port label", func(t *testing.T) {
|
|
try(t, func(s *svc) { s.PortLabel = "newPortLabel" })
|
|
})
|
|
|
|
t.Run("mod tags", func(t *testing.T) {
|
|
try(t, func(s *svc) { s.Tags = []string{"new", "tags"} })
|
|
})
|
|
|
|
t.Run("mod canary tags", func(t *testing.T) {
|
|
try(t, func(s *svc) { s.CanaryTags = []string{"new", "tags"} })
|
|
})
|
|
|
|
t.Run("mod enable tag override", func(t *testing.T) {
|
|
try(t, func(s *svc) { s.EnableTagOverride = true })
|
|
})
|
|
|
|
t.Run("mod connect sidecar tags", func(t *testing.T) {
|
|
try(t, func(s *svc) { s.Connect.SidecarService.Tags = []string{"new", "tags"} })
|
|
})
|
|
|
|
t.Run("mod connect sidecar port", func(t *testing.T) {
|
|
try(t, func(s *svc) { s.Connect.SidecarService.Port = "9090" })
|
|
})
|
|
|
|
t.Run("mod connect sidecar proxy local service address", func(t *testing.T) {
|
|
try(t, func(s *svc) { s.Connect.SidecarService.Proxy.LocalServiceAddress = "1.1.1.1" })
|
|
})
|
|
|
|
t.Run("mod connect sidecar proxy local service port", func(t *testing.T) {
|
|
try(t, func(s *svc) { s.Connect.SidecarService.Proxy.LocalServicePort = 9999 })
|
|
})
|
|
|
|
t.Run("mod connect sidecar proxy config", func(t *testing.T) {
|
|
try(t, func(s *svc) { s.Connect.SidecarService.Proxy.Config = map[string]interface{}{"foo": "baz"} })
|
|
})
|
|
|
|
t.Run("mod connect sidecar proxy upstream dest name", func(t *testing.T) {
|
|
try(t, func(s *svc) { s.Connect.SidecarService.Proxy.Upstreams[0].DestinationName = "dest2" })
|
|
})
|
|
|
|
t.Run("mod connect sidecar proxy upstream dest local bind port", func(t *testing.T) {
|
|
try(t, func(s *svc) { s.Connect.SidecarService.Proxy.Upstreams[0].LocalBindPort = 29999 })
|
|
})
|
|
}
|
|
|
|
func TestConsulConnect_Validate(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
c := &ConsulConnect{}
|
|
|
|
// An empty Connect stanza is invalid
|
|
require.Error(t, c.Validate())
|
|
|
|
c.Native = true
|
|
require.NoError(t, c.Validate())
|
|
|
|
// Native=true + Sidecar!=nil is invalid
|
|
c.SidecarService = &ConsulSidecarService{}
|
|
require.Error(t, c.Validate())
|
|
|
|
c.Native = false
|
|
require.NoError(t, c.Validate())
|
|
}
|
|
|
|
func TestConsulConnect_CopyEquals(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
c := &ConsulConnect{
|
|
SidecarService: &ConsulSidecarService{
|
|
Tags: []string{"tag1", "tag2"},
|
|
Port: "9001",
|
|
Proxy: &ConsulProxy{
|
|
LocalServiceAddress: "127.0.0.1",
|
|
LocalServicePort: 8080,
|
|
Upstreams: []ConsulUpstream{
|
|
{
|
|
DestinationName: "up1",
|
|
LocalBindPort: 9002,
|
|
},
|
|
{
|
|
DestinationName: "up2",
|
|
LocalBindPort: 9003,
|
|
},
|
|
},
|
|
Config: map[string]interface{}{
|
|
"foo": 1,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
require.NoError(t, c.Validate())
|
|
|
|
// Copies should be equivalent
|
|
o := c.Copy()
|
|
require.True(t, c.Equals(o))
|
|
|
|
o.SidecarService.Proxy.Upstreams = nil
|
|
require.False(t, c.Equals(o))
|
|
}
|
|
|
|
func TestConsulConnect_GatewayProxy_CopyEquals(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
c := &ConsulGatewayProxy{
|
|
ConnectTimeout: helper.TimeToPtr(1 * time.Second),
|
|
EnvoyGatewayBindTaggedAddresses: false,
|
|
EnvoyGatewayBindAddresses: make(map[string]*ConsulGatewayBindAddress),
|
|
}
|
|
|
|
require.NoError(t, c.Validate())
|
|
|
|
// Copies should be equivalent
|
|
o := c.Copy()
|
|
require.Equal(t, c, o)
|
|
require.True(t, c.Equals(o))
|
|
}
|
|
|
|
func TestSidecarTask_MergeIntoTask(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
task := MockJob().TaskGroups[0].Tasks[0]
|
|
sTask := &SidecarTask{
|
|
Name: "sidecar",
|
|
Driver: "sidecar",
|
|
User: "test",
|
|
Config: map[string]interface{}{
|
|
"foo": "bar",
|
|
},
|
|
Resources: &Resources{
|
|
CPU: 10000,
|
|
MemoryMB: 10000,
|
|
},
|
|
Env: map[string]string{
|
|
"sidecar": "proxy",
|
|
},
|
|
Meta: map[string]string{
|
|
"abc": "123",
|
|
},
|
|
KillTimeout: helper.TimeToPtr(15 * time.Second),
|
|
LogConfig: &LogConfig{
|
|
MaxFiles: 3,
|
|
},
|
|
ShutdownDelay: helper.TimeToPtr(5 * time.Second),
|
|
KillSignal: "SIGABRT",
|
|
}
|
|
|
|
expected := task.Copy()
|
|
expected.Name = "sidecar"
|
|
expected.Driver = "sidecar"
|
|
expected.User = "test"
|
|
expected.Config = map[string]interface{}{
|
|
"foo": "bar",
|
|
}
|
|
expected.Resources.CPU = 10000
|
|
expected.Resources.MemoryMB = 10000
|
|
expected.Env["sidecar"] = "proxy"
|
|
expected.Meta["abc"] = "123"
|
|
expected.KillTimeout = 15 * time.Second
|
|
expected.LogConfig.MaxFiles = 3
|
|
expected.ShutdownDelay = 5 * time.Second
|
|
expected.KillSignal = "SIGABRT"
|
|
|
|
sTask.MergeIntoTask(task)
|
|
require.Exactly(t, expected, task)
|
|
|
|
// Check that changing just driver config doesn't replace map
|
|
sTask.Config["abc"] = 123
|
|
expected.Config["abc"] = 123
|
|
|
|
sTask.MergeIntoTask(task)
|
|
require.Exactly(t, expected, task)
|
|
}
|
|
|
|
func TestSidecarTask_Equals(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
original := &SidecarTask{
|
|
Name: "sidecar-task-1",
|
|
Driver: "docker",
|
|
User: "nobody",
|
|
Config: map[string]interface{}{"foo": 1},
|
|
Env: map[string]string{"color": "blue"},
|
|
Resources: &Resources{MemoryMB: 300},
|
|
Meta: map[string]string{"index": "1"},
|
|
KillTimeout: helper.TimeToPtr(2 * time.Second),
|
|
LogConfig: &LogConfig{
|
|
MaxFiles: 2,
|
|
MaxFileSizeMB: 300,
|
|
},
|
|
ShutdownDelay: helper.TimeToPtr(10 * time.Second),
|
|
KillSignal: "SIGTERM",
|
|
}
|
|
|
|
t.Run("unmodified", func(t *testing.T) {
|
|
duplicate := original.Copy()
|
|
require.True(t, duplicate.Equals(original))
|
|
})
|
|
|
|
type st = SidecarTask
|
|
type tweaker = func(task *st)
|
|
|
|
try := func(t *testing.T, tweak tweaker) {
|
|
modified := original.Copy()
|
|
tweak(modified)
|
|
require.NotEqual(t, original, modified)
|
|
}
|
|
|
|
t.Run("mod name", func(t *testing.T) {
|
|
try(t, func(s *st) { s.Name = "sidecar-task-2" })
|
|
})
|
|
|
|
t.Run("mod driver", func(t *testing.T) {
|
|
try(t, func(s *st) { s.Driver = "exec" })
|
|
})
|
|
|
|
t.Run("mod user", func(t *testing.T) {
|
|
try(t, func(s *st) { s.User = "root" })
|
|
})
|
|
|
|
t.Run("mod config", func(t *testing.T) {
|
|
try(t, func(s *st) { s.Config = map[string]interface{}{"foo": 2} })
|
|
})
|
|
|
|
t.Run("mod env", func(t *testing.T) {
|
|
try(t, func(s *st) { s.Env = map[string]string{"color": "red"} })
|
|
})
|
|
|
|
t.Run("mod resources", func(t *testing.T) {
|
|
try(t, func(s *st) { s.Resources = &Resources{MemoryMB: 200} })
|
|
})
|
|
|
|
t.Run("mod meta", func(t *testing.T) {
|
|
try(t, func(s *st) { s.Meta = map[string]string{"index": "2"} })
|
|
})
|
|
|
|
t.Run("mod kill timeout", func(t *testing.T) {
|
|
try(t, func(s *st) { s.KillTimeout = helper.TimeToPtr(3 * time.Second) })
|
|
})
|
|
|
|
t.Run("mod log config", func(t *testing.T) {
|
|
try(t, func(s *st) { s.LogConfig = &LogConfig{MaxFiles: 3} })
|
|
})
|
|
|
|
t.Run("mod shutdown delay", func(t *testing.T) {
|
|
try(t, func(s *st) { s.ShutdownDelay = helper.TimeToPtr(20 * time.Second) })
|
|
})
|
|
|
|
t.Run("mod kill signal", func(t *testing.T) {
|
|
try(t, func(s *st) { s.KillSignal = "SIGHUP" })
|
|
})
|
|
}
|
|
|
|
func TestConsulUpstream_upstreamEquals(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
up := func(name string, port int) ConsulUpstream {
|
|
return ConsulUpstream{
|
|
DestinationName: name,
|
|
LocalBindPort: port,
|
|
}
|
|
}
|
|
|
|
t.Run("size mismatch", func(t *testing.T) {
|
|
a := []ConsulUpstream{up("foo", 8000)}
|
|
b := []ConsulUpstream{up("foo", 8000), up("bar", 9000)}
|
|
require.False(t, upstreamsEquals(a, b))
|
|
})
|
|
|
|
t.Run("different", func(t *testing.T) {
|
|
a := []ConsulUpstream{up("bar", 9000)}
|
|
b := []ConsulUpstream{up("foo", 8000)}
|
|
require.False(t, upstreamsEquals(a, b))
|
|
})
|
|
|
|
t.Run("different mesh_gateway", func(t *testing.T) {
|
|
a := []ConsulUpstream{{DestinationName: "foo", MeshGateway: &ConsulMeshGateway{Mode: "local"}}}
|
|
b := []ConsulUpstream{{DestinationName: "foo", MeshGateway: &ConsulMeshGateway{Mode: "remote"}}}
|
|
require.False(t, upstreamsEquals(a, b))
|
|
})
|
|
|
|
t.Run("identical", func(t *testing.T) {
|
|
a := []ConsulUpstream{up("foo", 8000), up("bar", 9000)}
|
|
b := []ConsulUpstream{up("foo", 8000), up("bar", 9000)}
|
|
require.True(t, upstreamsEquals(a, b))
|
|
})
|
|
|
|
t.Run("unsorted", func(t *testing.T) {
|
|
a := []ConsulUpstream{up("foo", 8000), up("bar", 9000)}
|
|
b := []ConsulUpstream{up("bar", 9000), up("foo", 8000)}
|
|
require.True(t, upstreamsEquals(a, b))
|
|
})
|
|
}
|
|
|
|
func TestConsulExposePath_exposePathsEqual(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
expose := func(path, protocol, listen string, local int) ConsulExposePath {
|
|
return ConsulExposePath{
|
|
Path: path,
|
|
Protocol: protocol,
|
|
LocalPathPort: local,
|
|
ListenerPort: listen,
|
|
}
|
|
}
|
|
|
|
t.Run("size mismatch", func(t *testing.T) {
|
|
a := []ConsulExposePath{expose("/1", "http", "myPort", 8000)}
|
|
b := []ConsulExposePath{expose("/1", "http", "myPort", 8000), expose("/2", "http", "myPort", 8000)}
|
|
require.False(t, exposePathsEqual(a, b))
|
|
})
|
|
|
|
t.Run("different", func(t *testing.T) {
|
|
a := []ConsulExposePath{expose("/1", "http", "myPort", 8000)}
|
|
b := []ConsulExposePath{expose("/2", "http", "myPort", 8000)}
|
|
require.False(t, exposePathsEqual(a, b))
|
|
})
|
|
|
|
t.Run("identical", func(t *testing.T) {
|
|
a := []ConsulExposePath{expose("/1", "http", "myPort", 8000)}
|
|
b := []ConsulExposePath{expose("/1", "http", "myPort", 8000)}
|
|
require.True(t, exposePathsEqual(a, b))
|
|
})
|
|
|
|
t.Run("unsorted", func(t *testing.T) {
|
|
a := []ConsulExposePath{expose("/2", "http", "myPort", 8000), expose("/1", "http", "myPort", 8000)}
|
|
b := []ConsulExposePath{expose("/1", "http", "myPort", 8000), expose("/2", "http", "myPort", 8000)}
|
|
require.True(t, exposePathsEqual(a, b))
|
|
})
|
|
}
|
|
|
|
func TestConsulExposeConfig_Copy(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
require.Nil(t, (*ConsulExposeConfig)(nil).Copy())
|
|
require.Equal(t, &ConsulExposeConfig{
|
|
Paths: []ConsulExposePath{{
|
|
Path: "/health",
|
|
}},
|
|
}, (&ConsulExposeConfig{
|
|
Paths: []ConsulExposePath{{
|
|
Path: "/health",
|
|
}},
|
|
}).Copy())
|
|
}
|
|
|
|
func TestConsulExposeConfig_Equals(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
require.True(t, (*ConsulExposeConfig)(nil).Equals(nil))
|
|
require.True(t, (&ConsulExposeConfig{
|
|
Paths: []ConsulExposePath{{
|
|
Path: "/health",
|
|
}},
|
|
}).Equals(&ConsulExposeConfig{
|
|
Paths: []ConsulExposePath{{
|
|
Path: "/health",
|
|
}},
|
|
}))
|
|
}
|
|
|
|
func TestConsulSidecarService_Copy(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("nil", func(t *testing.T) {
|
|
s := (*ConsulSidecarService)(nil)
|
|
result := s.Copy()
|
|
require.Nil(t, result)
|
|
})
|
|
|
|
t.Run("not nil", func(t *testing.T) {
|
|
s := &ConsulSidecarService{
|
|
Tags: []string{"foo", "bar"},
|
|
Port: "port1",
|
|
Proxy: &ConsulProxy{LocalServiceAddress: "10.0.0.1"},
|
|
}
|
|
result := s.Copy()
|
|
require.Equal(t, &ConsulSidecarService{
|
|
Tags: []string{"foo", "bar"},
|
|
Port: "port1",
|
|
Proxy: &ConsulProxy{LocalServiceAddress: "10.0.0.1"},
|
|
}, result)
|
|
})
|
|
}
|
|
|
|
var (
|
|
consulIngressGateway1 = &ConsulGateway{
|
|
Proxy: &ConsulGatewayProxy{
|
|
ConnectTimeout: helper.TimeToPtr(1 * time.Second),
|
|
EnvoyGatewayBindTaggedAddresses: true,
|
|
EnvoyGatewayBindAddresses: map[string]*ConsulGatewayBindAddress{
|
|
"listener1": {Address: "10.0.0.1", Port: 2001},
|
|
"listener2": {Address: "10.0.0.1", Port: 2002},
|
|
},
|
|
EnvoyGatewayNoDefaultBind: true,
|
|
Config: map[string]interface{}{
|
|
"foo": 1,
|
|
},
|
|
},
|
|
Ingress: &ConsulIngressConfigEntry{
|
|
TLS: &ConsulGatewayTLSConfig{
|
|
Enabled: true,
|
|
},
|
|
Listeners: []*ConsulIngressListener{{
|
|
Port: 3000,
|
|
Protocol: "http",
|
|
Services: []*ConsulIngressService{{
|
|
Name: "service1",
|
|
Hosts: []string{"10.0.0.1", "10.0.0.1:3000"},
|
|
}, {
|
|
Name: "service2",
|
|
Hosts: []string{"10.0.0.2", "10.0.0.2:3000"},
|
|
}},
|
|
}, {
|
|
Port: 3001,
|
|
Protocol: "tcp",
|
|
Services: []*ConsulIngressService{{
|
|
Name: "service3",
|
|
}},
|
|
}},
|
|
},
|
|
}
|
|
|
|
consulTerminatingGateway1 = &ConsulGateway{
|
|
Proxy: &ConsulGatewayProxy{
|
|
ConnectTimeout: helper.TimeToPtr(1 * time.Second),
|
|
EnvoyDNSDiscoveryType: "STRICT_DNS",
|
|
EnvoyGatewayBindAddresses: nil,
|
|
},
|
|
Terminating: &ConsulTerminatingConfigEntry{
|
|
Services: []*ConsulLinkedService{{
|
|
Name: "linked-service1",
|
|
CAFile: "ca.pem",
|
|
CertFile: "cert.pem",
|
|
KeyFile: "key.pem",
|
|
SNI: "service1.consul",
|
|
}, {
|
|
Name: "linked-service2",
|
|
}},
|
|
},
|
|
}
|
|
|
|
consulMeshGateway1 = &ConsulGateway{
|
|
Proxy: &ConsulGatewayProxy{
|
|
ConnectTimeout: helper.TimeToPtr(1 * time.Second),
|
|
},
|
|
Mesh: &ConsulMeshConfigEntry{
|
|
// nothing
|
|
},
|
|
}
|
|
)
|
|
|
|
func TestConsulGateway_Prefix(t *testing.T) {
|
|
t.Run("ingress", func(t *testing.T) {
|
|
result := (&ConsulGateway{Ingress: new(ConsulIngressConfigEntry)}).Prefix()
|
|
require.Equal(t, ConnectIngressPrefix, result)
|
|
})
|
|
|
|
t.Run("terminating", func(t *testing.T) {
|
|
result := (&ConsulGateway{Terminating: new(ConsulTerminatingConfigEntry)}).Prefix()
|
|
require.Equal(t, ConnectTerminatingPrefix, result)
|
|
})
|
|
|
|
t.Run("mesh", func(t *testing.T) {
|
|
result := (&ConsulGateway{Mesh: new(ConsulMeshConfigEntry)}).Prefix()
|
|
require.Equal(t, ConnectMeshPrefix, result)
|
|
})
|
|
}
|
|
|
|
func TestConsulGateway_Copy(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("nil", func(t *testing.T) {
|
|
g := (*ConsulGateway)(nil)
|
|
result := g.Copy()
|
|
require.Nil(t, result)
|
|
})
|
|
|
|
t.Run("as ingress", func(t *testing.T) {
|
|
result := consulIngressGateway1.Copy()
|
|
require.Equal(t, consulIngressGateway1, result)
|
|
require.True(t, result.Equals(consulIngressGateway1))
|
|
require.True(t, consulIngressGateway1.Equals(result))
|
|
})
|
|
|
|
t.Run("as terminating", func(t *testing.T) {
|
|
result := consulTerminatingGateway1.Copy()
|
|
require.Equal(t, consulTerminatingGateway1, result)
|
|
require.True(t, result.Equals(consulTerminatingGateway1))
|
|
require.True(t, consulTerminatingGateway1.Equals(result))
|
|
})
|
|
|
|
t.Run("as mesh", func(t *testing.T) {
|
|
result := consulMeshGateway1.Copy()
|
|
require.Equal(t, consulMeshGateway1, result)
|
|
require.True(t, result.Equals(consulMeshGateway1))
|
|
require.True(t, consulMeshGateway1.Equals(result))
|
|
})
|
|
}
|
|
|
|
func TestConsulGateway_Equals_mesh(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("nil", func(t *testing.T) {
|
|
a := (*ConsulGateway)(nil)
|
|
b := (*ConsulGateway)(nil)
|
|
require.True(t, a.Equals(b))
|
|
require.False(t, a.Equals(consulMeshGateway1))
|
|
require.False(t, consulMeshGateway1.Equals(a))
|
|
})
|
|
|
|
t.Run("reflexive", func(t *testing.T) {
|
|
require.True(t, consulMeshGateway1.Equals(consulMeshGateway1))
|
|
})
|
|
}
|
|
|
|
func TestConsulGateway_Equals_ingress(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("nil", func(t *testing.T) {
|
|
a := (*ConsulGateway)(nil)
|
|
b := (*ConsulGateway)(nil)
|
|
require.True(t, a.Equals(b))
|
|
require.False(t, a.Equals(consulIngressGateway1))
|
|
require.False(t, consulIngressGateway1.Equals(a))
|
|
})
|
|
|
|
original := consulIngressGateway1.Copy()
|
|
|
|
type cg = ConsulGateway
|
|
type tweaker = func(g *cg)
|
|
|
|
t.Run("reflexive", func(t *testing.T) {
|
|
require.True(t, original.Equals(original))
|
|
})
|
|
|
|
try := func(t *testing.T, tweak tweaker) {
|
|
modifiable := original.Copy()
|
|
tweak(modifiable)
|
|
require.False(t, original.Equals(modifiable))
|
|
require.False(t, modifiable.Equals(original))
|
|
require.True(t, modifiable.Equals(modifiable))
|
|
}
|
|
|
|
// proxy stanza equality checks
|
|
|
|
t.Run("mod gateway timeout", func(t *testing.T) {
|
|
try(t, func(g *cg) { g.Proxy.ConnectTimeout = helper.TimeToPtr(9 * time.Second) })
|
|
})
|
|
|
|
t.Run("mod gateway envoy_gateway_bind_tagged_addresses", func(t *testing.T) {
|
|
try(t, func(g *cg) { g.Proxy.EnvoyGatewayBindTaggedAddresses = false })
|
|
})
|
|
|
|
t.Run("mod gateway envoy_gateway_bind_addresses", func(t *testing.T) {
|
|
try(t, func(g *cg) {
|
|
g.Proxy.EnvoyGatewayBindAddresses = map[string]*ConsulGatewayBindAddress{
|
|
"listener3": {Address: "9.9.9.9", Port: 9999},
|
|
}
|
|
})
|
|
})
|
|
|
|
t.Run("mod gateway envoy_gateway_no_default_bind", func(t *testing.T) {
|
|
try(t, func(g *cg) { g.Proxy.EnvoyGatewayNoDefaultBind = false })
|
|
})
|
|
|
|
t.Run("mod gateway config", func(t *testing.T) {
|
|
try(t, func(g *cg) {
|
|
g.Proxy.Config = map[string]interface{}{
|
|
"foo": 2,
|
|
}
|
|
})
|
|
})
|
|
|
|
// ingress config entry equality checks
|
|
|
|
t.Run("mod ingress tls", func(t *testing.T) {
|
|
try(t, func(g *cg) { g.Ingress.TLS = nil })
|
|
try(t, func(g *cg) { g.Ingress.TLS.Enabled = false })
|
|
})
|
|
|
|
t.Run("mod ingress listeners count", func(t *testing.T) {
|
|
try(t, func(g *cg) { g.Ingress.Listeners = g.Ingress.Listeners[:1] })
|
|
})
|
|
|
|
t.Run("mod ingress listeners port", func(t *testing.T) {
|
|
try(t, func(g *cg) { g.Ingress.Listeners[0].Port = 7777 })
|
|
})
|
|
|
|
t.Run("mod ingress listeners protocol", func(t *testing.T) {
|
|
try(t, func(g *cg) { g.Ingress.Listeners[0].Protocol = "tcp" })
|
|
})
|
|
|
|
t.Run("mod ingress listeners services count", func(t *testing.T) {
|
|
try(t, func(g *cg) { g.Ingress.Listeners[0].Services = g.Ingress.Listeners[0].Services[:1] })
|
|
})
|
|
|
|
t.Run("mod ingress listeners services name", func(t *testing.T) {
|
|
try(t, func(g *cg) { g.Ingress.Listeners[0].Services[0].Name = "serviceX" })
|
|
})
|
|
|
|
t.Run("mod ingress listeners services hosts count", func(t *testing.T) {
|
|
try(t, func(g *cg) { g.Ingress.Listeners[0].Services[0].Hosts = g.Ingress.Listeners[0].Services[0].Hosts[:1] })
|
|
})
|
|
|
|
t.Run("mod ingress listeners services hosts content", func(t *testing.T) {
|
|
try(t, func(g *cg) { g.Ingress.Listeners[0].Services[0].Hosts[0] = "255.255.255.255" })
|
|
})
|
|
}
|
|
|
|
func TestConsulGateway_Equals_terminating(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
original := consulTerminatingGateway1.Copy()
|
|
|
|
type cg = ConsulGateway
|
|
type tweaker = func(c *cg)
|
|
|
|
t.Run("reflexive", func(t *testing.T) {
|
|
require.True(t, original.Equals(original))
|
|
})
|
|
|
|
try := func(t *testing.T, tweak tweaker) {
|
|
modifiable := original.Copy()
|
|
tweak(modifiable)
|
|
require.False(t, original.Equals(modifiable))
|
|
require.False(t, modifiable.Equals(original))
|
|
require.True(t, modifiable.Equals(modifiable))
|
|
}
|
|
|
|
// proxy stanza equality checks
|
|
|
|
t.Run("mod dns discovery type", func(t *testing.T) {
|
|
try(t, func(g *cg) { g.Proxy.EnvoyDNSDiscoveryType = "LOGICAL_DNS" })
|
|
})
|
|
|
|
// terminating config entry equality checks
|
|
|
|
t.Run("mod terminating services count", func(t *testing.T) {
|
|
try(t, func(g *cg) { g.Terminating.Services = g.Terminating.Services[:1] })
|
|
})
|
|
|
|
t.Run("mod terminating services name", func(t *testing.T) {
|
|
try(t, func(g *cg) { g.Terminating.Services[0].Name = "foo" })
|
|
})
|
|
|
|
t.Run("mod terminating services ca_file", func(t *testing.T) {
|
|
try(t, func(g *cg) { g.Terminating.Services[0].CAFile = "foo.pem" })
|
|
})
|
|
|
|
t.Run("mod terminating services cert_file", func(t *testing.T) {
|
|
try(t, func(g *cg) { g.Terminating.Services[0].CertFile = "foo.pem" })
|
|
})
|
|
|
|
t.Run("mod terminating services key_file", func(t *testing.T) {
|
|
try(t, func(g *cg) { g.Terminating.Services[0].KeyFile = "foo.pem" })
|
|
})
|
|
|
|
t.Run("mod terminating services sni", func(t *testing.T) {
|
|
try(t, func(g *cg) { g.Terminating.Services[0].SNI = "foo.consul" })
|
|
})
|
|
}
|
|
|
|
func TestConsulGateway_ingressServicesEqual(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
igs1 := []*ConsulIngressService{{
|
|
Name: "service1",
|
|
Hosts: []string{"host1", "host2"},
|
|
}, {
|
|
Name: "service2",
|
|
Hosts: []string{"host3"},
|
|
}}
|
|
|
|
require.False(t, ingressServicesEqual(igs1, nil))
|
|
require.True(t, ingressServicesEqual(igs1, igs1))
|
|
|
|
reversed := []*ConsulIngressService{
|
|
igs1[1], igs1[0], // services reversed
|
|
}
|
|
|
|
require.True(t, ingressServicesEqual(igs1, reversed))
|
|
|
|
hostOrder := []*ConsulIngressService{{
|
|
Name: "service1",
|
|
Hosts: []string{"host2", "host1"}, // hosts reversed
|
|
}, {
|
|
Name: "service2",
|
|
Hosts: []string{"host3"},
|
|
}}
|
|
|
|
require.True(t, ingressServicesEqual(igs1, hostOrder))
|
|
}
|
|
|
|
func TestConsulGateway_ingressListenersEqual(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ils1 := []*ConsulIngressListener{{
|
|
Port: 2000,
|
|
Protocol: "http",
|
|
Services: []*ConsulIngressService{{
|
|
Name: "service1",
|
|
Hosts: []string{"host1", "host2"},
|
|
}},
|
|
}, {
|
|
Port: 2001,
|
|
Protocol: "tcp",
|
|
Services: []*ConsulIngressService{{
|
|
Name: "service2",
|
|
}},
|
|
}}
|
|
|
|
require.False(t, ingressListenersEqual(ils1, nil))
|
|
|
|
reversed := []*ConsulIngressListener{
|
|
ils1[1], ils1[0],
|
|
}
|
|
|
|
require.True(t, ingressListenersEqual(ils1, reversed))
|
|
}
|
|
|
|
func TestConsulGateway_Validate(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("bad proxy", func(t *testing.T) {
|
|
err := (&ConsulGateway{
|
|
Proxy: &ConsulGatewayProxy{
|
|
ConnectTimeout: nil,
|
|
},
|
|
Ingress: nil,
|
|
}).Validate()
|
|
require.EqualError(t, err, "Consul Gateway Proxy connection_timeout must be set")
|
|
})
|
|
|
|
t.Run("bad ingress config entry", func(t *testing.T) {
|
|
err := (&ConsulGateway{
|
|
Ingress: &ConsulIngressConfigEntry{
|
|
Listeners: nil,
|
|
},
|
|
}).Validate()
|
|
require.EqualError(t, err, "Consul Ingress Gateway requires at least one listener")
|
|
})
|
|
|
|
t.Run("bad terminating config entry", func(t *testing.T) {
|
|
err := (&ConsulGateway{
|
|
Terminating: &ConsulTerminatingConfigEntry{
|
|
Services: nil,
|
|
},
|
|
}).Validate()
|
|
require.EqualError(t, err, "Consul Terminating Gateway requires at least one service")
|
|
})
|
|
|
|
t.Run("no config entry set", func(t *testing.T) {
|
|
err := (&ConsulGateway{
|
|
Ingress: nil,
|
|
Terminating: nil,
|
|
Mesh: nil,
|
|
}).Validate()
|
|
require.EqualError(t, err, "One Consul Gateway Configuration must be set")
|
|
})
|
|
|
|
t.Run("multiple config entries set", func(t *testing.T) {
|
|
err := (&ConsulGateway{
|
|
Ingress: &ConsulIngressConfigEntry{
|
|
Listeners: []*ConsulIngressListener{{
|
|
Port: 1111,
|
|
Protocol: "tcp",
|
|
Services: []*ConsulIngressService{{
|
|
Name: "service1",
|
|
}},
|
|
}},
|
|
},
|
|
Terminating: &ConsulTerminatingConfigEntry{
|
|
Services: []*ConsulLinkedService{{
|
|
Name: "linked-service1",
|
|
}},
|
|
},
|
|
}).Validate()
|
|
require.EqualError(t, err, "One Consul Gateway Configuration must be set")
|
|
})
|
|
|
|
t.Run("ok mesh", func(t *testing.T) {
|
|
err := (&ConsulGateway{
|
|
Mesh: new(ConsulMeshConfigEntry),
|
|
}).Validate()
|
|
require.NoError(t, err)
|
|
})
|
|
}
|
|
|
|
func TestConsulGatewayBindAddress_Validate(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("no address", func(t *testing.T) {
|
|
err := (&ConsulGatewayBindAddress{
|
|
Address: "",
|
|
Port: 2000,
|
|
}).Validate()
|
|
require.EqualError(t, err, "Consul Gateway Bind Address must be set")
|
|
})
|
|
|
|
t.Run("invalid port", func(t *testing.T) {
|
|
err := (&ConsulGatewayBindAddress{
|
|
Address: "10.0.0.1",
|
|
Port: 0,
|
|
}).Validate()
|
|
require.EqualError(t, err, "Consul Gateway Bind Address must set valid Port")
|
|
})
|
|
|
|
t.Run("ok", func(t *testing.T) {
|
|
err := (&ConsulGatewayBindAddress{
|
|
Address: "10.0.0.1",
|
|
Port: 2000,
|
|
}).Validate()
|
|
require.NoError(t, err)
|
|
})
|
|
}
|
|
|
|
func TestConsulGatewayProxy_Validate(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("no timeout", func(t *testing.T) {
|
|
err := (&ConsulGatewayProxy{
|
|
ConnectTimeout: nil,
|
|
}).Validate()
|
|
require.EqualError(t, err, "Consul Gateway Proxy connection_timeout must be set")
|
|
})
|
|
|
|
t.Run("invalid bind address", func(t *testing.T) {
|
|
err := (&ConsulGatewayProxy{
|
|
ConnectTimeout: helper.TimeToPtr(1 * time.Second),
|
|
EnvoyGatewayBindAddresses: map[string]*ConsulGatewayBindAddress{
|
|
"service1": {
|
|
Address: "10.0.0.1",
|
|
Port: 0,
|
|
}},
|
|
}).Validate()
|
|
require.EqualError(t, err, "Consul Gateway Bind Address must set valid Port")
|
|
})
|
|
|
|
t.Run("invalid dns discovery type", func(t *testing.T) {
|
|
err := (&ConsulGatewayProxy{
|
|
ConnectTimeout: helper.TimeToPtr(1 * time.Second),
|
|
EnvoyDNSDiscoveryType: "RANDOM_DNS",
|
|
}).Validate()
|
|
require.EqualError(t, err, "Consul Gateway Proxy Envoy DNS Discovery type must be STRICT_DNS or LOGICAL_DNS")
|
|
})
|
|
|
|
t.Run("ok with nothing set", func(t *testing.T) {
|
|
err := (&ConsulGatewayProxy{
|
|
ConnectTimeout: helper.TimeToPtr(1 * time.Second),
|
|
}).Validate()
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("ok with everything set", func(t *testing.T) {
|
|
err := (&ConsulGatewayProxy{
|
|
ConnectTimeout: helper.TimeToPtr(1 * time.Second),
|
|
EnvoyGatewayBindAddresses: map[string]*ConsulGatewayBindAddress{
|
|
"service1": {
|
|
Address: "10.0.0.1",
|
|
Port: 2000,
|
|
}},
|
|
EnvoyGatewayBindTaggedAddresses: true,
|
|
EnvoyGatewayNoDefaultBind: true,
|
|
}).Validate()
|
|
require.NoError(t, err)
|
|
})
|
|
}
|
|
|
|
func TestConsulIngressService_Validate(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("invalid name", func(t *testing.T) {
|
|
err := (&ConsulIngressService{
|
|
Name: "",
|
|
}).Validate(true)
|
|
require.EqualError(t, err, "Consul Ingress Service requires a name")
|
|
})
|
|
|
|
t.Run("http missing hosts", func(t *testing.T) {
|
|
err := (&ConsulIngressService{
|
|
Name: "service1",
|
|
}).Validate(true)
|
|
require.EqualError(t, err, "Consul Ingress Service requires one or more hosts when using HTTP protocol")
|
|
})
|
|
|
|
t.Run("tcp extraneous hosts", func(t *testing.T) {
|
|
err := (&ConsulIngressService{
|
|
Name: "service1",
|
|
Hosts: []string{"host1"},
|
|
}).Validate(false)
|
|
require.EqualError(t, err, "Consul Ingress Service supports hosts only when using HTTP protocol")
|
|
})
|
|
|
|
t.Run("ok tcp", func(t *testing.T) {
|
|
err := (&ConsulIngressService{
|
|
Name: "service1",
|
|
}).Validate(false)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("ok http", func(t *testing.T) {
|
|
err := (&ConsulIngressService{
|
|
Name: "service1",
|
|
Hosts: []string{"host1"},
|
|
}).Validate(true)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("http with wildcard service", func(t *testing.T) {
|
|
err := (&ConsulIngressService{
|
|
Name: "*",
|
|
}).Validate(true)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("tcp with wildcard service", func(t *testing.T) {
|
|
err := (&ConsulIngressService{
|
|
Name: "*",
|
|
}).Validate(false)
|
|
require.EqualError(t, err, "Consul Ingress Service supports wildcard names only with HTTP protocol")
|
|
})
|
|
}
|
|
|
|
func TestConsulIngressListener_Validate(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("invalid port", func(t *testing.T) {
|
|
err := (&ConsulIngressListener{
|
|
Port: 0,
|
|
Protocol: "tcp",
|
|
Services: []*ConsulIngressService{{
|
|
Name: "service1",
|
|
}},
|
|
}).Validate()
|
|
require.EqualError(t, err, "Consul Ingress Listener requires valid Port")
|
|
})
|
|
|
|
t.Run("invalid protocol", func(t *testing.T) {
|
|
err := (&ConsulIngressListener{
|
|
Port: 2000,
|
|
Protocol: "gopher",
|
|
Services: []*ConsulIngressService{{
|
|
Name: "service1",
|
|
}},
|
|
}).Validate()
|
|
require.EqualError(t, err, `Consul Ingress Listener requires protocol of "http" or "tcp", got "gopher"`)
|
|
})
|
|
|
|
t.Run("no services", func(t *testing.T) {
|
|
err := (&ConsulIngressListener{
|
|
Port: 2000,
|
|
Protocol: "tcp",
|
|
Services: nil,
|
|
}).Validate()
|
|
require.EqualError(t, err, "Consul Ingress Listener requires one or more services")
|
|
})
|
|
|
|
t.Run("invalid service", func(t *testing.T) {
|
|
err := (&ConsulIngressListener{
|
|
Port: 2000,
|
|
Protocol: "tcp",
|
|
Services: []*ConsulIngressService{{
|
|
Name: "",
|
|
}},
|
|
}).Validate()
|
|
require.EqualError(t, err, "Consul Ingress Service requires a name")
|
|
})
|
|
|
|
t.Run("ok", func(t *testing.T) {
|
|
err := (&ConsulIngressListener{
|
|
Port: 2000,
|
|
Protocol: "tcp",
|
|
Services: []*ConsulIngressService{{
|
|
Name: "service1",
|
|
}},
|
|
}).Validate()
|
|
require.NoError(t, err)
|
|
})
|
|
}
|
|
|
|
func TestConsulIngressConfigEntry_Validate(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("no listeners", func(t *testing.T) {
|
|
err := (&ConsulIngressConfigEntry{}).Validate()
|
|
require.EqualError(t, err, "Consul Ingress Gateway requires at least one listener")
|
|
})
|
|
|
|
t.Run("invalid listener", func(t *testing.T) {
|
|
err := (&ConsulIngressConfigEntry{
|
|
Listeners: []*ConsulIngressListener{{
|
|
Port: 9000,
|
|
Protocol: "tcp",
|
|
}},
|
|
}).Validate()
|
|
require.EqualError(t, err, "Consul Ingress Listener requires one or more services")
|
|
})
|
|
|
|
t.Run("full", func(t *testing.T) {
|
|
err := (&ConsulIngressConfigEntry{
|
|
TLS: &ConsulGatewayTLSConfig{
|
|
Enabled: true,
|
|
},
|
|
Listeners: []*ConsulIngressListener{{
|
|
Port: 9000,
|
|
Protocol: "tcp",
|
|
Services: []*ConsulIngressService{{
|
|
Name: "service1",
|
|
}},
|
|
}},
|
|
}).Validate()
|
|
require.NoError(t, err)
|
|
})
|
|
}
|
|
|
|
func TestConsulLinkedService_Validate(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("nil", func(t *testing.T) {
|
|
err := (*ConsulLinkedService)(nil).Validate()
|
|
require.Nil(t, err)
|
|
})
|
|
|
|
t.Run("missing name", func(t *testing.T) {
|
|
err := (&ConsulLinkedService{}).Validate()
|
|
require.EqualError(t, err, "Consul Linked Service requires Name")
|
|
})
|
|
|
|
t.Run("missing ca_file", func(t *testing.T) {
|
|
err := (&ConsulLinkedService{
|
|
Name: "linked-service1",
|
|
CertFile: "cert_file.pem",
|
|
KeyFile: "key_file.pem",
|
|
}).Validate()
|
|
require.EqualError(t, err, "Consul Linked Service TLS requires CAFile")
|
|
})
|
|
|
|
t.Run("mutual cert key", func(t *testing.T) {
|
|
err := (&ConsulLinkedService{
|
|
Name: "linked-service1",
|
|
CAFile: "ca_file.pem",
|
|
CertFile: "cert_file.pem",
|
|
}).Validate()
|
|
require.EqualError(t, err, "Consul Linked Service TLS Cert and Key must both be set")
|
|
})
|
|
|
|
t.Run("sni without ca_file", func(t *testing.T) {
|
|
err := (&ConsulLinkedService{
|
|
Name: "linked-service1",
|
|
SNI: "service.consul",
|
|
}).Validate()
|
|
require.EqualError(t, err, "Consul Linked Service TLS SNI requires CAFile")
|
|
})
|
|
|
|
t.Run("minimal", func(t *testing.T) {
|
|
err := (&ConsulLinkedService{
|
|
Name: "linked-service1",
|
|
}).Validate()
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("tls minimal", func(t *testing.T) {
|
|
err := (&ConsulLinkedService{
|
|
Name: "linked-service1",
|
|
CAFile: "ca_file.pem",
|
|
}).Validate()
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("tls mutual", func(t *testing.T) {
|
|
err := (&ConsulLinkedService{
|
|
Name: "linked-service1",
|
|
CAFile: "ca_file.pem",
|
|
CertFile: "cert_file.pem",
|
|
KeyFile: "key_file.pem",
|
|
}).Validate()
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("tls sni", func(t *testing.T) {
|
|
err := (&ConsulLinkedService{
|
|
Name: "linked-service1",
|
|
CAFile: "ca_file.pem",
|
|
SNI: "linked-service.consul",
|
|
}).Validate()
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("tls complete", func(t *testing.T) {
|
|
err := (&ConsulLinkedService{
|
|
Name: "linked-service1",
|
|
CAFile: "ca_file.pem",
|
|
CertFile: "cert_file.pem",
|
|
KeyFile: "key_file.pem",
|
|
SNI: "linked-service.consul",
|
|
}).Validate()
|
|
require.NoError(t, err)
|
|
})
|
|
}
|
|
|
|
func TestConsulLinkedService_Copy(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
require.Nil(t, (*ConsulLinkedService)(nil).Copy())
|
|
require.Equal(t, &ConsulLinkedService{
|
|
Name: "service1",
|
|
CAFile: "ca.pem",
|
|
CertFile: "cert.pem",
|
|
KeyFile: "key.pem",
|
|
SNI: "service1.consul",
|
|
}, (&ConsulLinkedService{
|
|
Name: "service1",
|
|
CAFile: "ca.pem",
|
|
CertFile: "cert.pem",
|
|
KeyFile: "key.pem",
|
|
SNI: "service1.consul",
|
|
}).Copy())
|
|
}
|
|
|
|
func TestConsulLinkedService_linkedServicesEqual(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
services := []*ConsulLinkedService{{
|
|
Name: "service1",
|
|
CAFile: "ca.pem",
|
|
}, {
|
|
Name: "service2",
|
|
CAFile: "ca.pem",
|
|
}}
|
|
|
|
require.False(t, linkedServicesEqual(services, nil))
|
|
require.True(t, linkedServicesEqual(services, services))
|
|
|
|
reversed := []*ConsulLinkedService{
|
|
services[1], services[0], // reversed
|
|
}
|
|
|
|
require.True(t, linkedServicesEqual(services, reversed))
|
|
|
|
different := []*ConsulLinkedService{
|
|
services[0], {
|
|
Name: "service2",
|
|
CAFile: "ca.pem",
|
|
SNI: "service2.consul",
|
|
},
|
|
}
|
|
|
|
require.False(t, linkedServicesEqual(services, different))
|
|
}
|
|
|
|
func TestConsulTerminatingConfigEntry_Validate(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("nil", func(t *testing.T) {
|
|
err := (*ConsulTerminatingConfigEntry)(nil).Validate()
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("no services", func(t *testing.T) {
|
|
err := (&ConsulTerminatingConfigEntry{
|
|
Services: make([]*ConsulLinkedService, 0),
|
|
}).Validate()
|
|
require.EqualError(t, err, "Consul Terminating Gateway requires at least one service")
|
|
})
|
|
|
|
t.Run("service invalid", func(t *testing.T) {
|
|
err := (&ConsulTerminatingConfigEntry{
|
|
Services: []*ConsulLinkedService{{
|
|
Name: "",
|
|
}},
|
|
}).Validate()
|
|
require.EqualError(t, err, "Consul Linked Service requires Name")
|
|
})
|
|
|
|
t.Run("ok", func(t *testing.T) {
|
|
err := (&ConsulTerminatingConfigEntry{
|
|
Services: []*ConsulLinkedService{{
|
|
Name: "service1",
|
|
}},
|
|
}).Validate()
|
|
require.NoError(t, err)
|
|
})
|
|
}
|
|
|
|
func TestConsulMeshGateway_Copy(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
require.Nil(t, (*ConsulMeshGateway)(nil))
|
|
require.Equal(t, &ConsulMeshGateway{
|
|
Mode: "remote",
|
|
}, &ConsulMeshGateway{
|
|
Mode: "remote",
|
|
})
|
|
}
|
|
|
|
func TestConsulMeshGateway_Equals(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
c := &ConsulMeshGateway{Mode: "local"}
|
|
require.False(t, c.Equals(nil))
|
|
require.True(t, c.Equals(c))
|
|
|
|
o := &ConsulMeshGateway{Mode: "remote"}
|
|
require.False(t, c.Equals(o))
|
|
}
|
|
|
|
func TestConsulMeshGateway_Validate(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("nil", func(t *testing.T) {
|
|
err := (*ConsulMeshGateway)(nil).Validate()
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("mode invalid", func(t *testing.T) {
|
|
err := (&ConsulMeshGateway{Mode: "banana"}).Validate()
|
|
require.EqualError(t, err, `Connect mesh_gateway mode "banana" not supported`)
|
|
})
|
|
|
|
t.Run("ok", func(t *testing.T) {
|
|
err := (&ConsulMeshGateway{Mode: "local"}).Validate()
|
|
require.NoError(t, err)
|
|
})
|
|
}
|