open-nomad/nomad/structs/services_test.go
Seth Hoenig f44a4f68ee consul/connect: trigger update as necessary on connect changes
This PR fixes a long standing bug where submitting jobs with changes
to connect services would not trigger updates as expected. Previously,
service blocks were not considered as sources of destructive updates
since they could be synced with consul non-destructively. With Connect,
task group services that have changes to their connect block or to
the service port should be destructive, since the network plumbing of
the alloc is going to need updating.

Fixes #8596 #7991

Non-destructive half in #7192
2020-10-05 14:53:00 -05:00

975 lines
26 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 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 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("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": &ConsulGatewayBindAddress{Address: "10.0.0.1", Port: 2001},
"listener2": &ConsulGatewayBindAddress{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",
}},
}},
},
}
)
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))
})
}
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 gway = ConsulGateway
type tweaker = func(g *gway)
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 *gway) { 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 *gway) { g.Proxy.EnvoyGatewayBindTaggedAddresses = false })
})
t.Run("mod gateway envoy_gateway_bind_addresses", func(t *testing.T) {
try(t, func(g *gway) {
g.Proxy.EnvoyGatewayBindAddresses = map[string]*ConsulGatewayBindAddress{
"listener3": &ConsulGatewayBindAddress{Address: "9.9.9.9", Port: 9999},
}
})
})
t.Run("mod gateway envoy_gateway_no_default_bind", func(t *testing.T) {
try(t, func(g *gway) { g.Proxy.EnvoyGatewayNoDefaultBind = false })
})
t.Run("mod gateway config", func(t *testing.T) {
try(t, func(g *gway) {
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 *gway) { g.Ingress.TLS = nil })
try(t, func(g *gway) { g.Ingress.TLS.Enabled = false })
})
t.Run("mod ingress listeners count", func(t *testing.T) {
try(t, func(g *gway) { g.Ingress.Listeners = g.Ingress.Listeners[:1] })
})
t.Run("mod ingress listeners port", func(t *testing.T) {
try(t, func(g *gway) { g.Ingress.Listeners[0].Port = 7777 })
})
t.Run("mod ingress listeners protocol", func(t *testing.T) {
try(t, func(g *gway) { g.Ingress.Listeners[0].Protocol = "tcp" })
})
t.Run("mod ingress listeners services count", func(t *testing.T) {
try(t, func(g *gway) { 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 *gway) { g.Ingress.Listeners[0].Services[0].Name = "serviceX" })
})
t.Run("mod ingress listeners services hosts count", func(t *testing.T) {
try(t, func(g *gway) { 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 *gway) { g.Ingress.Listeners[0].Services[0].Hosts[0] = "255.255.255.255" })
})
}
func TestConsulGateway_ingressServicesEqual(t *testing.T) {
igs1 := []*ConsulIngressService{{
Name: "service1",
Hosts: []string{"host1", "host2"},
}, {
Name: "service2",
Hosts: []string{"host3"},
}}
require.False(t, ingressServicesEqual(igs1, nil))
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) {
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.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")
})
}
func TestConsulGatewayBindAddress_Validate(t *testing.T) {
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.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("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.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)
})
}
func TestConsulIngressListener_Validate(t *testing.T) {
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.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)
})
}