mirror of
https://github.com/status-im/consul.git
synced 2025-01-13 15:26:48 +00:00
mesh: ensure that xRoutes have ParentRefs that have matching Tenancy to the enclosing resource (#19176)
We don't want an xRoute controlling traffic for a Service in another tenancy.
This commit is contained in:
parent
5fbf0c00d3
commit
f0e4897736
@ -61,19 +61,19 @@ func TestMutateUpstreams(t *testing.T) {
|
||||
},
|
||||
},
|
||||
"dest ref tenancy defaulting": {
|
||||
tenancy: newTestTenancy("foo.bar"),
|
||||
tenancy: resourcetest.Tenancy("foo.bar"),
|
||||
data: &pbmesh.Destinations{
|
||||
Destinations: []*pbmesh.Destination{
|
||||
{DestinationRef: newRefWithTenancy(pbcatalog.ServiceType, newTestTenancy(""), "api")},
|
||||
{DestinationRef: newRefWithTenancy(pbcatalog.ServiceType, newTestTenancy(".zim"), "api")},
|
||||
{DestinationRef: newRefWithTenancy(pbcatalog.ServiceType, newTestTenancy("gir.zim"), "api")},
|
||||
{DestinationRef: newRefWithTenancy(pbcatalog.ServiceType, "", "api")},
|
||||
{DestinationRef: newRefWithTenancy(pbcatalog.ServiceType, ".zim", "api")},
|
||||
{DestinationRef: newRefWithTenancy(pbcatalog.ServiceType, "gir.zim", "api")},
|
||||
},
|
||||
},
|
||||
expect: &pbmesh.Destinations{
|
||||
Destinations: []*pbmesh.Destination{
|
||||
{DestinationRef: newRefWithTenancy(pbcatalog.ServiceType, newTestTenancy("foo.bar"), "api")},
|
||||
{DestinationRef: newRefWithTenancy(pbcatalog.ServiceType, newTestTenancy("foo.zim"), "api")},
|
||||
{DestinationRef: newRefWithTenancy(pbcatalog.ServiceType, newTestTenancy("gir.zim"), "api")},
|
||||
{DestinationRef: newRefWithTenancy(pbcatalog.ServiceType, "foo.bar", "api")},
|
||||
{DestinationRef: newRefWithTenancy(pbcatalog.ServiceType, "foo.zim", "api")},
|
||||
{DestinationRef: newRefWithTenancy(pbcatalog.ServiceType, "gir.zim", "api")},
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -138,7 +138,7 @@ func TestValidateUpstreams(t *testing.T) {
|
||||
skipMutate: true,
|
||||
data: &pbmesh.Destinations{
|
||||
Destinations: []*pbmesh.Destination{
|
||||
{DestinationRef: newRefWithTenancy(pbcatalog.WorkloadType, nil, "api")},
|
||||
{DestinationRef: newRefWithTenancy(pbcatalog.WorkloadType, "default.default", "api")},
|
||||
},
|
||||
},
|
||||
expectErr: `invalid element at index 0 of list "upstreams": invalid "destination_ref" field: invalid "type" field: reference must have type catalog.v2beta1.Service`,
|
||||
@ -156,7 +156,7 @@ func TestValidateUpstreams(t *testing.T) {
|
||||
skipMutate: true,
|
||||
data: &pbmesh.Destinations{
|
||||
Destinations: []*pbmesh.Destination{
|
||||
{DestinationRef: newRefWithTenancy(pbcatalog.ServiceType, newTestTenancy(".bar"), "api")},
|
||||
{DestinationRef: newRefWithTenancy(pbcatalog.ServiceType, ".bar", "api")},
|
||||
},
|
||||
},
|
||||
expectErr: `invalid element at index 0 of list "upstreams": invalid "destination_ref" field: invalid "tenancy" field: invalid "partition" field: cannot be empty`,
|
||||
@ -165,7 +165,7 @@ func TestValidateUpstreams(t *testing.T) {
|
||||
skipMutate: true,
|
||||
data: &pbmesh.Destinations{
|
||||
Destinations: []*pbmesh.Destination{
|
||||
{DestinationRef: newRefWithTenancy(pbcatalog.ServiceType, newTestTenancy("foo"), "api")},
|
||||
{DestinationRef: newRefWithTenancy(pbcatalog.ServiceType, "foo", "api")},
|
||||
},
|
||||
},
|
||||
expectErr: `invalid element at index 0 of list "upstreams": invalid "destination_ref" field: invalid "tenancy" field: invalid "namespace" field: cannot be empty`,
|
||||
@ -174,7 +174,9 @@ func TestValidateUpstreams(t *testing.T) {
|
||||
skipMutate: true,
|
||||
data: &pbmesh.Destinations{
|
||||
Destinations: []*pbmesh.Destination{
|
||||
{DestinationRef: newRefWithTenancy(pbcatalog.ServiceType, &pbresource.Tenancy{Partition: "foo", Namespace: "bar"}, "api")},
|
||||
{DestinationRef: resourcetest.Resource(pbcatalog.ServiceType, "api").
|
||||
WithTenancy(&pbresource.Tenancy{Partition: "foo", Namespace: "bar"}).
|
||||
Reference("")},
|
||||
},
|
||||
},
|
||||
expectErr: `invalid element at index 0 of list "upstreams": invalid "destination_ref" field: invalid "tenancy" field: invalid "peer_name" field: must be set to "local"`,
|
||||
@ -182,9 +184,9 @@ func TestValidateUpstreams(t *testing.T) {
|
||||
"normal": {
|
||||
data: &pbmesh.Destinations{
|
||||
Destinations: []*pbmesh.Destination{
|
||||
{DestinationRef: newRefWithTenancy(pbcatalog.ServiceType, newTestTenancy("foo.bar"), "api")},
|
||||
{DestinationRef: newRefWithTenancy(pbcatalog.ServiceType, newTestTenancy("foo.zim"), "api")},
|
||||
{DestinationRef: newRefWithTenancy(pbcatalog.ServiceType, newTestTenancy("gir.zim"), "api")},
|
||||
{DestinationRef: newRefWithTenancy(pbcatalog.ServiceType, "foo.bar", "api")},
|
||||
{DestinationRef: newRefWithTenancy(pbcatalog.ServiceType, "foo.zim", "api")},
|
||||
{DestinationRef: newRefWithTenancy(pbcatalog.ServiceType, "gir.zim", "api")},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -64,7 +64,7 @@ func ValidateGRPCRoute(res *pbresource.Resource) error {
|
||||
}
|
||||
|
||||
var merr error
|
||||
if err := validateParentRefs(route.ParentRefs); err != nil {
|
||||
if err := validateParentRefs(res.Id, route.ParentRefs); err != nil {
|
||||
merr = multierror.Append(merr, err)
|
||||
}
|
||||
|
||||
|
@ -81,6 +81,7 @@ func TestMutateGRPCRoute(t *testing.T) {
|
||||
WithTenancy(tc.routeTenancy).
|
||||
WithData(t, tc.route).
|
||||
Build()
|
||||
resource.DefaultIDTenancy(res.Id, nil, resource.DefaultNamespacedTenancy())
|
||||
|
||||
err := MutateGRPCRoute(res)
|
||||
require.NoError(t, err)
|
||||
@ -103,14 +104,17 @@ func TestMutateGRPCRoute(t *testing.T) {
|
||||
|
||||
func TestValidateGRPCRoute(t *testing.T) {
|
||||
type testcase struct {
|
||||
route *pbmesh.GRPCRoute
|
||||
expectErr string
|
||||
routeTenancy *pbresource.Tenancy
|
||||
route *pbmesh.GRPCRoute
|
||||
expectErr string
|
||||
}
|
||||
|
||||
run := func(t *testing.T, tc testcase) {
|
||||
res := resourcetest.Resource(pbmesh.GRPCRouteType, "api").
|
||||
WithTenancy(tc.routeTenancy).
|
||||
WithData(t, tc.route).
|
||||
Build()
|
||||
resource.DefaultIDTenancy(res.Id, nil, resource.DefaultNamespacedTenancy())
|
||||
|
||||
// Ensure things are properly mutated and updated in the inputs.
|
||||
err := MutateGRPCRoute(res)
|
||||
@ -582,6 +586,7 @@ func TestValidateGRPCRoute(t *testing.T) {
|
||||
// Add common parent refs test cases.
|
||||
for name, parentTC := range getXRouteParentRefTestCases() {
|
||||
cases["parent-ref: "+name] = testcase{
|
||||
routeTenancy: parentTC.routeTenancy,
|
||||
route: &pbmesh.GRPCRoute{
|
||||
ParentRefs: parentTC.refs,
|
||||
},
|
||||
@ -597,6 +602,7 @@ func TestValidateGRPCRoute(t *testing.T) {
|
||||
})
|
||||
}
|
||||
cases["backend-ref: "+name] = testcase{
|
||||
routeTenancy: backendTC.routeTenancy,
|
||||
route: &pbmesh.GRPCRoute{
|
||||
ParentRefs: []*pbmesh.ParentReference{
|
||||
newParentRef(pbcatalog.ServiceType, "web", ""),
|
||||
|
@ -75,7 +75,7 @@ func ValidateHTTPRoute(res *pbresource.Resource) error {
|
||||
}
|
||||
|
||||
var merr error
|
||||
if err := validateParentRefs(route.ParentRefs); err != nil {
|
||||
if err := validateParentRefs(res.Id, route.ParentRefs); err != nil {
|
||||
merr = multierror.Append(merr, err)
|
||||
}
|
||||
|
||||
|
@ -4,14 +4,10 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/durationpb"
|
||||
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||
|
||||
"github.com/hashicorp/consul/internal/resource"
|
||||
"github.com/hashicorp/consul/internal/resource/resourcetest"
|
||||
@ -35,6 +31,7 @@ func TestMutateHTTPRoute(t *testing.T) {
|
||||
WithTenancy(tc.routeTenancy).
|
||||
WithData(t, tc.route).
|
||||
Build()
|
||||
resource.DefaultIDTenancy(res.Id, nil, resource.DefaultNamespacedTenancy())
|
||||
|
||||
err := MutateHTTPRoute(res)
|
||||
|
||||
@ -200,14 +197,17 @@ func TestMutateHTTPRoute(t *testing.T) {
|
||||
|
||||
func TestValidateHTTPRoute(t *testing.T) {
|
||||
type testcase struct {
|
||||
route *pbmesh.HTTPRoute
|
||||
expectErr string
|
||||
routeTenancy *pbresource.Tenancy
|
||||
route *pbmesh.HTTPRoute
|
||||
expectErr string
|
||||
}
|
||||
|
||||
run := func(t *testing.T, tc testcase) {
|
||||
res := resourcetest.Resource(pbmesh.HTTPRouteType, "api").
|
||||
WithTenancy(tc.routeTenancy).
|
||||
WithData(t, tc.route).
|
||||
Build()
|
||||
resource.DefaultIDTenancy(res.Id, nil, resource.DefaultNamespacedTenancy())
|
||||
|
||||
// Ensure things are properly mutated and updated in the inputs.
|
||||
err := MutateHTTPRoute(res)
|
||||
@ -844,6 +844,7 @@ func TestValidateHTTPRoute(t *testing.T) {
|
||||
// Add common parent refs test cases.
|
||||
for name, parentTC := range getXRouteParentRefTestCases() {
|
||||
cases["parent-ref: "+name] = testcase{
|
||||
routeTenancy: parentTC.routeTenancy,
|
||||
route: &pbmesh.HTTPRoute{
|
||||
ParentRefs: parentTC.refs,
|
||||
},
|
||||
@ -859,6 +860,7 @@ func TestValidateHTTPRoute(t *testing.T) {
|
||||
})
|
||||
}
|
||||
cases["backend-ref: "+name] = testcase{
|
||||
routeTenancy: backendTC.routeTenancy,
|
||||
route: &pbmesh.HTTPRoute{
|
||||
ParentRefs: []*pbmesh.ParentReference{
|
||||
newParentRef(pbcatalog.ServiceType, "web", ""),
|
||||
@ -878,319 +880,6 @@ func TestValidateHTTPRoute(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type xRouteParentRefMutateTestcase struct {
|
||||
routeTenancy *pbresource.Tenancy
|
||||
refs []*pbmesh.ParentReference
|
||||
expect []*pbmesh.ParentReference
|
||||
}
|
||||
|
||||
func getXRouteParentRefMutateTestCases() map[string]xRouteParentRefMutateTestcase {
|
||||
newRef := func(typ *pbresource.Type, tenancyStr, name string) *pbresource.Reference {
|
||||
return resourcetest.Resource(typ, name).
|
||||
WithTenancy(newTestTenancy(tenancyStr)).
|
||||
Reference("")
|
||||
}
|
||||
|
||||
newParentRef := func(typ *pbresource.Type, tenancyStr, name, port string) *pbmesh.ParentReference {
|
||||
return &pbmesh.ParentReference{
|
||||
Ref: newRef(typ, tenancyStr, name),
|
||||
Port: port,
|
||||
}
|
||||
}
|
||||
|
||||
return map[string]xRouteParentRefMutateTestcase{
|
||||
"parent ref tenancies defaulted": {
|
||||
routeTenancy: newTestTenancy("foo.bar"),
|
||||
refs: []*pbmesh.ParentReference{
|
||||
newParentRef(pbcatalog.ServiceType, "", "api", ""),
|
||||
newParentRef(pbcatalog.ServiceType, ".zim", "api", ""),
|
||||
newParentRef(pbcatalog.ServiceType, "gir.zim", "api", ""),
|
||||
},
|
||||
expect: []*pbmesh.ParentReference{
|
||||
newParentRef(pbcatalog.ServiceType, "foo.bar", "api", ""),
|
||||
newParentRef(pbcatalog.ServiceType, "foo.zim", "api", ""),
|
||||
newParentRef(pbcatalog.ServiceType, "gir.zim", "api", ""),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type xRouteBackendRefMutateTestcase struct {
|
||||
routeTenancy *pbresource.Tenancy
|
||||
refs []*pbmesh.BackendReference
|
||||
expect []*pbmesh.BackendReference
|
||||
}
|
||||
|
||||
func getXRouteBackendRefMutateTestCases() map[string]xRouteBackendRefMutateTestcase {
|
||||
newRef := func(typ *pbresource.Type, tenancyStr, name string) *pbresource.Reference {
|
||||
return resourcetest.Resource(typ, name).
|
||||
WithTenancy(newTestTenancy(tenancyStr)).
|
||||
Reference("")
|
||||
}
|
||||
|
||||
newBackendRef := func(typ *pbresource.Type, tenancyStr, name, port string) *pbmesh.BackendReference {
|
||||
return &pbmesh.BackendReference{
|
||||
Ref: newRef(typ, tenancyStr, name),
|
||||
Port: port,
|
||||
}
|
||||
}
|
||||
|
||||
return map[string]xRouteBackendRefMutateTestcase{
|
||||
"backend ref tenancies defaulted": {
|
||||
routeTenancy: newTestTenancy("foo.bar"),
|
||||
refs: []*pbmesh.BackendReference{
|
||||
newBackendRef(pbcatalog.ServiceType, "", "api", ""),
|
||||
newBackendRef(pbcatalog.ServiceType, ".zim", "api", ""),
|
||||
newBackendRef(pbcatalog.ServiceType, "gir.zim", "api", ""),
|
||||
},
|
||||
expect: []*pbmesh.BackendReference{
|
||||
newBackendRef(pbcatalog.ServiceType, "foo.bar", "api", ""),
|
||||
newBackendRef(pbcatalog.ServiceType, "foo.zim", "api", ""),
|
||||
newBackendRef(pbcatalog.ServiceType, "gir.zim", "api", ""),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type xRouteParentRefTestcase struct {
|
||||
refs []*pbmesh.ParentReference
|
||||
expectErr string
|
||||
}
|
||||
|
||||
func getXRouteParentRefTestCases() map[string]xRouteParentRefTestcase {
|
||||
return map[string]xRouteParentRefTestcase{
|
||||
"no parent refs": {
|
||||
expectErr: `invalid "parent_refs" field: cannot be empty`,
|
||||
},
|
||||
"parent ref with nil ref": {
|
||||
refs: []*pbmesh.ParentReference{
|
||||
newParentRef(pbcatalog.ServiceType, "api", ""),
|
||||
{
|
||||
Ref: nil,
|
||||
Port: "http",
|
||||
},
|
||||
},
|
||||
expectErr: `invalid element at index 1 of list "parent_refs": invalid "ref" field: missing required field`,
|
||||
},
|
||||
"parent ref with bad type ref": {
|
||||
refs: []*pbmesh.ParentReference{
|
||||
newParentRef(pbcatalog.ServiceType, "api", ""),
|
||||
newParentRef(pbcatalog.WorkloadType, "api", ""),
|
||||
},
|
||||
expectErr: `invalid element at index 1 of list "parent_refs": invalid "ref" field: invalid "type" field: reference must have type catalog.v2beta1.Service`,
|
||||
},
|
||||
"parent ref with section": {
|
||||
refs: []*pbmesh.ParentReference{
|
||||
newParentRef(pbcatalog.ServiceType, "api", ""),
|
||||
{
|
||||
Ref: resourcetest.Resource(pbcatalog.ServiceType, "web").Reference("section2"),
|
||||
Port: "http",
|
||||
},
|
||||
},
|
||||
expectErr: `invalid element at index 1 of list "parent_refs": invalid "ref" field: invalid "section" field: section cannot be set here`,
|
||||
},
|
||||
"duplicate exact parents": {
|
||||
refs: []*pbmesh.ParentReference{
|
||||
newParentRef(pbcatalog.ServiceType, "api", "http"),
|
||||
newParentRef(pbcatalog.ServiceType, "api", "http"),
|
||||
},
|
||||
expectErr: `invalid element at index 1 of list "parent_refs": invalid "port" field: parent ref "catalog.v2beta1.Service/default.local.default/api" for port "http" exists twice`,
|
||||
},
|
||||
"duplicate wild parents": {
|
||||
refs: []*pbmesh.ParentReference{
|
||||
newParentRef(pbcatalog.ServiceType, "api", ""),
|
||||
newParentRef(pbcatalog.ServiceType, "api", ""),
|
||||
},
|
||||
expectErr: `invalid element at index 1 of list "parent_refs": invalid "port" field: parent ref "catalog.v2beta1.Service/default.local.default/api" for wildcard port exists twice`,
|
||||
},
|
||||
"duplicate parents via exact+wild overlap": {
|
||||
refs: []*pbmesh.ParentReference{
|
||||
newParentRef(pbcatalog.ServiceType, "api", "http"),
|
||||
newParentRef(pbcatalog.ServiceType, "api", ""),
|
||||
},
|
||||
expectErr: `invalid element at index 1 of list "parent_refs": invalid "port" field: parent ref "catalog.v2beta1.Service/default.local.default/api" for ports [http] covered by wildcard port already`,
|
||||
},
|
||||
"duplicate parents via exact+wild overlap (reversed)": {
|
||||
refs: []*pbmesh.ParentReference{
|
||||
newParentRef(pbcatalog.ServiceType, "api", ""),
|
||||
newParentRef(pbcatalog.ServiceType, "api", "http"),
|
||||
},
|
||||
expectErr: `invalid element at index 1 of list "parent_refs": invalid "port" field: parent ref "catalog.v2beta1.Service/default.local.default/api" for port "http" covered by wildcard port already`,
|
||||
},
|
||||
"good single parent ref": {
|
||||
refs: []*pbmesh.ParentReference{
|
||||
newParentRef(pbcatalog.ServiceType, "api", "http"),
|
||||
},
|
||||
},
|
||||
"good muliple parent refs": {
|
||||
refs: []*pbmesh.ParentReference{
|
||||
newParentRef(pbcatalog.ServiceType, "api", "http"),
|
||||
newParentRef(pbcatalog.ServiceType, "web", ""),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type xRouteBackendRefTestcase struct {
|
||||
refs []*pbmesh.BackendReference
|
||||
expectErr string
|
||||
}
|
||||
|
||||
func getXRouteBackendRefTestCases() map[string]xRouteBackendRefTestcase {
|
||||
return map[string]xRouteBackendRefTestcase{
|
||||
"no backend refs": {
|
||||
expectErr: `invalid "backend_refs" field: cannot be empty`,
|
||||
},
|
||||
"backend ref with nil ref": {
|
||||
refs: []*pbmesh.BackendReference{
|
||||
newBackendRef(pbcatalog.ServiceType, "api", ""),
|
||||
{
|
||||
Ref: nil,
|
||||
Port: "http",
|
||||
},
|
||||
},
|
||||
expectErr: `invalid element at index 0 of list "rules": invalid element at index 1 of list "backend_refs": invalid "backend_ref" field: invalid "ref" field: missing required field`,
|
||||
},
|
||||
"backend ref with bad type ref": {
|
||||
refs: []*pbmesh.BackendReference{
|
||||
newBackendRef(pbcatalog.ServiceType, "api", ""),
|
||||
newBackendRef(pbcatalog.WorkloadType, "api", ""),
|
||||
},
|
||||
expectErr: `invalid element at index 0 of list "rules": invalid element at index 1 of list "backend_refs": invalid "backend_ref" field: invalid "ref" field: invalid "type" field: reference must have type catalog.v2beta1.Service`,
|
||||
},
|
||||
"backend ref with section": {
|
||||
refs: []*pbmesh.BackendReference{
|
||||
newBackendRef(pbcatalog.ServiceType, "api", ""),
|
||||
{
|
||||
Ref: resourcetest.Resource(pbcatalog.ServiceType, "web").Reference("section2"),
|
||||
Port: "http",
|
||||
},
|
||||
},
|
||||
expectErr: `invalid element at index 0 of list "rules": invalid element at index 1 of list "backend_refs": invalid "backend_ref" field: invalid "ref" field: invalid "section" field: section cannot be set here`,
|
||||
},
|
||||
"backend ref with datacenter": {
|
||||
refs: []*pbmesh.BackendReference{
|
||||
newBackendRef(pbcatalog.ServiceType, "api", ""),
|
||||
{
|
||||
Ref: newRef(pbcatalog.ServiceType, "db"),
|
||||
Port: "http",
|
||||
Datacenter: "dc2",
|
||||
},
|
||||
},
|
||||
expectErr: `invalid element at index 0 of list "rules": invalid element at index 1 of list "backend_refs": invalid "backend_ref" field: invalid "datacenter" field: datacenter is not yet supported on backend refs`,
|
||||
},
|
||||
"good backend ref": {
|
||||
refs: []*pbmesh.BackendReference{
|
||||
newBackendRef(pbcatalog.ServiceType, "api", ""),
|
||||
{
|
||||
Ref: newRef(pbcatalog.ServiceType, "db"),
|
||||
Port: "http",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type xRouteTimeoutsTestcase struct {
|
||||
timeouts *pbmesh.HTTPRouteTimeouts
|
||||
expectErr string
|
||||
}
|
||||
|
||||
func getXRouteTimeoutsTestCases() map[string]xRouteTimeoutsTestcase {
|
||||
return map[string]xRouteTimeoutsTestcase{
|
||||
"bad request": {
|
||||
timeouts: &pbmesh.HTTPRouteTimeouts{
|
||||
Request: durationpb.New(-1 * time.Second),
|
||||
},
|
||||
expectErr: `invalid element at index 0 of list "rules": invalid "timeouts" field: invalid "request" field: timeout cannot be negative: -1s`,
|
||||
},
|
||||
"bad idle": {
|
||||
timeouts: &pbmesh.HTTPRouteTimeouts{
|
||||
Idle: durationpb.New(-1 * time.Second),
|
||||
},
|
||||
expectErr: `invalid element at index 0 of list "rules": invalid "timeouts" field: invalid "idle" field: timeout cannot be negative: -1s`,
|
||||
},
|
||||
"good all": {
|
||||
timeouts: &pbmesh.HTTPRouteTimeouts{
|
||||
Request: durationpb.New(1 * time.Second),
|
||||
Idle: durationpb.New(3 * time.Second),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type xRouteRetriesTestcase struct {
|
||||
retries *pbmesh.HTTPRouteRetries
|
||||
expectErr string
|
||||
}
|
||||
|
||||
func getXRouteRetriesTestCases() map[string]xRouteRetriesTestcase {
|
||||
return map[string]xRouteRetriesTestcase{
|
||||
"bad conditions": {
|
||||
retries: &pbmesh.HTTPRouteRetries{
|
||||
OnConditions: []string{"garbage"},
|
||||
},
|
||||
expectErr: `invalid element at index 0 of list "rules": invalid "retries" field: invalid element at index 0 of list "on_conditions": not a valid retry condition: "garbage"`,
|
||||
},
|
||||
"good all": {
|
||||
retries: &pbmesh.HTTPRouteRetries{
|
||||
Number: wrapperspb.UInt32(5),
|
||||
OnConditions: []string{"internal"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newRef(typ *pbresource.Type, name string) *pbresource.Reference {
|
||||
return newRefWithTenancy(typ, nil, name)
|
||||
}
|
||||
|
||||
func newRefWithTenancy(typ *pbresource.Type, tenancy *pbresource.Tenancy, name string) *pbresource.Reference {
|
||||
if tenancy == nil {
|
||||
tenancy = resource.DefaultNamespacedTenancy()
|
||||
}
|
||||
return resourcetest.Resource(typ, name).
|
||||
WithTenancy(tenancy).
|
||||
Reference("")
|
||||
}
|
||||
|
||||
func newBackendRef(typ *pbresource.Type, name, port string) *pbmesh.BackendReference {
|
||||
return &pbmesh.BackendReference{
|
||||
Ref: newRef(typ, name),
|
||||
Port: port,
|
||||
}
|
||||
}
|
||||
|
||||
func newParentRef(typ *pbresource.Type, name, port string) *pbmesh.ParentReference {
|
||||
return newParentRefWithTenancy(typ, nil, name, port)
|
||||
}
|
||||
|
||||
func newParentRefWithTenancy(typ *pbresource.Type, tenancy *pbresource.Tenancy, name, port string) *pbmesh.ParentReference {
|
||||
return &pbmesh.ParentReference{
|
||||
Ref: newRefWithTenancy(typ, tenancy, name),
|
||||
Port: port,
|
||||
}
|
||||
}
|
||||
|
||||
func newTestTenancy(s string) *pbresource.Tenancy {
|
||||
parts := strings.Split(s, ".")
|
||||
switch len(parts) {
|
||||
case 0:
|
||||
return resource.DefaultClusteredTenancy()
|
||||
case 1:
|
||||
v := resource.DefaultPartitionedTenancy()
|
||||
v.Partition = parts[0]
|
||||
return v
|
||||
case 2:
|
||||
v := resource.DefaultNamespacedTenancy()
|
||||
v.Partition = parts[0]
|
||||
v.Namespace = parts[1]
|
||||
return v
|
||||
default:
|
||||
return &pbresource.Tenancy{Partition: "BAD", Namespace: "BAD", PeerName: "BAD"}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPRouteACLs(t *testing.T) {
|
||||
testXRouteACLs[*pbmesh.HTTPRoute](t, func(t *testing.T, parentRefs, backendRefs []*pbresource.Reference) *pbresource.Resource {
|
||||
data := &pbmesh.HTTPRoute{
|
||||
|
@ -64,7 +64,7 @@ func ValidateTCPRoute(res *pbresource.Resource) error {
|
||||
|
||||
var merr error
|
||||
|
||||
if err := validateParentRefs(route.ParentRefs); err != nil {
|
||||
if err := validateParentRefs(res.Id, route.ParentRefs); err != nil {
|
||||
merr = multierror.Append(merr, err)
|
||||
}
|
||||
|
||||
|
@ -81,6 +81,7 @@ func TestMutateTCPRoute(t *testing.T) {
|
||||
WithTenancy(tc.routeTenancy).
|
||||
WithData(t, tc.route).
|
||||
Build()
|
||||
resource.DefaultIDTenancy(res.Id, nil, resource.DefaultNamespacedTenancy())
|
||||
|
||||
err := MutateTCPRoute(res)
|
||||
require.NoError(t, err)
|
||||
@ -103,15 +104,17 @@ func TestMutateTCPRoute(t *testing.T) {
|
||||
|
||||
func TestValidateTCPRoute(t *testing.T) {
|
||||
type testcase struct {
|
||||
route *pbmesh.TCPRoute
|
||||
expectErr string
|
||||
routeTenancy *pbresource.Tenancy
|
||||
route *pbmesh.TCPRoute
|
||||
expectErr string
|
||||
}
|
||||
|
||||
run := func(t *testing.T, tc testcase) {
|
||||
res := resourcetest.Resource(pbmesh.TCPRouteType, "api").
|
||||
WithTenancy(resource.DefaultNamespacedTenancy()).
|
||||
WithTenancy(tc.routeTenancy).
|
||||
WithData(t, tc.route).
|
||||
Build()
|
||||
resource.DefaultIDTenancy(res.Id, nil, resource.DefaultNamespacedTenancy())
|
||||
|
||||
// Ensure things are properly mutated and updated in the inputs.
|
||||
err := MutateTCPRoute(res)
|
||||
@ -167,6 +170,7 @@ func TestValidateTCPRoute(t *testing.T) {
|
||||
// Add common parent refs test cases.
|
||||
for name, parentTC := range getXRouteParentRefTestCases() {
|
||||
cases["parent-ref: "+name] = testcase{
|
||||
routeTenancy: parentTC.routeTenancy,
|
||||
route: &pbmesh.TCPRoute{
|
||||
ParentRefs: parentTC.refs,
|
||||
},
|
||||
@ -182,6 +186,7 @@ func TestValidateTCPRoute(t *testing.T) {
|
||||
})
|
||||
}
|
||||
cases["backend-ref: "+name] = testcase{
|
||||
routeTenancy: backendTC.routeTenancy,
|
||||
route: &pbmesh.TCPRoute{
|
||||
ParentRefs: []*pbmesh.ParentReference{
|
||||
newParentRef(pbcatalog.ServiceType, "web", ""),
|
||||
|
@ -60,7 +60,7 @@ func mutateXRouteRef(xrouteTenancy *pbresource.Tenancy, ref *pbresource.Referenc
|
||||
return !proto.Equal(orig, ref)
|
||||
}
|
||||
|
||||
func validateParentRefs(parentRefs []*pbmesh.ParentReference) error {
|
||||
func validateParentRefs(id *pbresource.ID, parentRefs []*pbmesh.ParentReference) error {
|
||||
var merr error
|
||||
if len(parentRefs) == 0 {
|
||||
merr = multierror.Append(merr, resource.ErrInvalidField{
|
||||
@ -92,6 +92,12 @@ func validateParentRefs(parentRefs []*pbmesh.ParentReference) error {
|
||||
if err := catalog.ValidateLocalServiceRefNoSection(parent.Ref, wrapRefErr); err != nil {
|
||||
merr = multierror.Append(merr, err)
|
||||
} else {
|
||||
if !resource.EqualTenancy(id.Tenancy, parent.Ref.Tenancy) {
|
||||
merr = multierror.Append(merr, wrapRefErr(resource.ErrInvalidField{
|
||||
Name: "tenancy",
|
||||
Wrapped: resource.ErrReferenceTenancyNotEqual,
|
||||
}))
|
||||
}
|
||||
prk := portedRefKey{
|
||||
Key: resource.NewReferenceKey(parent.Ref),
|
||||
Port: parent.Port,
|
||||
|
@ -6,17 +6,365 @@ package types
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/types/known/durationpb"
|
||||
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||
|
||||
"github.com/hashicorp/consul/acl"
|
||||
"github.com/hashicorp/consul/agent/structs"
|
||||
"github.com/hashicorp/consul/internal/resource"
|
||||
"github.com/hashicorp/consul/internal/resource/resourcetest"
|
||||
pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v2beta1"
|
||||
pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v2beta1"
|
||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||
)
|
||||
|
||||
type xRouteParentRefMutateTestcase struct {
|
||||
routeTenancy *pbresource.Tenancy
|
||||
refs []*pbmesh.ParentReference
|
||||
expect []*pbmesh.ParentReference
|
||||
}
|
||||
|
||||
func getXRouteParentRefMutateTestCases() map[string]xRouteParentRefMutateTestcase {
|
||||
newRef := func(typ *pbresource.Type, tenancyStr, name string) *pbresource.Reference {
|
||||
return resourcetest.Resource(typ, name).
|
||||
WithTenancy(resourcetest.Tenancy(tenancyStr)).
|
||||
Reference("")
|
||||
}
|
||||
|
||||
newParentRef := func(typ *pbresource.Type, tenancyStr, name, port string) *pbmesh.ParentReference {
|
||||
return &pbmesh.ParentReference{
|
||||
Ref: newRef(typ, tenancyStr, name),
|
||||
Port: port,
|
||||
}
|
||||
}
|
||||
|
||||
return map[string]xRouteParentRefMutateTestcase{
|
||||
"parent ref tenancies defaulted": {
|
||||
routeTenancy: resourcetest.Tenancy("foo.bar"),
|
||||
refs: []*pbmesh.ParentReference{
|
||||
newParentRef(pbcatalog.ServiceType, "", "api", ""),
|
||||
newParentRef(pbcatalog.ServiceType, ".zim", "api", ""),
|
||||
newParentRef(pbcatalog.ServiceType, "gir.zim", "api", ""),
|
||||
},
|
||||
expect: []*pbmesh.ParentReference{
|
||||
newParentRef(pbcatalog.ServiceType, "foo.bar", "api", ""),
|
||||
newParentRef(pbcatalog.ServiceType, "foo.zim", "api", ""),
|
||||
newParentRef(pbcatalog.ServiceType, "gir.zim", "api", ""),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type xRouteBackendRefMutateTestcase struct {
|
||||
routeTenancy *pbresource.Tenancy
|
||||
refs []*pbmesh.BackendReference
|
||||
expect []*pbmesh.BackendReference
|
||||
}
|
||||
|
||||
func getXRouteBackendRefMutateTestCases() map[string]xRouteBackendRefMutateTestcase {
|
||||
newRef := func(typ *pbresource.Type, tenancyStr, name string) *pbresource.Reference {
|
||||
return resourcetest.Resource(typ, name).
|
||||
WithTenancy(resourcetest.Tenancy(tenancyStr)).
|
||||
Reference("")
|
||||
}
|
||||
|
||||
newBackendRef := func(typ *pbresource.Type, tenancyStr, name, port string) *pbmesh.BackendReference {
|
||||
return &pbmesh.BackendReference{
|
||||
Ref: newRef(typ, tenancyStr, name),
|
||||
Port: port,
|
||||
}
|
||||
}
|
||||
|
||||
return map[string]xRouteBackendRefMutateTestcase{
|
||||
"backend ref tenancies defaulted": {
|
||||
routeTenancy: resourcetest.Tenancy("foo.bar"),
|
||||
refs: []*pbmesh.BackendReference{
|
||||
newBackendRef(pbcatalog.ServiceType, "", "api", ""),
|
||||
newBackendRef(pbcatalog.ServiceType, ".zim", "api", ""),
|
||||
newBackendRef(pbcatalog.ServiceType, "gir.zim", "api", ""),
|
||||
},
|
||||
expect: []*pbmesh.BackendReference{
|
||||
newBackendRef(pbcatalog.ServiceType, "foo.bar", "api", ""),
|
||||
newBackendRef(pbcatalog.ServiceType, "foo.zim", "api", ""),
|
||||
newBackendRef(pbcatalog.ServiceType, "gir.zim", "api", ""),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type xRouteParentRefTestcase struct {
|
||||
routeTenancy *pbresource.Tenancy
|
||||
refs []*pbmesh.ParentReference
|
||||
expectErr string
|
||||
}
|
||||
|
||||
func getXRouteParentRefTestCases() map[string]xRouteParentRefTestcase {
|
||||
newRef := func(typ *pbresource.Type, tenancyStr, name string) *pbresource.Reference {
|
||||
return resourcetest.Resource(typ, name).
|
||||
WithTenancy(resourcetest.Tenancy(tenancyStr)).
|
||||
Reference("")
|
||||
}
|
||||
|
||||
newParentRef := func(typ *pbresource.Type, tenancyStr, name, port string) *pbmesh.ParentReference {
|
||||
return &pbmesh.ParentReference{
|
||||
Ref: newRef(typ, tenancyStr, name),
|
||||
Port: port,
|
||||
}
|
||||
}
|
||||
return map[string]xRouteParentRefTestcase{
|
||||
"no parent refs": {
|
||||
routeTenancy: resource.DefaultNamespacedTenancy(),
|
||||
expectErr: `invalid "parent_refs" field: cannot be empty`,
|
||||
},
|
||||
"parent ref with nil ref": {
|
||||
routeTenancy: resource.DefaultNamespacedTenancy(),
|
||||
refs: []*pbmesh.ParentReference{
|
||||
newParentRef(pbcatalog.ServiceType, "", "api", ""),
|
||||
{
|
||||
Ref: nil,
|
||||
Port: "http",
|
||||
},
|
||||
},
|
||||
expectErr: `invalid element at index 1 of list "parent_refs": invalid "ref" field: missing required field`,
|
||||
},
|
||||
"parent ref with bad type ref": {
|
||||
routeTenancy: resource.DefaultNamespacedTenancy(),
|
||||
refs: []*pbmesh.ParentReference{
|
||||
newParentRef(pbcatalog.ServiceType, "", "api", ""),
|
||||
newParentRef(pbcatalog.WorkloadType, "", "api", ""),
|
||||
},
|
||||
expectErr: `invalid element at index 1 of list "parent_refs": invalid "ref" field: invalid "type" field: reference must have type catalog.v2beta1.Service`,
|
||||
},
|
||||
"parent ref with section": {
|
||||
routeTenancy: resource.DefaultNamespacedTenancy(),
|
||||
refs: []*pbmesh.ParentReference{
|
||||
newParentRef(pbcatalog.ServiceType, "", "api", ""),
|
||||
{
|
||||
Ref: resourcetest.Resource(pbcatalog.ServiceType, "web").Reference("section2"),
|
||||
Port: "http",
|
||||
},
|
||||
},
|
||||
expectErr: `invalid element at index 1 of list "parent_refs": invalid "ref" field: invalid "section" field: section cannot be set here`,
|
||||
},
|
||||
"cross namespace parent": {
|
||||
routeTenancy: resource.DefaultNamespacedTenancy(),
|
||||
refs: []*pbmesh.ParentReference{
|
||||
newParentRef(pbcatalog.ServiceType, "default.foo", "api", ""),
|
||||
},
|
||||
expectErr: `invalid element at index 0 of list "parent_refs": invalid "ref" field: invalid "tenancy" field: resource tenancy and reference tenancy differ`,
|
||||
},
|
||||
"cross partition parent": {
|
||||
routeTenancy: resource.DefaultNamespacedTenancy(),
|
||||
refs: []*pbmesh.ParentReference{
|
||||
newParentRef(pbcatalog.ServiceType, "alpha.default", "api", ""),
|
||||
},
|
||||
expectErr: `invalid element at index 0 of list "parent_refs": invalid "ref" field: invalid "tenancy" field: resource tenancy and reference tenancy differ`,
|
||||
},
|
||||
"cross tenancy parent": {
|
||||
routeTenancy: resource.DefaultNamespacedTenancy(),
|
||||
refs: []*pbmesh.ParentReference{
|
||||
newParentRef(pbcatalog.ServiceType, "alpha.foo", "api", ""),
|
||||
},
|
||||
expectErr: `invalid element at index 0 of list "parent_refs": invalid "ref" field: invalid "tenancy" field: resource tenancy and reference tenancy differ`,
|
||||
},
|
||||
"duplicate exact parents": {
|
||||
routeTenancy: resource.DefaultNamespacedTenancy(),
|
||||
refs: []*pbmesh.ParentReference{
|
||||
newParentRef(pbcatalog.ServiceType, "", "api", "http"),
|
||||
newParentRef(pbcatalog.ServiceType, "", "api", "http"),
|
||||
},
|
||||
expectErr: `invalid element at index 1 of list "parent_refs": invalid "port" field: parent ref "catalog.v2beta1.Service/default.local.default/api" for port "http" exists twice`,
|
||||
},
|
||||
"duplicate wild parents": {
|
||||
routeTenancy: resource.DefaultNamespacedTenancy(),
|
||||
refs: []*pbmesh.ParentReference{
|
||||
newParentRef(pbcatalog.ServiceType, "", "api", ""),
|
||||
newParentRef(pbcatalog.ServiceType, "", "api", ""),
|
||||
},
|
||||
expectErr: `invalid element at index 1 of list "parent_refs": invalid "port" field: parent ref "catalog.v2beta1.Service/default.local.default/api" for wildcard port exists twice`,
|
||||
},
|
||||
"duplicate parents via exact+wild overlap": {
|
||||
routeTenancy: resource.DefaultNamespacedTenancy(),
|
||||
refs: []*pbmesh.ParentReference{
|
||||
newParentRef(pbcatalog.ServiceType, "", "api", "http"),
|
||||
newParentRef(pbcatalog.ServiceType, "", "api", ""),
|
||||
},
|
||||
expectErr: `invalid element at index 1 of list "parent_refs": invalid "port" field: parent ref "catalog.v2beta1.Service/default.local.default/api" for ports [http] covered by wildcard port already`,
|
||||
},
|
||||
"duplicate parents via exact+wild overlap (reversed)": {
|
||||
routeTenancy: resource.DefaultNamespacedTenancy(),
|
||||
refs: []*pbmesh.ParentReference{
|
||||
newParentRef(pbcatalog.ServiceType, "", "api", ""),
|
||||
newParentRef(pbcatalog.ServiceType, "", "api", "http"),
|
||||
},
|
||||
expectErr: `invalid element at index 1 of list "parent_refs": invalid "port" field: parent ref "catalog.v2beta1.Service/default.local.default/api" for port "http" covered by wildcard port already`,
|
||||
},
|
||||
"good single parent ref": {
|
||||
routeTenancy: resource.DefaultNamespacedTenancy(),
|
||||
refs: []*pbmesh.ParentReference{
|
||||
newParentRef(pbcatalog.ServiceType, "", "api", "http"),
|
||||
},
|
||||
},
|
||||
"good muliple parent refs": {
|
||||
routeTenancy: resource.DefaultNamespacedTenancy(),
|
||||
refs: []*pbmesh.ParentReference{
|
||||
newParentRef(pbcatalog.ServiceType, "", "api", "http"),
|
||||
newParentRef(pbcatalog.ServiceType, "", "web", ""),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type xRouteBackendRefTestcase struct {
|
||||
routeTenancy *pbresource.Tenancy
|
||||
refs []*pbmesh.BackendReference
|
||||
expectErr string
|
||||
}
|
||||
|
||||
func getXRouteBackendRefTestCases() map[string]xRouteBackendRefTestcase {
|
||||
newRef := func(typ *pbresource.Type, tenancyStr, name string) *pbresource.Reference {
|
||||
return resourcetest.Resource(typ, name).
|
||||
WithTenancy(resourcetest.Tenancy(tenancyStr)).
|
||||
Reference("")
|
||||
}
|
||||
|
||||
newBackendRef := func(typ *pbresource.Type, tenancyStr, name, port string) *pbmesh.BackendReference {
|
||||
return &pbmesh.BackendReference{
|
||||
Ref: newRef(typ, tenancyStr, name),
|
||||
Port: port,
|
||||
}
|
||||
}
|
||||
return map[string]xRouteBackendRefTestcase{
|
||||
"no backend refs": {
|
||||
routeTenancy: resource.DefaultNamespacedTenancy(),
|
||||
expectErr: `invalid "backend_refs" field: cannot be empty`,
|
||||
},
|
||||
"backend ref with nil ref": {
|
||||
routeTenancy: resource.DefaultNamespacedTenancy(),
|
||||
refs: []*pbmesh.BackendReference{
|
||||
newBackendRef(pbcatalog.ServiceType, "", "api", ""),
|
||||
{
|
||||
Ref: nil,
|
||||
Port: "http",
|
||||
},
|
||||
},
|
||||
expectErr: `invalid element at index 0 of list "rules": invalid element at index 1 of list "backend_refs": invalid "backend_ref" field: invalid "ref" field: missing required field`,
|
||||
},
|
||||
"backend ref with bad type ref": {
|
||||
routeTenancy: resource.DefaultNamespacedTenancy(),
|
||||
refs: []*pbmesh.BackendReference{
|
||||
newBackendRef(pbcatalog.ServiceType, "", "api", ""),
|
||||
newBackendRef(pbcatalog.WorkloadType, "", "api", ""),
|
||||
},
|
||||
expectErr: `invalid element at index 0 of list "rules": invalid element at index 1 of list "backend_refs": invalid "backend_ref" field: invalid "ref" field: invalid "type" field: reference must have type catalog.v2beta1.Service`,
|
||||
},
|
||||
"backend ref with section": {
|
||||
routeTenancy: resource.DefaultNamespacedTenancy(),
|
||||
refs: []*pbmesh.BackendReference{
|
||||
newBackendRef(pbcatalog.ServiceType, "", "api", ""),
|
||||
{
|
||||
Ref: resourcetest.Resource(pbcatalog.ServiceType, "web").Reference("section2"),
|
||||
Port: "http",
|
||||
},
|
||||
},
|
||||
expectErr: `invalid element at index 0 of list "rules": invalid element at index 1 of list "backend_refs": invalid "backend_ref" field: invalid "ref" field: invalid "section" field: section cannot be set here`,
|
||||
},
|
||||
"backend ref with datacenter": {
|
||||
routeTenancy: resource.DefaultNamespacedTenancy(),
|
||||
refs: []*pbmesh.BackendReference{
|
||||
newBackendRef(pbcatalog.ServiceType, "", "api", ""),
|
||||
{
|
||||
Ref: newRef(pbcatalog.ServiceType, "", "db"),
|
||||
Port: "http",
|
||||
Datacenter: "dc2",
|
||||
},
|
||||
},
|
||||
expectErr: `invalid element at index 0 of list "rules": invalid element at index 1 of list "backend_refs": invalid "backend_ref" field: invalid "datacenter" field: datacenter is not yet supported on backend refs`,
|
||||
},
|
||||
"cross namespace backend": {
|
||||
routeTenancy: resource.DefaultNamespacedTenancy(),
|
||||
refs: []*pbmesh.BackendReference{
|
||||
newBackendRef(pbcatalog.ServiceType, "default.foo", "api", ""),
|
||||
},
|
||||
},
|
||||
"cross partition backend": {
|
||||
routeTenancy: resource.DefaultNamespacedTenancy(),
|
||||
refs: []*pbmesh.BackendReference{
|
||||
newBackendRef(pbcatalog.ServiceType, "alpha.default", "api", ""),
|
||||
},
|
||||
},
|
||||
"cross tenancy backend": {
|
||||
routeTenancy: resource.DefaultNamespacedTenancy(),
|
||||
refs: []*pbmesh.BackendReference{
|
||||
newBackendRef(pbcatalog.ServiceType, "alpha.foo", "api", ""),
|
||||
},
|
||||
},
|
||||
"good backend ref": {
|
||||
routeTenancy: resource.DefaultNamespacedTenancy(),
|
||||
refs: []*pbmesh.BackendReference{
|
||||
newBackendRef(pbcatalog.ServiceType, "", "api", ""),
|
||||
{
|
||||
Ref: newRef(pbcatalog.ServiceType, "", "db"),
|
||||
Port: "http",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type xRouteTimeoutsTestcase struct {
|
||||
timeouts *pbmesh.HTTPRouteTimeouts
|
||||
expectErr string
|
||||
}
|
||||
|
||||
func getXRouteTimeoutsTestCases() map[string]xRouteTimeoutsTestcase {
|
||||
return map[string]xRouteTimeoutsTestcase{
|
||||
"bad request": {
|
||||
timeouts: &pbmesh.HTTPRouteTimeouts{
|
||||
Request: durationpb.New(-1 * time.Second),
|
||||
},
|
||||
expectErr: `invalid element at index 0 of list "rules": invalid "timeouts" field: invalid "request" field: timeout cannot be negative: -1s`,
|
||||
},
|
||||
"bad idle": {
|
||||
timeouts: &pbmesh.HTTPRouteTimeouts{
|
||||
Idle: durationpb.New(-1 * time.Second),
|
||||
},
|
||||
expectErr: `invalid element at index 0 of list "rules": invalid "timeouts" field: invalid "idle" field: timeout cannot be negative: -1s`,
|
||||
},
|
||||
"good all": {
|
||||
timeouts: &pbmesh.HTTPRouteTimeouts{
|
||||
Request: durationpb.New(1 * time.Second),
|
||||
Idle: durationpb.New(3 * time.Second),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type xRouteRetriesTestcase struct {
|
||||
retries *pbmesh.HTTPRouteRetries
|
||||
expectErr string
|
||||
}
|
||||
|
||||
func getXRouteRetriesTestCases() map[string]xRouteRetriesTestcase {
|
||||
return map[string]xRouteRetriesTestcase{
|
||||
"bad conditions": {
|
||||
retries: &pbmesh.HTTPRouteRetries{
|
||||
OnConditions: []string{"garbage"},
|
||||
},
|
||||
expectErr: `invalid element at index 0 of list "rules": invalid "retries" field: invalid element at index 0 of list "on_conditions": not a valid retry condition: "garbage"`,
|
||||
},
|
||||
"good all": {
|
||||
retries: &pbmesh.HTTPRouteRetries{
|
||||
Number: wrapperspb.UInt32(5),
|
||||
OnConditions: []string{"internal"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func testXRouteACLs[R XRouteData](t *testing.T, newRoute func(t *testing.T, parentRefs, backendRefs []*pbresource.Reference) *pbresource.Resource) {
|
||||
// Wire up a registry to generically invoke hooks
|
||||
registry := resource.NewRegistry()
|
||||
@ -164,3 +512,33 @@ func testXRouteACLs[R XRouteData](t *testing.T, newRoute func(t *testing.T, pare
|
||||
assert(t, "2parents 2backends", rules, resTwoParentsTwoBackends, DENY, DENY)
|
||||
})
|
||||
}
|
||||
|
||||
func newRef(typ *pbresource.Type, name string) *pbresource.Reference {
|
||||
return resourcetest.Resource(typ, name).
|
||||
WithTenancy(resource.DefaultNamespacedTenancy()).
|
||||
Reference("")
|
||||
}
|
||||
|
||||
func newRefWithTenancy(typ *pbresource.Type, tenancyStr, name string) *pbresource.Reference {
|
||||
return resourcetest.Resource(typ, name).
|
||||
WithTenancy(resourcetest.Tenancy(tenancyStr)).
|
||||
Reference("")
|
||||
}
|
||||
|
||||
func newBackendRef(typ *pbresource.Type, name, port string) *pbmesh.BackendReference {
|
||||
return &pbmesh.BackendReference{
|
||||
Ref: newRef(typ, name),
|
||||
Port: port,
|
||||
}
|
||||
}
|
||||
|
||||
func newParentRef(typ *pbresource.Type, name, port string) *pbmesh.ParentReference {
|
||||
return newParentRefWithTenancy(typ, "default.default", name, port)
|
||||
}
|
||||
|
||||
func newParentRefWithTenancy(typ *pbresource.Type, tenancyStr string, name, port string) *pbmesh.ParentReference {
|
||||
return &pbmesh.ParentReference{
|
||||
Ref: newRefWithTenancy(typ, tenancyStr, name),
|
||||
Port: port,
|
||||
}
|
||||
}
|
||||
|
37
internal/resource/resourcetest/tenancy.go
Normal file
37
internal/resource/resourcetest/tenancy.go
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package resourcetest
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/consul/internal/resource"
|
||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||
)
|
||||
|
||||
// Tenancy constructs a pbresource.Tenancy from a concise string representation
|
||||
// suitable for use in unit tests.
|
||||
//
|
||||
// - "" : partition="" namespace="" peerName="local"
|
||||
// - "foo" : partition="foo" namespace="" peerName="local"
|
||||
// - "foo.bar" : partition="foo" namespace="bar" peerName="local"
|
||||
// - <others> : partition="BAD" namespace="BAD" peerName="BAD"
|
||||
func Tenancy(s string) *pbresource.Tenancy {
|
||||
parts := strings.Split(s, ".")
|
||||
switch len(parts) {
|
||||
case 0:
|
||||
return resource.DefaultClusteredTenancy()
|
||||
case 1:
|
||||
v := resource.DefaultPartitionedTenancy()
|
||||
v.Partition = parts[0]
|
||||
return v
|
||||
case 2:
|
||||
v := resource.DefaultNamespacedTenancy()
|
||||
v.Partition = parts[0]
|
||||
v.Namespace = parts[1]
|
||||
return v
|
||||
default:
|
||||
return &pbresource.Tenancy{Partition: "BAD", Namespace: "BAD", PeerName: "BAD"}
|
||||
}
|
||||
}
|
@ -103,6 +103,27 @@ func DefaultNamespacedTenancy() *pbresource.Tenancy {
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultIDTenancy will default/normalize the Tenancy of the provided
|
||||
// ID in the context of some parent resource containing that ID.
|
||||
// The default tenancy for the ID's type is also provided in cases where
|
||||
// "default" is needed selectively or the parent is more precise than the
|
||||
// child.
|
||||
func DefaultIDTenancy(id *pbresource.ID, parentTenancy, scopeTenancy *pbresource.Tenancy) {
|
||||
if id == nil {
|
||||
return
|
||||
}
|
||||
if id.Tenancy == nil {
|
||||
id.Tenancy = &pbresource.Tenancy{}
|
||||
}
|
||||
|
||||
if parentTenancy != nil {
|
||||
dup := proto.Clone(parentTenancy).(*pbresource.Tenancy)
|
||||
parentTenancy = dup
|
||||
}
|
||||
|
||||
defaultTenancy(id.Tenancy, parentTenancy, scopeTenancy)
|
||||
}
|
||||
|
||||
// DefaultReferenceTenancy will default/normalize the Tenancy of the provided
|
||||
// Reference in the context of some parent resource containing that Reference.
|
||||
// The default tenancy for the Reference's type is also provided in cases where
|
||||
|
Loading…
x
Reference in New Issue
Block a user