Skip to content

Commit

Permalink
mesh: storing bound reference pointers on a ComputedRoutes resource
Browse files Browse the repository at this point in the history
  • Loading branch information
rboyer committed Sep 22, 2023
1 parent de231bb commit 406f3fa
Show file tree
Hide file tree
Showing 15 changed files with 1,192 additions and 295 deletions.
66 changes: 66 additions & 0 deletions internal/mesh/internal/controllers/routes/bound_refs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

package routes

import (
"sort"

"github.com/hashicorp/consul/internal/resource"
"github.com/hashicorp/consul/proto-public/pbresource"
)

type sectionRefKey struct {
resource.ReferenceKey
Section string
}

type BoundReferenceCollector struct {
refs map[sectionRefKey]*pbresource.Reference
}

func NewBoundReferenceCollector() *BoundReferenceCollector {
return &BoundReferenceCollector{
refs: make(map[sectionRefKey]*pbresource.Reference),
}
}

func (c *BoundReferenceCollector) List() []*pbresource.Reference {
if len(c.refs) == 0 {
return nil
}

out := make([]*pbresource.Reference, 0, len(c.refs))
for _, ref := range c.refs {
out = append(out, ref)
}

sort.Slice(out, func(i, j int) bool {
return resource.LessReference(out[i], out[j])
})

return out
}

func (c *BoundReferenceCollector) AddRefOrID(ref resource.ReferenceOrID) {
if c == nil {
return
}
c.AddRef(resource.ReferenceFromReferenceOrID(ref))
}

func (c *BoundReferenceCollector) AddRef(ref *pbresource.Reference) {
if c == nil {
return
}
srk := sectionRefKey{
ReferenceKey: resource.NewReferenceKey(ref),
Section: ref.Section,
}

if _, ok := c.refs[srk]; ok {
return
}

c.refs[srk] = ref
}
6 changes: 6 additions & 0 deletions internal/mesh/internal/controllers/routes/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ func (r *routesReconciler) Reconcile(ctx context.Context, rt controller.Runtime,
return err
}

if prev != nil {
r.mapper.TrackComputedRoutes(prev)
} else {
r.mapper.UntrackComputedRoutes(computedRoutesID)
}

if err := ensureComputedRoutesIsSynced(ctx, logger, rt.Client, result, prev); err != nil {
return err
}
Expand Down
224 changes: 224 additions & 0 deletions internal/mesh/internal/controllers/routes/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ func (suite *controllerSuite) TestController() {
testutil.RunStep(suite.T(), "default tcp route", func(t *testing.T) {
// Check that the computed routes resource exists and it has one port that is the default.
expect := &pbmesh.ComputedRoutes{
BoundReferences: []*pbresource.Reference{
apiServiceRef,
},
PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{
"tcp": {
UsingDefaultConfig: true,
Expand Down Expand Up @@ -161,6 +164,9 @@ func (suite *controllerSuite) TestController() {

testutil.RunStep(suite.T(), "default other routes", func(t *testing.T) {
expect := &pbmesh.ComputedRoutes{
BoundReferences: []*pbresource.Reference{
apiServiceRef,
},
PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{
"tcp": {
UsingDefaultConfig: true,
Expand Down Expand Up @@ -318,6 +324,13 @@ func (suite *controllerSuite) TestController() {

testutil.RunStep(suite.T(), "one of each", func(t *testing.T) {
expect := &pbmesh.ComputedRoutes{
BoundReferences: []*pbresource.Reference{
apiServiceRef,
fooServiceRef,
resource.Reference(grpcRoute1ID, ""),
resource.Reference(httpRoute1ID, ""),
resource.Reference(tcpRoute1ID, ""),
},
PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{
"tcp": {
Config: &pbmesh.ComputedPortRoutes_Tcp{
Expand Down Expand Up @@ -502,6 +515,16 @@ func (suite *controllerSuite) TestController() {

testutil.RunStep(suite.T(), "one good one bad route", func(t *testing.T) {
expect := &pbmesh.ComputedRoutes{
BoundReferences: []*pbresource.Reference{
apiServiceRef,
fooServiceRef,
resource.Reference(grpcRoute1ID, ""),
resource.Reference(grpcRoute2ID, ""),
resource.Reference(httpRoute1ID, ""),
resource.Reference(httpRoute2ID, ""),
resource.Reference(tcpRoute1ID, ""),
resource.Reference(tcpRoute2ID, ""),
},
PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{
"tcp": {
Config: &pbmesh.ComputedPortRoutes_Tcp{
Expand Down Expand Up @@ -737,6 +760,16 @@ func (suite *controllerSuite) TestController() {

testutil.RunStep(suite.T(), "overlapping xRoutes generate conflicts", func(t *testing.T) {
expect := &pbmesh.ComputedRoutes{
BoundReferences: []*pbresource.Reference{
apiServiceRef,
fooServiceRef,
resource.Reference(grpcRoute1ID, ""),
resource.Reference(grpcRoute2ID, ""),
resource.Reference(httpRoute1ID, ""),
resource.Reference(httpRoute2ID, ""),
resource.Reference(tcpRoute1ID, ""),
resource.Reference(tcpRoute2ID, ""),
},
PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{
"tcp": {
Config: &pbmesh.ComputedPortRoutes_Tcp{
Expand Down Expand Up @@ -896,6 +929,13 @@ func (suite *controllerSuite) TestController() {

testutil.RunStep(suite.T(), "overlapping xRoutes due to port wildcarding", func(t *testing.T) {
expect := &pbmesh.ComputedRoutes{
BoundReferences: []*pbresource.Reference{
apiServiceRef,
fooServiceRef,
resource.Reference(grpcRoute1ID, ""),
resource.Reference(httpRoute1ID, ""),
resource.Reference(tcpRoute1ID, ""),
},
PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{
"tcp": {
Config: &pbmesh.ComputedPortRoutes_Tcp{
Expand Down Expand Up @@ -1048,6 +1088,190 @@ func (suite *controllerSuite) TestController() {
suite.client.WaitForStatusCondition(t, grpcRoute1ID, StatusKey,
ConditionParentRefOutsideMesh(newRef(catalog.ServiceType, "api")))
})

// Get down to just 2 ports for all relevant services.
for _, name := range []string{"foo", "bar", "api"} {
_ = rtest.Resource(catalog.ServiceType, name).
WithTenancy(resource.DefaultNamespacedTenancy()).
WithData(suite.T(), &pbcatalog.Service{
Workloads: &pbcatalog.WorkloadSelector{
Prefixes: []string{name + "-"},
},
Ports: []*pbcatalog.ServicePort{
{TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH},
{TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP},
},
}).
Write(suite.T(), suite.client)
}

httpRoute1 = &pbmesh.HTTPRoute{
ParentRefs: []*pbmesh.ParentReference{
newParentRef(fooServiceRef, "http"),
newParentRef(barServiceRef, "http"),
},
Rules: []*pbmesh.HTTPRouteRule{{
BackendRefs: []*pbmesh.HTTPBackendRef{{
BackendRef: newBackendRef(apiServiceRef, "", ""),
}},
}},
}
httpRoute1ID = rtest.Resource(types.HTTPRouteType, "route1").
WithTenancy(resource.DefaultNamespacedTenancy()).
WithData(suite.T(), httpRoute1).
Write(suite.T(), suite.client).
Id

var (
fooLastVersion string
barLastVersion string

fooComputedRoutesID = rtest.Resource(types.ComputedRoutesType, "foo").
WithTenancy(resource.DefaultNamespacedTenancy()).
ID()
barComputedRoutesID = rtest.Resource(types.ComputedRoutesType, "bar").
WithTenancy(resource.DefaultNamespacedTenancy()).
ID()
)

testutil.RunStep(suite.T(), "create a route linked to two parents", func(t *testing.T) {
expectFoo := &pbmesh.ComputedRoutes{
BoundReferences: []*pbresource.Reference{
apiServiceRef,
fooServiceRef,
resource.Reference(httpRoute1ID, ""),
},
PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{
"http": {
Config: &pbmesh.ComputedPortRoutes_Http{
Http: &pbmesh.ComputedHTTPRoute{
Rules: []*pbmesh.ComputedHTTPRouteRule{
{
Matches: defaultHTTPRouteMatches(),
BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{
BackendTarget: backendName("api", "http"),
}},
},
{
Matches: defaultHTTPRouteMatches(),
BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{
BackendTarget: types.NullRouteBackend,
}},
},
},
},
},
ParentRef: newParentRef(fooServiceRef, "http"),
Protocol: pbcatalog.Protocol_PROTOCOL_HTTP,
Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("api", "http"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh",
BackendRef: newBackendRef(apiServiceRef, "http", ""),
},
},
},
},
}
expectBar := &pbmesh.ComputedRoutes{
BoundReferences: []*pbresource.Reference{
apiServiceRef,
barServiceRef,
resource.Reference(httpRoute1ID, ""),
},
PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{
"http": {
Config: &pbmesh.ComputedPortRoutes_Http{
Http: &pbmesh.ComputedHTTPRoute{
Rules: []*pbmesh.ComputedHTTPRouteRule{
{
Matches: defaultHTTPRouteMatches(),
BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{
BackendTarget: backendName("api", "http"),
}},
},
{
Matches: defaultHTTPRouteMatches(),
BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{
BackendTarget: types.NullRouteBackend,
}},
},
},
},
},
ParentRef: newParentRef(barServiceRef, "http"),
Protocol: pbcatalog.Protocol_PROTOCOL_HTTP,
Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("api", "http"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh",
BackendRef: newBackendRef(apiServiceRef, "http", ""),
},
},
},
},
}

fooLastVersion = requireNewComputedRoutesVersion(t, suite.client, fooComputedRoutesID, fooLastVersion, expectFoo)
barLastVersion = requireNewComputedRoutesVersion(t, suite.client, barComputedRoutesID, barLastVersion, expectBar)

suite.client.WaitForStatusCondition(t, httpRoute1ID, StatusKey, ConditionXRouteOK)
})

// Remove bar parent
httpRoute1 = &pbmesh.HTTPRoute{
ParentRefs: []*pbmesh.ParentReference{
newParentRef(fooServiceRef, "http"),
},
Rules: []*pbmesh.HTTPRouteRule{{
BackendRefs: []*pbmesh.HTTPBackendRef{{
BackendRef: newBackendRef(apiServiceRef, "", ""),
}},
}},
}
httpRoute1ID = rtest.Resource(types.HTTPRouteType, "route1").
WithTenancy(resource.DefaultNamespacedTenancy()).
WithData(suite.T(), httpRoute1).
Write(suite.T(), suite.client).
Id

testutil.RunStep(suite.T(), "remove a parent ref and show that the old computed routes is reconciled one more time", func(t *testing.T) {
expectBar := &pbmesh.ComputedRoutes{
BoundReferences: []*pbresource.Reference{
barServiceRef,
},
PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{
"http": {
Config: &pbmesh.ComputedPortRoutes_Http{
Http: &pbmesh.ComputedHTTPRoute{
Rules: []*pbmesh.ComputedHTTPRouteRule{
{
Matches: defaultHTTPRouteMatches(),
BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{
BackendTarget: backendName("bar", "http"),
}},
},
},
},
},
UsingDefaultConfig: true,
ParentRef: newParentRef(barServiceRef, "http"),
Protocol: pbcatalog.Protocol_PROTOCOL_HTTP,
Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("bar", "http"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh",
BackendRef: newBackendRef(barServiceRef, "http", ""),
},
},
},
},
}

barLastVersion = requireNewComputedRoutesVersion(t, suite.client, barComputedRoutesID, barLastVersion, expectBar)

suite.client.WaitForStatusCondition(t, httpRoute1ID, StatusKey, ConditionXRouteOK)
})
}

func newParentRef(ref *pbresource.Reference, port string) *pbmesh.ParentReference {
Expand Down
Loading

0 comments on commit 406f3fa

Please sign in to comment.