-
Notifications
You must be signed in to change notification settings - Fork 4.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add type validations for the catalog resources
Also adding some common resource validation error types to the internal/resource package.
- Loading branch information
Showing
45 changed files
with
3,192 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package types | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/hashicorp/consul/internal/resource" | ||
pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v1alpha1" | ||
"github.com/hashicorp/consul/proto-public/pbresource" | ||
"github.com/stretchr/testify/require" | ||
"google.golang.org/protobuf/reflect/protoreflect" | ||
"google.golang.org/protobuf/types/known/anypb" | ||
) | ||
|
||
func createDNSPolicyResource(t *testing.T, data protoreflect.ProtoMessage) *pbresource.Resource { | ||
res := &pbresource.Resource{ | ||
Id: &pbresource.ID{ | ||
Type: DNSPolicyType, | ||
Tenancy: &pbresource.Tenancy{ | ||
Partition: "default", | ||
Namespace: "default", | ||
PeerName: "local", | ||
}, | ||
Name: "test-policy", | ||
}, | ||
} | ||
|
||
var err error | ||
res.Data, err = anypb.New(data) | ||
require.NoError(t, err) | ||
return res | ||
} | ||
|
||
func TestValidateDNSPolicy_Ok(t *testing.T) { | ||
data := &pbcatalog.DNSPolicy{ | ||
Workloads: &pbcatalog.WorkloadSelector{ | ||
Prefixes: []string{""}, | ||
}, | ||
Weights: &pbcatalog.Weights{ | ||
Passing: 3, | ||
Warning: 0, | ||
}, | ||
} | ||
|
||
res := createDNSPolicyResource(t, data) | ||
|
||
err := ValidateDNSPolicy(res) | ||
require.NoError(t, err) | ||
} | ||
|
||
func TestValidateDNSPolicy_ParseError(t *testing.T) { | ||
// Any type other than the DNSPolicy type would work | ||
// to cause the error we are expecting | ||
data := &pbcatalog.IP{Address: "198.18.0.1"} | ||
|
||
res := createDNSPolicyResource(t, data) | ||
|
||
err := ValidateDNSPolicy(res) | ||
require.Error(t, err) | ||
require.True(t, errors.As(err, &resource.ErrDataParse{})) | ||
} | ||
|
||
func TestValidateDNSPolicy_MissingWeights(t *testing.T) { | ||
data := &pbcatalog.DNSPolicy{ | ||
Workloads: &pbcatalog.WorkloadSelector{ | ||
Prefixes: []string{""}, | ||
}, | ||
} | ||
|
||
res := createDNSPolicyResource(t, data) | ||
|
||
err := ValidateDNSPolicy(res) | ||
require.Error(t, err) | ||
expected := resource.ErrInvalidField{ | ||
Name: "weights", | ||
Wrapped: resource.ErrMissing, | ||
} | ||
var actual resource.ErrInvalidField | ||
require.True(t, errors.As(err, &actual)) | ||
require.Equal(t, expected, actual) | ||
} | ||
|
||
func TestValidateDNSPolicy_InvalidPassingWeight(t *testing.T) { | ||
for _, weight := range []uint32{0, 1000000} { | ||
t.Run(fmt.Sprintf("%d", weight), func(t *testing.T) { | ||
data := &pbcatalog.DNSPolicy{ | ||
Workloads: &pbcatalog.WorkloadSelector{ | ||
Prefixes: []string{""}, | ||
}, | ||
Weights: &pbcatalog.Weights{ | ||
Passing: weight, | ||
}, | ||
} | ||
|
||
res := createDNSPolicyResource(t, data) | ||
|
||
err := ValidateDNSPolicy(res) | ||
require.Error(t, err) | ||
expected := resource.ErrInvalidField{ | ||
Name: "passing", | ||
Wrapped: errDNSPassingWeightOutOfRange, | ||
} | ||
var actual resource.ErrInvalidField | ||
require.True(t, errors.As(err, &actual)) | ||
require.Equal(t, "weights", actual.Name) | ||
err = actual.Unwrap() | ||
require.True(t, errors.As(err, &actual)) | ||
require.Equal(t, expected, actual) | ||
}) | ||
} | ||
} | ||
|
||
func TestValidateDNSPolicy_InvalidWarningWeight(t *testing.T) { | ||
data := &pbcatalog.DNSPolicy{ | ||
Workloads: &pbcatalog.WorkloadSelector{ | ||
Prefixes: []string{""}, | ||
}, | ||
Weights: &pbcatalog.Weights{ | ||
Passing: 1, | ||
Warning: 1000000, | ||
}, | ||
} | ||
|
||
res := createDNSPolicyResource(t, data) | ||
|
||
err := ValidateDNSPolicy(res) | ||
require.Error(t, err) | ||
expected := resource.ErrInvalidField{ | ||
Name: "warning", | ||
Wrapped: errDNSWarningWeightOutOfRange, | ||
} | ||
var actual resource.ErrInvalidField | ||
require.True(t, errors.As(err, &actual)) | ||
require.Equal(t, "weights", actual.Name) | ||
err = actual.Unwrap() | ||
require.True(t, errors.As(err, &actual)) | ||
require.Equal(t, expected, actual) | ||
} | ||
|
||
func TestValidateDNSPolicy_EmptySelector(t *testing.T) { | ||
data := &pbcatalog.DNSPolicy{ | ||
Weights: &pbcatalog.Weights{ | ||
Passing: 10, | ||
Warning: 3, | ||
}, | ||
} | ||
|
||
res := createDNSPolicyResource(t, data) | ||
|
||
err := ValidateDNSPolicy(res) | ||
require.Error(t, err) | ||
expected := resource.ErrInvalidField{ | ||
Name: "workloads", | ||
Wrapped: resource.ErrEmpty, | ||
} | ||
var actual resource.ErrInvalidField | ||
require.True(t, errors.As(err, &actual)) | ||
require.Equal(t, expected, actual) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package types | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
) | ||
|
||
var ( | ||
errNotDNSLabel = errors.New(fmt.Sprintf("value must match regex: %s", dnsLabelRegex)) | ||
errNotIPAddress = errors.New("value is not a valid IP address") | ||
errUnixSocketMultiport = errors.New("Unix socket address references more than one port") | ||
errInvalidPhysicalPort = errors.New("port number is outside the range 1 to 65535") | ||
errInvalidVirtualPort = errors.New("port number is outside the range 0 to 65535") | ||
errDNSWarningWeightOutOfRange = errors.New("DNS warning weight is outside the range 0 to 65535") | ||
errDNSPassingWeightOutOfRange = errors.New("DNS passing weight is outside of the range 1 to 65535") | ||
errLocalityZoneNoRegion = errors.New("locality region cannot be empty if the zone is set") | ||
errInvalidHealth = errors.New("health status must be one of: passing, warning, critical or maintenance") | ||
) | ||
|
||
type errInvalidWorkloadHostFormat struct { | ||
Host string | ||
} | ||
|
||
func (err errInvalidWorkloadHostFormat) Error() string { | ||
return fmt.Sprintf("%q is not an IP address, Unix socket path or a DNS name.", err.Host) | ||
} | ||
|
||
type errInvalidNodeHostFormat struct { | ||
Host string | ||
} | ||
|
||
func (err errInvalidNodeHostFormat) Error() string { | ||
return fmt.Sprintf("%q is not an IP address or a DNS name.", err.Host) | ||
} | ||
|
||
type errInvalidPortReference struct { | ||
Name string | ||
} | ||
|
||
func (err errInvalidPortReference) Error() string { | ||
return fmt.Sprintf("port with name %q has not been defined", err.Name) | ||
} | ||
|
||
type errVirtualPortReused struct { | ||
Index int | ||
Value uint32 | ||
} | ||
|
||
func (err errVirtualPortReused) Error() string { | ||
return fmt.Sprintf("virtual port %d was previously assigned at index %d", err.Value, err.Index) | ||
} | ||
|
||
type errTooMuchMesh struct { | ||
Ports []string | ||
} | ||
|
||
func (err errTooMuchMesh) Error() string { | ||
return fmt.Sprintf("protocol \"mesh\" was specified in more than 1 port: %+v", err.Ports) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package types | ||
|
||
import ( | ||
"flag" | ||
"os" | ||
"path/filepath" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
// update allows golden files to be updated based on the current output. | ||
var update = flag.Bool("update", false, "update golden files") | ||
|
||
func goldenError(t *testing.T, name string, actual string) { | ||
t.Helper() | ||
|
||
fpath := filepath.Join("testdata", name+".golden") | ||
|
||
if *update { | ||
require.NoError(t, os.WriteFile(fpath, []byte(actual), 0644)) | ||
} else { | ||
expected, err := os.ReadFile(fpath) | ||
require.NoError(t, err) | ||
require.Equal(t, string(expected), actual) | ||
} | ||
} | ||
|
||
func TestErrorStrings(t *testing.T) { | ||
type testCase struct { | ||
err error | ||
expected string | ||
} | ||
|
||
cases := map[string]error{ | ||
"errInvalidWorkloadHostFormat": errInvalidWorkloadHostFormat{ | ||
Host: "-foo-bar-", | ||
}, | ||
"errInvalidNodeHostFormat": errInvalidNodeHostFormat{ | ||
Host: "unix:///node.sock", | ||
}, | ||
"errInvalidPortReference": errInvalidPortReference{ | ||
Name: "http", | ||
}, | ||
"errVirtualPortReused": errVirtualPortReused{ | ||
Index: 3, | ||
Value: 8080, | ||
}, | ||
"errTooMuchMesh": errTooMuchMesh{ | ||
Ports: []string{"http", "grpc"}, | ||
}, | ||
"errNotDNSLabel": errNotDNSLabel, | ||
"errNotIPAddress": errNotIPAddress, | ||
"errUnixSocketMultiport": errUnixSocketMultiport, | ||
"errInvalidPhysicalPort": errInvalidPhysicalPort, | ||
"errInvalidVirtualPort": errInvalidVirtualPort, | ||
"errDNSWarningWeightOutOfRange": errDNSWarningWeightOutOfRange, | ||
"errDNSPassingWeightOutOfRange": errDNSPassingWeightOutOfRange, | ||
"errLocalityZoneNoRegion": errLocalityZoneNoRegion, | ||
} | ||
|
||
for name, err := range cases { | ||
t.Run(name, func(t *testing.T) { | ||
goldenError(t, name, err.Error()) | ||
}) | ||
} | ||
} |
Oops, something went wrong.