agent/structs: validate service definitions, port required for proxy

pull/4275/head
Mitchell Hashimoto 7 years ago
parent 9a62bce03b
commit 965a902474
No known key found for this signature in database
GPG Key ID: 744E147AA52F5B0A

@ -912,6 +912,13 @@ func (b *Builder) Validate(rt RuntimeConfig) error {
return b.err return b.err
} }
// Check for errors in the service definitions
for _, s := range rt.Services {
if err := s.Validate(); err != nil {
return fmt.Errorf("service %q: %s", s.Name, err)
}
}
// ---------------------------------------------------------------- // ----------------------------------------------------------------
// warnings // warnings
// //

@ -1,5 +1,11 @@
package structs package structs
import (
"fmt"
"github.com/hashicorp/go-multierror"
)
// ServiceDefinition is used to JSON decode the Service definitions. For // ServiceDefinition is used to JSON decode the Service definitions. For
// documentation on specific fields see NodeService which is better documented. // documentation on specific fields see NodeService which is better documented.
type ServiceDefinition struct { type ServiceDefinition struct {
@ -68,6 +74,31 @@ func (s *ServiceDefinition) ConnectManagedProxy() (*ConnectManagedProxy, error)
return p, nil return p, nil
} }
// Validate validates the service definition. This also calls the underlying
// Validate method on the NodeService.
//
// NOTE(mitchellh): This currently only validates fields related to Connect
// and is incomplete with regards to other fields.
func (s *ServiceDefinition) Validate() error {
var result error
if s.Kind == ServiceKindTypical {
if s.Connect != nil && s.Connect.Proxy != nil {
if s.Port == 0 {
result = multierror.Append(result, fmt.Errorf(
"Services with a Connect managed proxy must have a port set"))
}
}
}
// Validate the NodeService which covers a lot
if err := s.NodeService().Validate(); err != nil {
result = multierror.Append(result, err)
}
return result
}
func (s *ServiceDefinition) CheckTypes() (checks CheckTypes, err error) { func (s *ServiceDefinition) CheckTypes() (checks CheckTypes, err error) {
if !s.Check.Empty() { if !s.Check.Empty() {
err := s.Check.Validate() err := s.Check.Validate()

@ -2,10 +2,12 @@ package structs
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"time" "time"
"github.com/pascaldekloe/goe/verify" "github.com/pascaldekloe/goe/verify"
"github.com/stretchr/testify/require"
) )
func TestAgentStructs_CheckTypes(t *testing.T) { func TestAgentStructs_CheckTypes(t *testing.T) {
@ -54,3 +56,55 @@ func TestAgentStructs_CheckTypes(t *testing.T) {
} }
} }
} }
func TestServiceDefinitionValidate(t *testing.T) {
cases := []struct {
Name string
Modify func(*ServiceDefinition)
Err string
}{
{
"valid",
func(x *ServiceDefinition) {},
"",
},
{
"managed proxy with a port set",
func(x *ServiceDefinition) {
x.Port = 8080
x.Connect = &ServiceDefinitionConnect{
Proxy: &ServiceDefinitionConnectProxy{},
}
},
"",
},
{
"managed proxy with no port set",
func(x *ServiceDefinition) {
x.Connect = &ServiceDefinitionConnect{
Proxy: &ServiceDefinitionConnectProxy{},
}
},
"must have a port",
},
}
for _, tc := range cases {
t.Run(tc.Name, func(t *testing.T) {
require := require.New(t)
service := TestServiceDefinition(t)
tc.Modify(service)
err := service.Validate()
t.Logf("error: %s", err)
require.Equal(err != nil, tc.Err != "")
if err == nil {
return
}
require.Contains(strings.ToLower(err.Error()), strings.ToLower(tc.Err))
})
}
}

@ -29,6 +29,14 @@ func TestRegisterRequestProxy(t testing.T) *RegisterRequest {
} }
} }
// TestNodeService returns a *NodeService representing a valid regular service.
func TestNodeService(t testing.T) *NodeService {
return &NodeService{
Kind: ServiceKindTypical,
Service: "web",
}
}
// TestNodeServiceProxy returns a *NodeService representing a valid // TestNodeServiceProxy returns a *NodeService representing a valid
// Connect proxy. // Connect proxy.
func TestNodeServiceProxy(t testing.T) *NodeService { func TestNodeServiceProxy(t testing.T) *NodeService {

@ -4,6 +4,13 @@ import (
"github.com/mitchellh/go-testing-interface" "github.com/mitchellh/go-testing-interface"
) )
// TestServiceDefinition returns a ServiceDefinition for a typical service.
func TestServiceDefinition(t testing.T) *ServiceDefinition {
return &ServiceDefinition{
Name: "db",
}
}
// TestServiceDefinitionProxy returns a ServiceDefinition for a proxy. // TestServiceDefinitionProxy returns a ServiceDefinition for a proxy.
func TestServiceDefinitionProxy(t testing.T) *ServiceDefinition { func TestServiceDefinitionProxy(t testing.T) *ServiceDefinition {
return &ServiceDefinition{ return &ServiceDefinition{

Loading…
Cancel
Save