From cd28e66baf95bb8ca7a0db9c27b7d7fb40d945b8 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Wed, 9 Aug 2023 16:26:37 -0500 Subject: [PATCH 01/49] wip: routes controller etc --- .../mesh/internal/controllers/register.go | 3 + .../internal/controllers/routes/controller.go | 186 ++++++ .../controllers/routes/controller_test.go | 132 ++++ .../mesh/internal/controllers/routes/cross.go | 103 +++ .../internal/controllers/routes/generate.go | 537 ++++++++++++++++ .../controllers/routes/intermediate.go | 108 ++++ .../controllers/routes/loader/loader.go | 311 ++++++++++ .../controllers/routes/loader/loader_test.go | 400 ++++++++++++ .../controllers/routes/loader/memoized.go | 164 +++++ .../controllers/routes/loader/related.go | 204 ++++++ .../controllers/routes/pending_status.go | 92 +++ .../internal/controllers/routes/sort_rules.go | 220 +++++++ .../internal/controllers/routes/status.go | 40 ++ .../controllers/routes/status_xroute.go | 133 ++++ .../mesh/internal/controllers/routes/util.go | 30 + .../controllers/routes/xroutemapper/util.go | 102 +++ .../routes/xroutemapper/xroutemapper.go | 311 ++++++++++ .../routes/xroutemapper/xroutemapper_test.go | 587 ++++++++++++++++++ internal/mesh/internal/types/util.go | 22 + internal/mesh/internal/types/xroute.go | 54 ++ .../pbmesh/v1alpha1/computed_routes.pb.go | 4 +- .../pbmesh/v1alpha1/computed_routes.proto | 2 + 22 files changed, 3744 insertions(+), 1 deletion(-) create mode 100644 internal/mesh/internal/controllers/routes/controller.go create mode 100644 internal/mesh/internal/controllers/routes/controller_test.go create mode 100644 internal/mesh/internal/controllers/routes/cross.go create mode 100644 internal/mesh/internal/controllers/routes/generate.go create mode 100644 internal/mesh/internal/controllers/routes/intermediate.go create mode 100644 internal/mesh/internal/controllers/routes/loader/loader.go create mode 100644 internal/mesh/internal/controllers/routes/loader/loader_test.go create mode 100644 internal/mesh/internal/controllers/routes/loader/memoized.go create mode 100644 internal/mesh/internal/controllers/routes/loader/related.go create mode 100644 internal/mesh/internal/controllers/routes/pending_status.go create mode 100644 internal/mesh/internal/controllers/routes/sort_rules.go create mode 100644 internal/mesh/internal/controllers/routes/status.go create mode 100644 internal/mesh/internal/controllers/routes/status_xroute.go create mode 100644 internal/mesh/internal/controllers/routes/util.go create mode 100644 internal/mesh/internal/controllers/routes/xroutemapper/util.go create mode 100644 internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper.go create mode 100644 internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper_test.go diff --git a/internal/mesh/internal/controllers/register.go b/internal/mesh/internal/controllers/register.go index e24decb3710..5c5a9d070e5 100644 --- a/internal/mesh/internal/controllers/register.go +++ b/internal/mesh/internal/controllers/register.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/consul/internal/catalog" "github.com/hashicorp/consul/internal/controller" "github.com/hashicorp/consul/internal/mesh/internal/cache/sidecarproxycache" + "github.com/hashicorp/consul/internal/mesh/internal/controllers/routes" "github.com/hashicorp/consul/internal/mesh/internal/controllers/sidecarproxy" "github.com/hashicorp/consul/internal/mesh/internal/controllers/xds" "github.com/hashicorp/consul/internal/mesh/internal/mappers/sidecarproxymapper" @@ -31,4 +32,6 @@ func Register(mgr *controller.Manager, deps Dependencies) { mgr.Register( sidecarproxy.Controller(destinationsCache, proxyCfgCache, m, deps.TrustDomainFetcher, deps.LocalDatacenter), ) + + mgr.Register(routes.Controller()) } diff --git a/internal/mesh/internal/controllers/routes/controller.go b/internal/mesh/internal/controllers/routes/controller.go new file mode 100644 index 00000000000..859b11e26fa --- /dev/null +++ b/internal/mesh/internal/controllers/routes/controller.go @@ -0,0 +1,186 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package routes + +import ( + "context" + + "github.com/hashicorp/go-hclog" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/anypb" + + "github.com/hashicorp/consul/internal/catalog" + "github.com/hashicorp/consul/internal/controller" + "github.com/hashicorp/consul/internal/mesh/internal/controllers/routes/loader" + "github.com/hashicorp/consul/internal/mesh/internal/controllers/routes/xroutemapper" + "github.com/hashicorp/consul/internal/mesh/internal/types" + "github.com/hashicorp/consul/internal/resource" + pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" + "github.com/hashicorp/consul/proto-public/pbresource" +) + +const ( + metaManagedBy = "managed-by-controller" +) + +func Controller() controller.Controller { + mapper := xroutemapper.New() + + r := &routesReconciler{ + mapper: mapper, + } + return controller.ForType(types.ComputedRoutesType). + WithWatch(types.HTTPRouteType, mapper.MapHTTPRoute). + WithWatch(types.GRPCRouteType, mapper.MapGRPCRoute). + WithWatch(types.TCPRouteType, mapper.MapTCPRoute). + WithWatch(types.DestinationPolicyType, mapper.MapDestinationPolicy). + WithWatch(catalog.FailoverPolicyType, mapper.MapFailoverPolicy). + WithWatch(catalog.ServiceType, mapper.MapService). + WithReconciler(r) +} + +type routesReconciler struct { + mapper *xroutemapper.Mapper +} + +// TODO: only emit status updates on the xRoute types and when we do so, suffix them with the parent they apply to +func (r *routesReconciler) Reconcile(ctx context.Context, rt controller.Runtime, req controller.Request) error { + // Notably don't inject the resource-id here, since we have to do a fan-out + // to multiple resources due to xRoutes having multiple parent refs. + rt.Logger = rt.Logger.With("controller", StatusKey) + // rt.Logger = rt.Logger.With("event-resource-id", resource.IDToString(req.ID)) + + rt.Logger.Trace("reconciling computed routes") + + loggerFor := func(id *pbresource.ID) hclog.Logger { + return rt.Logger.With("resource-id", resource.IDToString(id)) + } + related, err := loader.LoadResourcesForComputedRoutes(ctx, loggerFor, rt.Client, r.mapper, req.ID) + if err != nil { + rt.Logger.Error("error loading relevant resources", "error", err) + return err + } + + pending := make(PendingStatuses) + + ValidateXRouteReferences(related, pending) + + generatedResults := GenerateComputedRoutes(ctx, rt.Logger, related, pending) + + if err := UpdatePendingStatuses(ctx, rt, pending); err != nil { + rt.Logger.Error("error updating statuses for affected relevant resources", "error", err) + return err + } + + for _, result := range generatedResults { + computedRoutesID := result.ID + + logger := rt.Logger.With("resource-id", resource.IDToString(computedRoutesID)) + + prev, err := resource.GetDecodedResource[pbmesh.ComputedRoutes, *pbmesh.ComputedRoutes](ctx, rt.Client, computedRoutesID) + if err != nil { + logger.Error("error loading previous computed routes", "error", err) + return err + } + + if err := ensureComputedRoutesIsSynced(ctx, logger, rt.Client, result, prev); err != nil { + return err + } + } + + return nil +} + +func ensureComputedRoutesIsSynced( + ctx context.Context, + logger hclog.Logger, + client pbresource.ResourceServiceClient, + result *ComputedRoutesResult, + prev *types.DecodedComputedRoutes, +) error { + if result.Data == nil { + return deleteComputedRoutes(ctx, logger, client, prev) + } + + // Upsert the resource if changed. + if prev != nil { + if proto.Equal(prev.Data, result.Data) { + return nil // no change + } + result.ID = prev.Resource.Id + } + + return upsertComputedRoutes(ctx, logger, client, result.ID, result.OwnerID, result.Data) +} + +func upsertComputedRoutes( + ctx context.Context, + logger hclog.Logger, + client pbresource.ResourceServiceClient, + id *pbresource.ID, + ownerID *pbresource.ID, + data *pbmesh.ComputedRoutes, +) error { + mcData, err := anypb.New(data) + if err != nil { + logger.Error("error marshalling new computed routes payload", "error", err) + return err + } + + // Now perform the write. The computed routes resource should be owned + // by the service so that it will automatically be deleted upon service + // deletion. + + _, err = client.Write(ctx, &pbresource.WriteRequest{ + Resource: &pbresource.Resource{ + Id: id, + Owner: ownerID, + Metadata: map[string]string{ + metaManagedBy: StatusKey, + }, + Data: mcData, + }, + }) + if err != nil { + logger.Error("error writing generated mesh config", "error", err) + return err + } + + logger.Trace("updated mesh config was successfully written") + + return nil +} + +func deleteComputedRoutes( + ctx context.Context, + logger hclog.Logger, + client pbresource.ResourceServiceClient, + prev *types.DecodedComputedRoutes, +) error { + if prev == nil { + return nil + } + + // The service the mesh config controls no longer participates in the + // mesh at all. + + logger.Trace("removing previous computed routes") + + // This performs a CAS deletion. + _, err := client.Delete(ctx, &pbresource.DeleteRequest{ + Id: prev.Resource.Id, + Version: prev.Resource.Version, + }) + // Potentially we could look for CAS failures by checking if the gRPC + // status code is Aborted. However its an edge case and there could + // possibly be other reasons why the gRPC status code would be aborted + // besides CAS version mismatches. The simplest thing to do is to just + // propagate the error and retry reconciliation later. + if err != nil { + logger.Error("error deleting previous mesh config", "error", err) + return err + } + + return nil +} diff --git a/internal/mesh/internal/controllers/routes/controller_test.go b/internal/mesh/internal/controllers/routes/controller_test.go new file mode 100644 index 00000000000..24787b32bd8 --- /dev/null +++ b/internal/mesh/internal/controllers/routes/controller_test.go @@ -0,0 +1,132 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package routes + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing" + "github.com/hashicorp/consul/internal/catalog" + "github.com/hashicorp/consul/internal/controller" + "github.com/hashicorp/consul/internal/mesh/internal/types" + rtest "github.com/hashicorp/consul/internal/resource/resourcetest" + pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v1alpha1" + pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" + "github.com/hashicorp/consul/proto-public/pbresource" + "github.com/hashicorp/consul/proto/private/prototest" + "github.com/hashicorp/consul/sdk/testutil" +) + +type controllerSuite struct { + suite.Suite + + ctx context.Context + client *rtest.Client + rt controller.Runtime +} + +func (suite *controllerSuite) SetupTest() { + suite.ctx = testutil.TestContext(suite.T()) + client := svctest.RunResourceService(suite.T(), types.Register, catalog.RegisterTypes) + suite.rt = controller.Runtime{ + Client: client, + Logger: testutil.Logger(suite.T()), + } + suite.client = rtest.NewClient(client) +} + +func (suite *controllerSuite) TestController() { + // TODO: tidy comment + // + // This test's purpose is to exercise the controller in a halfway realistic + // way. + + // Run the controller manager + mgr := controller.NewManager(suite.client, suite.rt.Logger) + mgr.Register(Controller()) + mgr.SetRaftLeader(true) + go mgr.Run(suite.ctx) + + // Start out by creating a single port service and let it create the + // default mesh config for tcp. + + serviceData := &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"api-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "tcp", Protocol: pbcatalog.Protocol_PROTOCOL_TCP}, + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + // {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + }, + } + + _ = rtest.Resource(catalog.ServiceType, "api"). + WithData(suite.T(), serviceData). + Write(suite.T(), suite.client) + + testutil.RunStep(suite.T(), "default tcp route", func(t *testing.T) { + // Check that the mesh config resource exists and it has one port that is the default. + computedRoutesID := rtest.Resource(types.ComputedRoutesType, "api").ID() + computedRoutes := suite.client.WaitForNewVersion(suite.T(), computedRoutesID, "") + + apiServiceRef := rtest.Resource(catalog.ServiceType, "api").Reference("") + + expect := &pbmesh.ComputedRoutes{ + PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ + "tcp": { + Config: &pbmesh.ComputedPortRoutes_Tcp{ + Tcp: &pbmesh.InterpretedTCPRoute{ + ParentRef: newParentRef(apiServiceRef, "tcp"), + Rules: []*pbmesh.InterpretedTCPRouteRule{{ + BackendRefs: []*pbmesh.InterpretedTCPBackendRef{{ + BackendTarget: "catalog.v1alpha1.Service/default.local.default/api?port=tcp", + }}, + }}, + }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + "catalog.v1alpha1.Service/default.local.default/api?port=tcp": { + BackendRef: newBackendRef(apiServiceRef, "tcp", ""), + Service: serviceData, + }, + }, + }, + }, + } + + suite.requireComputedRoutes(expect, computedRoutes) + }) + +} + +func newParentRef(ref *pbresource.Reference, port string) *pbmesh.ParentReference { + return &pbmesh.ParentReference{ + Ref: ref, + Port: port, + } +} + +func newBackendRef(ref *pbresource.Reference, port string, datacenter string) *pbmesh.BackendReference { + return &pbmesh.BackendReference{ + Ref: ref, + Port: port, + Datacenter: datacenter, + } +} + +func (suite *controllerSuite) requireComputedRoutes(expected *pbmesh.ComputedRoutes, resource *pbresource.Resource) { + suite.T().Helper() + var mc pbmesh.ComputedRoutes + require.NoError(suite.T(), resource.Data.UnmarshalTo(&mc)) + prototest.AssertDeepEqual(suite.T(), expected, &mc) +} + +func TestController(t *testing.T) { + suite.Run(t, new(controllerSuite)) +} diff --git a/internal/mesh/internal/controllers/routes/cross.go b/internal/mesh/internal/controllers/routes/cross.go new file mode 100644 index 00000000000..5c2f8e7fa10 --- /dev/null +++ b/internal/mesh/internal/controllers/routes/cross.go @@ -0,0 +1,103 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package routes + +import ( + "github.com/hashicorp/consul/internal/catalog" + "github.com/hashicorp/consul/internal/mesh/internal/controllers/routes/loader" + "github.com/hashicorp/consul/internal/mesh/internal/types" + "github.com/hashicorp/consul/internal/resource" + pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v1alpha1" + pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" + "github.com/hashicorp/consul/proto-public/pbresource" +) + +// ValidateXRouteReferences examines all of the ParentRefs and BackendRefs of +// xRoutes provided and issues status conditions if anything is unacceptable. +func ValidateXRouteReferences(related *loader.RelatedResources, pending PendingStatuses) { + related.WalkRoutes(func( + rk resource.ReferenceKey, + res *pbresource.Resource, + route types.XRouteData, + ) { + parentRefs := route.GetParentRefs() + backendRefs := route.GetUnderlyingBackendRefs() + + conditions := computeNewRouteRefConditions(related, res, parentRefs, backendRefs) + + pending.AddConditions(rk, res, conditions) + }) +} + +func computeNewRouteRefConditions( + related *loader.RelatedResources, + routeRes *pbresource.Resource, + parentRefs []*pbmesh.ParentReference, + backendRefs []*pbmesh.BackendReference, +) []*pbresource.Condition { + var conditions []*pbresource.Condition + + // TODO: handle port numbers here too? the virtual port + + for _, parentRef := range parentRefs { + if parentRef.Ref == nil || !resource.EqualType(parentRef.Ref.Type, catalog.ServiceType) { + continue + } + if parentRef.Ref.Section != "" { + continue + } + if svc := related.GetService(parentRef.Ref); svc != nil { + allowedPorts, hasMesh := getAllowedPorts(svc) + if hasMesh { + if parentRef.Port != "" { + if _, found := allowedPorts[parentRef.Port]; !found { + conditions = append(conditions, ConditionUnknownParentRefPort(parentRef.Ref, parentRef.Port)) + } + } + } else { + conditions = append(conditions, ConditionParentRefOutsideMesh(parentRef.Ref)) + } + } else { + conditions = append(conditions, ConditionMissingParentRef(parentRef.Ref)) + } + } + + for _, backendRef := range backendRefs { + if backendRef.Ref == nil || !resource.EqualType(backendRef.Ref.Type, catalog.ServiceType) { + continue + } + if backendRef.Ref.Section != "" { + continue + } + if svc := related.GetService(backendRef.Ref); svc != nil { + allowedPorts, hasMesh := getAllowedPorts(svc) + if hasMesh { + if backendRef.Port != "" { + if _, found := allowedPorts[backendRef.Port]; !found { + conditions = append(conditions, ConditionUnknownBackendRefPort(backendRef.Ref, backendRef.Port)) + } + } + } else { + conditions = append(conditions, ConditionBackendRefOutsideMesh(backendRef.Ref)) + } + } else { + conditions = append(conditions, ConditionMissingBackendRef(backendRef.Ref)) + } + } + + return conditions +} + +func getAllowedPorts(svc *types.DecodedService) (map[string]pbcatalog.Protocol, bool) { + allowedPortProtocols := make(map[string]pbcatalog.Protocol) + hasMesh := false + for _, port := range svc.Data.Ports { + if port.Protocol == pbcatalog.Protocol_PROTOCOL_MESH { + hasMesh = true + continue // skip + } + allowedPortProtocols[port.TargetPort] = port.Protocol + } + return allowedPortProtocols, hasMesh +} diff --git a/internal/mesh/internal/controllers/routes/generate.go b/internal/mesh/internal/controllers/routes/generate.go new file mode 100644 index 00000000000..453c0dc750d --- /dev/null +++ b/internal/mesh/internal/controllers/routes/generate.go @@ -0,0 +1,537 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package routes + +import ( + "context" + + "github.com/hashicorp/go-hclog" + + "github.com/hashicorp/consul/internal/catalog" + "github.com/hashicorp/consul/internal/mesh/internal/controllers/routes/loader" + "github.com/hashicorp/consul/internal/mesh/internal/types" + "github.com/hashicorp/consul/internal/resource" + pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v1alpha1" + pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" + "github.com/hashicorp/consul/proto-public/pbresource" +) + +// GenerateComputedRoutes walks a set of related resources and assembles them +// into one effective easy-to-consume ComputedRoutes result. +// +// Any status conditions generated during traversal can be queued for +// persistence using the PendingStatuses map. +// +// This should not internally generate, nor return any errors. +func GenerateComputedRoutes( + ctx context.Context, + logger hclog.Logger, + related *loader.RelatedResources, + pending PendingStatuses, +) []*ComputedRoutesResult { + out := make([]*ComputedRoutesResult, 0, len(related.ComputedRoutesList)) + for _, computedRoutesID := range related.ComputedRoutesList { + out = append(out, compile(ctx, logger, related, computedRoutesID, pending)) + } + return out +} + +type ComputedRoutesResult struct { + ID *pbresource.ID + OwnerID *pbresource.ID + // If Data is empty it means delete if exists. + Data *pbmesh.ComputedRoutes +} + +func compile( + ctx context.Context, + logger hclog.Logger, + related *loader.RelatedResources, + computedRoutesID *pbresource.ID, + pending PendingStatuses, +) *ComputedRoutesResult { + logger = logger.With("mesh-config-id", resource.IDToString(computedRoutesID)) + + // There is one mesh config for the entire service (perfect name alignment). + // + // All ports are embedded within. + + parentServiceID := &pbresource.ID{ + Type: catalog.ServiceType, + Tenancy: computedRoutesID.Tenancy, + Name: computedRoutesID.Name, + } + + parentServiceRef := resource.Reference(parentServiceID, "") + + parentServiceDec := related.GetService(parentServiceID) + + allowedPortProtocols := make(map[string]pbcatalog.Protocol) + inMesh := false + if parentServiceDec != nil { + parentServiceID = parentServiceDec.Resource.Id // get ULID out of it + + for _, port := range parentServiceDec.Data.Ports { + if port.Protocol == pbcatalog.Protocol_PROTOCOL_MESH { + inMesh = true + continue // skip + } + allowedPortProtocols[port.TargetPort] = port.Protocol + } + } + + meshConfig := &pbmesh.ComputedRoutes{ + PortedConfigs: make(map[string]*pbmesh.ComputedPortRoutes), + } + + // Visit all of the routes relevant to this mesh config. + routeNodesByPort := make(map[string][]*inputRouteNode) + related.WalkRoutesForParentRef(parentServiceRef, func( + rk resource.ReferenceKey, + res *pbresource.Resource, + xroute types.XRouteData, + ) { + port := "" + found := false + for _, ref := range xroute.GetParentRefs() { + if resource.ReferenceOrIDMatch(ref.Ref, parentServiceRef) { + found = true + port = ref.Port + break + } + } + + if !found { + return // not relevant to this mesh config + + } + + var node *inputRouteNode + switch route := xroute.(type) { + case *pbmesh.HTTPRoute: + node = compileHTTPRouteNode(port, res, route) + case *pbmesh.GRPCRoute: + node = compileGRPCRouteNode(port, res, route) + case *pbmesh.TCPRoute: + node = compileTCPRouteNode(port, res, route) + default: + return // unknown xroute type (impossible) + } + + // Check if the user provided port is actually valid. + if port != "" { + if _, ok := allowedPortProtocols[port]; !ok { + for _, details := range node.NewTargets { + details.NullRouteTraffic = true + } + } + } + + if node.ParentPort == "" { + // Do a port explosion. + for port := range allowedPortProtocols { + nodeCopy := node.Clone() + nodeCopy.ParentPort = port + routeNodesByPort[nodeCopy.ParentPort] = append(routeNodesByPort[nodeCopy.ParentPort], nodeCopy) + } + } else { + routeNodesByPort[node.ParentPort] = append(routeNodesByPort[node.ParentPort], node) + } + }) + + // Fill in defaults where there was no xroute defined + for port, protocol := range allowedPortProtocols { + // always add the parent as a possible target due to catch-all + defaultBackendRef := &pbmesh.BackendReference{ + Ref: parentServiceRef, + Port: port, + } + + routeNode, ok := routeNodesByPort[port] + if ok { + // TODO: ignore this whole section? + + // TODO: figure out how to add the catchall route/match section like in v1. + // + // Just add it to one of them. + defaultBackendTarget := routeNode[0].AddTarget(defaultBackendRef, &pbmesh.BackendTargetDetails{ + BackendRef: defaultBackendRef, + // TODO + }) + _ = defaultBackendTarget + } else { + var typ *pbresource.Type + switch protocol { + case pbcatalog.Protocol_PROTOCOL_HTTP2: + fallthrough // to HTTP + case pbcatalog.Protocol_PROTOCOL_HTTP: + typ = types.HTTPRouteType + case pbcatalog.Protocol_PROTOCOL_GRPC: + typ = types.GRPCRouteType + case pbcatalog.Protocol_PROTOCOL_TCP: + fallthrough // to default + default: + typ = types.TCPRouteType + } + + routeNode := createDefaultRouteNode(parentServiceRef, port, typ) + + routeNodesByPort[port] = append(routeNodesByPort[port], routeNode) + } + } + + // First sort the input routes by the final criteria, so we can let the + // stable sort take care of the ultimate tiebreakers. + for port, routeNodes := range routeNodesByPort { + gammaInitialSortWrappedRoutes(routeNodes) + + // Now that they are sorted by age and name, we can figure out which + // xRoute should apply to each port (the first). + var top *inputRouteNode + for i, routeNode := range routeNodes { + if i == 0 { + top = routeNode + continue + } + if top.RouteType != routeNode.RouteType { + res := routeNode.OriginalResource() + pending.AddConditions(resource.NewReferenceKey(res.Id), res, []*pbresource.Condition{ + ConditionConflictNotBoundToParentRef( + parentServiceRef, + port, + top.RouteType, + ), + }) + continue + } + top.AppendRulesFrom(routeNode) + top.AddTargetsFrom(routeNode) + } + + // TODO: default route values by port by protocol? + + // Now we can do the big sort. + gammaSortRouteRules(top) + + // Inject catch-all rules to ensure stray requests will ultimately end + // up routing to the parent ref. + if !top.Default { + defaultRouteNode := createDefaultRouteNode(parentServiceRef, port, top.RouteType) + top.AppendRulesFrom(defaultRouteNode) + top.AddTargetsFrom(defaultRouteNode) + } + + mc := &pbmesh.ComputedPortRoutes{ + Targets: top.NewTargets, + } + parentRef := &pbmesh.ParentReference{ + Ref: parentServiceRef, + Port: port, + } + + switch { + case resource.EqualType(top.RouteType, types.HTTPRouteType): + mc.Config = &pbmesh.ComputedPortRoutes_Http{ + Http: &pbmesh.InterpretedHTTPRoute{ + ParentRef: parentRef, + Rules: top.HTTPRules, + }, + } + // TODO + case resource.EqualType(top.RouteType, types.GRPCRouteType): + mc.Config = &pbmesh.ComputedPortRoutes_Grpc{ + Grpc: &pbmesh.InterpretedGRPCRoute{ + ParentRef: parentRef, + Rules: top.GRPCRules, + }, + } + // TODO + case resource.EqualType(top.RouteType, types.TCPRouteType): + mc.Config = &pbmesh.ComputedPortRoutes_Tcp{ + Tcp: &pbmesh.InterpretedTCPRoute{ + ParentRef: parentRef, + Rules: top.TCPRules, + }, + } + // TODO + default: + panic("impossible") + } + + meshConfig.PortedConfigs[port] = mc + + for _, details := range mc.Targets { + svcRef := details.BackendRef.Ref + + svc := related.GetService(svcRef) + failoverPolicy := related.GetFailoverPolicyForService(svcRef) + destConfig := related.GetDestinationPolicy(svcRef) + + if svc == nil { + details.NullRouteTraffic = true + } else { + details.Service = svc.Data + + if failoverPolicy != nil { + details.FailoverPolicy = catalog.SimplifyFailoverPolicy( + svc.Data, + failoverPolicy.Data, + ) + } + if destConfig != nil { + details.DestinationPolicy = destConfig.Data + } + } + } + + // TODO: Create derived composite routing instruction. + meshConfig.PortedConfigs[port] = mc + } + + if !inMesh { + meshConfig = nil + } + + return &ComputedRoutesResult{ + ID: computedRoutesID, + OwnerID: parentServiceID, + Data: meshConfig, + } +} + +func compileHTTPRouteNode( + port string, + res *pbresource.Resource, + route *pbmesh.HTTPRoute, +) *inputRouteNode { + node := newInputRouteNode(port) + + dec := &types.DecodedHTTPRoute{ + Resource: res, + Data: route, + } + + node.RouteType = types.HTTPRouteType + node.HTTP = dec + node.HTTPRules = make([]*pbmesh.InterpretedHTTPRouteRule, 0, len(route.Rules)) + for _, rule := range route.Rules { + irule := &pbmesh.InterpretedHTTPRouteRule{ + Matches: protoSliceClone(rule.Matches), + Filters: protoSliceClone(rule.Filters), + BackendRefs: make([]*pbmesh.InterpretedHTTPBackendRef, 0, len(rule.BackendRefs)), // TODO: populate + Timeouts: rule.Timeouts, + Retries: rule.Retries, + } + + // https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteRule + // + // If no matches are specified, the default is a prefix path match + // on “/”, which has the effect of matching every HTTP request. + if len(irule.Matches) == 0 { + irule.Matches = []*pbmesh.HTTPRouteMatch{{ + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, + Value: "/", + }, + }} + } + for _, match := range irule.Matches { + if match.Path == nil { + // Path specifies a HTTP request path matcher. If this + // field is not specified, a default prefix match on + // the “/” path is provided. + match.Path = &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, + Value: "/", + } + } + } + + for _, backendRef := range rule.BackendRefs { + details := &pbmesh.BackendTargetDetails{ + BackendRef: backendRef.BackendRef, + // TODO + } + key := node.AddTarget(backendRef.BackendRef, details) + ibr := &pbmesh.InterpretedHTTPBackendRef{ + BackendTarget: key, + Weight: backendRef.Weight, + Filters: backendRef.Filters, + } + irule.BackendRefs = append(irule.BackendRefs, ibr) + } + + node.HTTPRules = append(node.HTTPRules, irule) + } + + // TODO: finish populating backendrefs using parentrefs if target unspecified + return node +} + +func compileGRPCRouteNode( + port string, + res *pbresource.Resource, + route *pbmesh.GRPCRoute, +) *inputRouteNode { + node := newInputRouteNode(port) + dec := &types.DecodedGRPCRoute{ + Resource: res, + Data: route, + } + + node.RouteType = types.GRPCRouteType + node.GRPC = dec + node.GRPCRules = make([]*pbmesh.InterpretedGRPCRouteRule, 0, len(route.Rules)) + for _, rule := range route.Rules { + irule := &pbmesh.InterpretedGRPCRouteRule{ + Matches: protoSliceClone(rule.Matches), + Filters: protoSliceClone(rule.Filters), + BackendRefs: make([]*pbmesh.InterpretedGRPCBackendRef, 0, len(rule.BackendRefs)), // TODO: populate + Timeouts: rule.Timeouts, + Retries: rule.Retries, + } + + // https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRouteRule + // + // If no matches are specified, the implementation MUST match every gRPC request. + if len(irule.Matches) == 0 { + irule.Matches = []*pbmesh.GRPCRouteMatch{{}} + } + + for _, backendRef := range rule.BackendRefs { + details := &pbmesh.BackendTargetDetails{ + BackendRef: backendRef.BackendRef, + // TODO + } + key := node.AddTarget(backendRef.BackendRef, details) + ibr := &pbmesh.InterpretedGRPCBackendRef{ + BackendTarget: key, + Weight: backendRef.Weight, + Filters: backendRef.Filters, + } + irule.BackendRefs = append(irule.BackendRefs, ibr) + } + + node.GRPCRules = append(node.GRPCRules, irule) + } + + // TODO: finish populating backendrefs using parentrefs if target unspecified + return node +} + +func compileTCPRouteNode( + port string, + res *pbresource.Resource, + route *pbmesh.TCPRoute, +) *inputRouteNode { + node := newInputRouteNode(port) + dec := &types.DecodedTCPRoute{ + Resource: res, + Data: route, + } + + node.RouteType = types.TCPRouteType + node.TCP = dec + node.TCPRules = make([]*pbmesh.InterpretedTCPRouteRule, 0, len(route.Rules)) + for _, rule := range route.Rules { + irule := &pbmesh.InterpretedTCPRouteRule{ + BackendRefs: make([]*pbmesh.InterpretedTCPBackendRef, 0, len(rule.BackendRefs)), // TODO: populate + } + + // https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.TCPRoute + + for _, backendRef := range rule.BackendRefs { + details := &pbmesh.BackendTargetDetails{ + BackendRef: backendRef.BackendRef, + // TODO + } + key := node.AddTarget(backendRef.BackendRef, details) + ibr := &pbmesh.InterpretedTCPBackendRef{ + BackendTarget: key, + Weight: backendRef.Weight, + } + irule.BackendRefs = append(irule.BackendRefs, ibr) + } + + node.TCPRules = append(node.TCPRules, irule) + } + + // TODO: finish populating backendrefs using parentrefs if target unspecified + return node +} + +func createDefaultRouteNode( + parentServiceRef *pbresource.Reference, + port string, + typ *pbresource.Type, +) *inputRouteNode { + // always add the parent as a possible target due to catch-all + defaultBackendRef := &pbmesh.BackendReference{ + Ref: parentServiceRef, + Port: port, + } + + routeNode := newInputRouteNode(port) + + defaultBackendTarget := routeNode.AddTarget(defaultBackendRef, &pbmesh.BackendTargetDetails{ + BackendRef: defaultBackendRef, + // TODO + }) + switch { + case resource.EqualType(types.HTTPRouteType, typ): + setupDefaultHTTPRouteNode(routeNode, defaultBackendTarget) + case resource.EqualType(types.GRPCRouteType, typ): + setupDefaultGRPCRouteNode(routeNode, defaultBackendTarget) + case resource.EqualType(types.TCPRouteType, typ): + fallthrough + default: + setupDefaultTCPRouteNode(routeNode, defaultBackendTarget) + } + + routeNode.Default = true + return routeNode +} + +func setupDefaultHTTPRouteNode( + routeNode *inputRouteNode, + defaultBackendTarget string, +) { + routeNode.RouteType = types.HTTPRouteType + routeNode.HTTPRules = []*pbmesh.InterpretedHTTPRouteRule{{ + Matches: []*pbmesh.HTTPRouteMatch{{ + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, + Value: "/", + }, + }}, + BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendTarget: defaultBackendTarget, + }}, + }} +} + +func setupDefaultGRPCRouteNode( + routeNode *inputRouteNode, + defaultBackendTarget string, +) { + routeNode.RouteType = types.GRPCRouteType + routeNode.GRPCRules = []*pbmesh.InterpretedGRPCRouteRule{{ + Matches: []*pbmesh.GRPCRouteMatch{{}}, + BackendRefs: []*pbmesh.InterpretedGRPCBackendRef{{ + BackendTarget: defaultBackendTarget, + }}, + }} +} + +func setupDefaultTCPRouteNode( + routeNode *inputRouteNode, + defaultBackendTarget string, +) { + routeNode.RouteType = types.TCPRouteType + routeNode.TCPRules = []*pbmesh.InterpretedTCPRouteRule{{ + BackendRefs: []*pbmesh.InterpretedTCPBackendRef{{ + BackendTarget: defaultBackendTarget, + }}, + }} +} diff --git a/internal/mesh/internal/controllers/routes/intermediate.go b/internal/mesh/internal/controllers/routes/intermediate.go new file mode 100644 index 00000000000..820d06593cf --- /dev/null +++ b/internal/mesh/internal/controllers/routes/intermediate.go @@ -0,0 +1,108 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package routes + +import ( + "google.golang.org/protobuf/proto" + + "github.com/hashicorp/consul/internal/mesh/internal/types" + "github.com/hashicorp/consul/internal/resource" + pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" + "github.com/hashicorp/consul/proto-public/pbresource" +) + +// TODO: move to a new package? compiler? + +// inputRouteNode is a dressed up version of an XRoute meant as working state +// for one pass of the compilation procedure. +type inputRouteNode struct { + ParentPort string // always set + + // only one of these can be set to non-empty + HTTPRules []*pbmesh.InterpretedHTTPRouteRule + GRPCRules []*pbmesh.InterpretedGRPCRouteRule + TCPRules []*pbmesh.InterpretedTCPRouteRule + + RouteType *pbresource.Type + Default bool + + NewTargets map[string]*pbmesh.BackendTargetDetails + + // These three are the originals. If something needs customization for + // compilation a shadow field will exist on this enclosing object instead. + // + // only one of these can be set to non-nil + HTTP *types.DecodedHTTPRoute + GRPC *types.DecodedGRPCRoute + TCP *types.DecodedTCPRoute +} + +func newInputRouteNode(port string) *inputRouteNode { + return &inputRouteNode{ + ParentPort: port, + NewTargets: make(map[string]*pbmesh.BackendTargetDetails), + } +} + +func (n *inputRouteNode) AddTarget(backendRef *pbmesh.BackendReference, data *pbmesh.BackendTargetDetails) string { + n.Default = false + key := types.BackendRefToString(backendRef) + + if _, ok := n.NewTargets[key]; !ok { + n.NewTargets[key] = data + } + + return key +} + +func (n *inputRouteNode) AddTargetsFrom(next *inputRouteNode) { + n.Default = false + for key, details := range next.NewTargets { + if _, ok := n.NewTargets[key]; !ok { + n.NewTargets[key] = details // add if not already there + } + } +} + +func (n *inputRouteNode) AppendRulesFrom(next *inputRouteNode) { + n.Default = false + switch { + case resource.EqualType(n.RouteType, types.HTTPRouteType): + n.HTTPRules = append(n.HTTPRules, next.HTTPRules...) + case resource.EqualType(n.RouteType, types.GRPCRouteType): + n.GRPCRules = append(n.GRPCRules, next.GRPCRules...) + case resource.EqualType(n.RouteType, types.TCPRouteType): + n.TCPRules = append(n.TCPRules, next.TCPRules...) + default: + panic("impossible") + } +} + +func (n *inputRouteNode) OriginalResource() *pbresource.Resource { + switch { + case n.HTTP != nil: + return n.HTTP.GetResource() + case n.GRPC != nil: + return n.GRPC.GetResource() + case n.TCP != nil: + return n.TCP.GetResource() + default: + panic("impossible") + } +} + +func (n *inputRouteNode) Clone() *inputRouteNode { + n2 := *n + n2.HTTPRules = protoSliceClone(n.HTTPRules) + n2.GRPCRules = protoSliceClone(n.GRPCRules) + n2.TCPRules = protoSliceClone(n.TCPRules) + + n2.NewTargets = make(map[string]*pbmesh.BackendTargetDetails) + for key, details := range n.NewTargets { + n2.NewTargets[key] = proto.Clone(details).(*pbmesh.BackendTargetDetails) + } + + // only shallow copy the protobuf stuff since we don't touch it + return &n2 +} diff --git a/internal/mesh/internal/controllers/routes/loader/loader.go b/internal/mesh/internal/controllers/routes/loader/loader.go new file mode 100644 index 00000000000..109e2b3be31 --- /dev/null +++ b/internal/mesh/internal/controllers/routes/loader/loader.go @@ -0,0 +1,311 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package loader + +import ( + "context" + "fmt" + + "github.com/hashicorp/go-hclog" + + "github.com/hashicorp/consul/internal/catalog" + "github.com/hashicorp/consul/internal/mesh/internal/controllers/routes/xroutemapper" + "github.com/hashicorp/consul/internal/mesh/internal/types" + "github.com/hashicorp/consul/internal/resource" + "github.com/hashicorp/consul/proto-public/pbresource" +) + +type loader struct { + mapper *xroutemapper.Mapper + + mem *memoizingLoader + + // output var + out *RelatedResources + + // working state + mcToLoad map[resource.ReferenceKey]struct{} + mcDone map[resource.ReferenceKey]struct{} +} + +func LoadResourcesForComputedRoutes( + ctx context.Context, + loggerFor func(*pbresource.ID) hclog.Logger, + client pbresource.ResourceServiceClient, + mapper *xroutemapper.Mapper, + computedRoutesID *pbresource.ID, +) (*RelatedResources, error) { + if loggerFor == nil { + loggerFor = func(_ *pbresource.ID) hclog.Logger { + return hclog.NewNullLogger() + } + } + loader := &loader{ + mapper: mapper, + mem: newMemoizingLoader(client), + mcToLoad: make(map[resource.ReferenceKey]struct{}), + mcDone: make(map[resource.ReferenceKey]struct{}), + } + + if err := loader.load(ctx, loggerFor, client, computedRoutesID); err != nil { + return nil, err + } + + return loader.out, nil +} + +func (l *loader) requestLoad(computedRoutesID *pbresource.ID) { + if !resource.EqualType(computedRoutesID.Type, types.ComputedRoutesType) { + panic("input must be a ComputedRoutes type") + } + rk := resource.NewReferenceKey(computedRoutesID) + + if _, done := l.mcDone[rk]; done { + return + } + l.mcToLoad[rk] = struct{}{} +} + +func (l *loader) markLoaded(computedRoutesID *pbresource.ID) { + if !resource.EqualType(computedRoutesID.Type, types.ComputedRoutesType) { + panic("input must be a ComputedRoutes type") + } + rk := resource.NewReferenceKey(computedRoutesID) + + l.mcDone[rk] = struct{}{} + delete(l.mcToLoad, rk) +} + +func (l *loader) nextRequested() *pbresource.ID { + for rk := range l.mcToLoad { + return rk.ToID() + } + return nil +} + +func (l *loader) load( + ctx context.Context, + loggerFor func(*pbresource.ID) hclog.Logger, + client pbresource.ResourceServiceClient, + computedRoutesID *pbresource.ID, +) error { + l.out = NewRelatedResources() + + // Seed the graph fetch for our starting position. + l.requestLoad(computedRoutesID) + + for { + mcID := l.nextRequested() + if mcID == nil { + break + } + + if err := l.loadOne(ctx, loggerFor, client, mcID); err != nil { + return err + } + } + + return nil +} + +func (l *loader) loadOne( + ctx context.Context, + loggerFor func(*pbresource.ID) hclog.Logger, + client pbresource.ResourceServiceClient, + computedRoutesID *pbresource.ID, +) error { + logger := loggerFor(computedRoutesID) + + // There is one mesh config for the entire service (perfect name alignment). + // + // All ports are embedded within. + + parentServiceID := changeResourceType(computedRoutesID, catalog.ServiceType) + parentServiceRef := resource.Reference(parentServiceID, "") + + if err := l.loadUpstreamService(ctx, logger, parentServiceID); err != nil { + return err + } + + if err := l.gatherXRoutesAsInput(ctx, logger, computedRoutesID, parentServiceRef); err != nil { + return err + } + + l.out.AddComputedRoutesIDs(computedRoutesID) + + l.markLoaded(computedRoutesID) + + return nil +} + +func (l *loader) gatherXRoutesAsInput( + ctx context.Context, + logger hclog.Logger, + computedRoutesID *pbresource.ID, + parentServiceRef *pbresource.Reference, +) error { + routeIDs := l.mapper.RouteIDsByParentServiceRef(parentServiceRef) + + // read the xRoutes + for _, routeID := range routeIDs { + switch { + case resource.EqualType(routeID.Type, types.HTTPRouteType): + route, err := l.mem.GetHTTPRoute(ctx, routeID) + if err != nil { + return fmt.Errorf("the resource service has returned an unexpected error loading %s: %w", routeID, err) + } + var routeData types.XRouteData + if route != nil { + routeData = route.Data + } + err = l.gatherSingleXRouteAsInput(ctx, logger, computedRoutesID, routeID, routeData, func() { + l.out.AddResource(route) + }) + if err != nil { + return fmt.Errorf("the resource service has returned an unexpected error loading %s: %w", routeID, err) + } + case resource.EqualType(routeID.Type, types.GRPCRouteType): + route, err := l.mem.GetGRPCRoute(ctx, routeID) + if err != nil { + return fmt.Errorf("the resource service has returned an unexpected error loading %s: %w", routeID, err) + } + var routeData types.XRouteData + if route != nil { + routeData = route.Data + } + err = l.gatherSingleXRouteAsInput(ctx, logger, computedRoutesID, routeID, routeData, func() { + l.out.AddResource(route) + }) + if err != nil { + return fmt.Errorf("the resource service has returned an unexpected error loading %s: %w", routeID, err) + } + case resource.EqualType(routeID.Type, types.TCPRouteType): + route, err := l.mem.GetTCPRoute(ctx, routeID) + if err != nil { + return fmt.Errorf("the resource service has returned an unexpected error loading %s: %w", routeID, err) + } + var routeData types.XRouteData + if route != nil { + routeData = route.Data + } + err = l.gatherSingleXRouteAsInput(ctx, logger, computedRoutesID, routeID, routeData, func() { + l.out.AddResource(route) + }) + if err != nil { + return fmt.Errorf("the resource service has returned an unexpected error loading %s: %w", routeID, err) + } + default: + logger.Warn("skipping xRoute reference of unknown type", "ID", resource.IDToString(routeID)) + continue + } + } + + return nil +} + +func (l *loader) loadUpstreamService( + ctx context.Context, + logger hclog.Logger, + svcID *pbresource.ID, +) error { + logger = logger.With("service-id", resource.IDToString(svcID)) + + service, err := l.mem.GetService(ctx, svcID) + if err != nil { + logger.Error("error retrieving the service", "serviceID", svcID, "error", err) + return err + } + if service != nil { + l.out.AddResource(service) + + failoverPolicyID := changeResourceType(svcID, catalog.FailoverPolicyType) + failoverPolicy, err := l.mem.GetFailoverPolicy(ctx, failoverPolicyID) + if err != nil { + logger.Error("error retrieving the failover policy", "failoverPolicyID", failoverPolicyID, "error", err) + return err + } + if failoverPolicy != nil { + l.mapper.TrackFailoverPolicy(failoverPolicy) + l.out.AddResource(failoverPolicy) + + destRefs := failoverPolicy.Data.GetUnderlyingDestinationRefs() + for _, destRef := range destRefs { + destID := resource.IDFromReference(destRef) + + failService, err := l.mem.GetService(ctx, destID) + if err != nil { + logger.Error("error retrieving a failover destination service", + "serviceID", destID, "error", err) + return err + } + if failService != nil { + l.out.AddResource(failService) + } + } + } else { + l.mapper.UntrackFailoverPolicy(failoverPolicyID) + } + + destPolicyID := changeResourceType(svcID, types.DestinationPolicyType) + destPolicy, err := l.mem.GetDestinationPolicy(ctx, destPolicyID) + if err != nil { + logger.Error("error retrieving the destination config", "destPolicyID", destPolicyID, "error", err) + return err + } + if destPolicy != nil { + l.out.AddResource(destPolicy) + } + } + + return nil +} + +func (l *loader) gatherSingleXRouteAsInput( + ctx context.Context, + logger hclog.Logger, + computedRoutesID *pbresource.ID, + routeID *pbresource.ID, + route types.XRouteData, + relatedRouteCaptureFn func(), +) error { + if route == nil { + logger.Trace("XRoute has been deleted") + l.mapper.UntrackXRoute(routeID) + return nil + } + l.mapper.TrackXRoute(routeID, route) + + relatedRouteCaptureFn() + + for _, parentRef := range route.GetParentRefs() { + if types.IsServiceType(parentRef.Ref.Type) { + parentMeshConfigID := &pbresource.ID{ + Type: types.ComputedRoutesType, + Tenancy: parentRef.Ref.Tenancy, + Name: parentRef.Ref.Name, + } + // Note: this will only schedule things to load that have not already been loaded + l.requestLoad(parentMeshConfigID) + } + } + + for _, backendRef := range route.GetUnderlyingBackendRefs() { + if types.IsServiceType(backendRef.Ref.Type) { + svcID := resource.IDFromReference(backendRef.Ref) + if err := l.loadUpstreamService(ctx, logger, svcID); err != nil { + return err + } + } + } + + return nil +} + +func changeResourceType(id *pbresource.ID, newType *pbresource.Type) *pbresource.ID { + return &pbresource.ID{ + Type: newType, + Tenancy: id.Tenancy, + Name: id.Name, + } +} diff --git a/internal/mesh/internal/controllers/routes/loader/loader_test.go b/internal/mesh/internal/controllers/routes/loader/loader_test.go new file mode 100644 index 00000000000..f1910d1c0ef --- /dev/null +++ b/internal/mesh/internal/controllers/routes/loader/loader_test.go @@ -0,0 +1,400 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package loader + +import ( + "testing" + "time" + + "github.com/hashicorp/go-hclog" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/types/known/durationpb" + + svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing" + "github.com/hashicorp/consul/internal/catalog" + "github.com/hashicorp/consul/internal/controller" + "github.com/hashicorp/consul/internal/mesh/internal/controllers/routes/xroutemapper" + "github.com/hashicorp/consul/internal/mesh/internal/types" + "github.com/hashicorp/consul/internal/resource" + rtest "github.com/hashicorp/consul/internal/resource/resourcetest" + pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v1alpha1" + pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" + "github.com/hashicorp/consul/proto-public/pbresource" + "github.com/hashicorp/consul/proto/private/prototest" + "github.com/hashicorp/consul/sdk/testutil" +) + +func TestLoadResourcesForComputedRoutes(t *testing.T) { + ctx := testutil.TestContext(t) + rclient := svctest.RunResourceService(t, types.Register, catalog.RegisterTypes) + rt := controller.Runtime{ + Client: rclient, + Logger: testutil.Logger(t), + } + client := rtest.NewClient(rclient) + + loggerFor := func(id *pbresource.ID) hclog.Logger { + return rt.Logger.With("resource-id", resource.IDToString(id)) + } + + mapper := xroutemapper.New() + + deleteRes := func(id *pbresource.ID, untrack bool) { + client.MustDelete(t, id) + if untrack { + switch { + case types.IsRouteType(id.Type): + mapper.UntrackXRoute(id) + case types.IsFailoverPolicyType(id.Type): + mapper.UntrackFailoverPolicy(id) + } + } + } + + writeHTTP := func(name string, data *pbmesh.HTTPRoute) *types.DecodedHTTPRoute { + res := rtest.Resource(types.HTTPRouteType, name). + WithData(t, data). + Write(t, client) + mapper.TrackXRoute(res.Id, data) + dec, err := resource.Decode[pbmesh.HTTPRoute, *pbmesh.HTTPRoute](res) + require.NoError(t, err) + return dec + } + + writeGRPC := func(name string, data *pbmesh.GRPCRoute) *types.DecodedGRPCRoute { + res := rtest.Resource(types.GRPCRouteType, name). + WithData(t, data). + Write(t, client) + mapper.TrackXRoute(res.Id, data) + dec, err := resource.Decode[pbmesh.GRPCRoute, *pbmesh.GRPCRoute](res) + require.NoError(t, err) + return dec + } + _ = writeGRPC // TODO + + writeTCP := func(name string, data *pbmesh.TCPRoute) *types.DecodedTCPRoute { + res := rtest.Resource(types.TCPRouteType, name). + WithData(t, data). + Write(t, client) + mapper.TrackXRoute(res.Id, data) + dec, err := resource.Decode[pbmesh.TCPRoute, *pbmesh.TCPRoute](res) + require.NoError(t, err) + return dec + } + _ = writeTCP // TODO + + writeDestPolicy := func(name string, data *pbmesh.DestinationPolicy) *types.DecodedDestinationPolicy { + res := rtest.Resource(types.DestinationPolicyType, name). + WithData(t, data). + Write(t, client) + dec, err := resource.Decode[pbmesh.DestinationPolicy, *pbmesh.DestinationPolicy](res) + require.NoError(t, err) + return dec + } + + writeFailover := func(name string, data *pbcatalog.FailoverPolicy) *types.DecodedFailoverPolicy { + res := rtest.Resource(catalog.FailoverPolicyType, name). + WithData(t, data). + Write(t, client) + dec, err := resource.Decode[pbcatalog.FailoverPolicy, *pbcatalog.FailoverPolicy](res) + require.NoError(t, err) + return dec + } + + writeService := func(name string, data *pbcatalog.Service) *types.DecodedService { + res := rtest.Resource(catalog.ServiceType, name). + WithData(t, data). + Write(t, client) + dec, err := resource.Decode[pbcatalog.Service, *pbcatalog.Service](res) + require.NoError(t, err) + return dec + } + + ///////////////////////////////////// + + // Init some port-aligned services. + apiSvc := writeService("api", &pbcatalog.Service{ + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + {TargetPort: "tcp", Protocol: pbcatalog.Protocol_PROTOCOL_TCP}, + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + }, + }) + adminSvc := writeService("admin", &pbcatalog.Service{ + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + {TargetPort: "tcp", Protocol: pbcatalog.Protocol_PROTOCOL_TCP}, + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + }, + }) + fooSvc := writeService("foo", &pbcatalog.Service{ + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + {TargetPort: "tcp", Protocol: pbcatalog.Protocol_PROTOCOL_TCP}, + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + }, + }) + barSvc := writeService("bar", &pbcatalog.Service{ + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + {TargetPort: "tcp", Protocol: pbcatalog.Protocol_PROTOCOL_TCP}, + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + }, + }) + + apiRoutesID := &pbresource.ID{ + Type: types.ComputedRoutesType, + Tenancy: defaultTenancy(), + Name: "api", + } + adminRoutesID := &pbresource.ID{ + Type: types.ComputedRoutesType, + Tenancy: defaultTenancy(), + Name: "admin", + } + + testutil.RunStep(t, "only service", func(t *testing.T) { + out, err := LoadResourcesForComputedRoutes(ctx, loggerFor, rt.Client, mapper, apiRoutesID) + require.NoError(t, err) + + prototest.AssertDeepEqual(t, NewRelatedResources().AddResources( + apiSvc, + ).AddComputedRoutesIDs(apiRoutesID), out) + require.Equal(t, doubleMap(t /* empty */), out.RoutesByParentRef) + }) + + // Write one silly http route + route1 := writeHTTP("api-route1", &pbmesh.HTTPRoute{ + ParentRefs: []*pbmesh.ParentReference{{ + Ref: newRef(catalog.ServiceType, "api"), + // all ports + }}, + }) + + testutil.RunStep(t, "one silly route", func(t *testing.T) { + out, err := LoadResourcesForComputedRoutes(ctx, loggerFor, rt.Client, mapper, apiRoutesID) + require.NoError(t, err) + + prototest.AssertDeepEqual(t, NewRelatedResources().AddResources( + apiSvc, + route1, + ).AddComputedRoutesIDs(apiRoutesID), out) + require.Equal(t, doubleMap(t, + apiSvc, route1, + ), out.RoutesByParentRef) + }) + + // add a second route that is more interesting and is TCP + route2 := writeTCP("api-route2", &pbmesh.TCPRoute{ + ParentRefs: []*pbmesh.ParentReference{{ + Ref: newRef(catalog.ServiceType, "api"), + // all ports + }}, + Rules: []*pbmesh.TCPRouteRule{{ + BackendRefs: []*pbmesh.TCPBackendRef{ + { + BackendRef: &pbmesh.BackendReference{ + Ref: newRef(catalog.ServiceType, "foo"), + }, + Weight: 30, + }, + { + BackendRef: &pbmesh.BackendReference{ + Ref: newRef(catalog.ServiceType, "bar"), + }, + Weight: 70, + }, + }, + }}, + }) + + testutil.RunStep(t, "two routes", func(t *testing.T) { + out, err := LoadResourcesForComputedRoutes(ctx, loggerFor, rt.Client, mapper, apiRoutesID) + require.NoError(t, err) + + prototest.AssertDeepEqual(t, NewRelatedResources().AddResources( + apiSvc, + fooSvc, + barSvc, + route1, + route2, + ).AddComputedRoutesIDs(apiRoutesID), out) + require.Equal(t, doubleMap(t, + apiSvc, route1, + apiSvc, route2, + ), out.RoutesByParentRef) + }) + + // update the first to overlap with the second + route1 = writeHTTP("api-route1", &pbmesh.HTTPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + { + Ref: newRef(catalog.ServiceType, "api"), + // all ports + }, + { + Ref: newRef(catalog.ServiceType, "admin"), + // all ports + }, + }, + }) + + testutil.RunStep(t, "two overlapping mesh configs", func(t *testing.T) { + out, err := LoadResourcesForComputedRoutes(ctx, loggerFor, rt.Client, mapper, apiRoutesID) + require.NoError(t, err) + + prototest.AssertDeepEqual(t, NewRelatedResources().AddResources( + apiSvc, + fooSvc, + barSvc, + adminSvc, + route1, + route2, + ).AddComputedRoutesIDs(apiRoutesID, adminRoutesID), out) + require.Equal(t, doubleMap(t, + apiSvc, route1, + apiSvc, route2, + adminSvc, route1, + ), out.RoutesByParentRef) + }) + + // add a third (GRPC) that overlaps them both + + route3 := writeGRPC("api-route3", &pbmesh.GRPCRoute{ + ParentRefs: []*pbmesh.ParentReference{ + { + Ref: newRef(catalog.ServiceType, "api"), + // all ports + }, + { + Ref: newRef(catalog.ServiceType, "admin"), + // all ports + }, + }, + }) + + testutil.RunStep(t, "three overlapping mesh configs", func(t *testing.T) { + out, err := LoadResourcesForComputedRoutes(ctx, loggerFor, rt.Client, mapper, apiRoutesID) + require.NoError(t, err) + + prototest.AssertDeepEqual(t, NewRelatedResources().AddResources( + apiSvc, + fooSvc, + barSvc, + adminSvc, + route1, + route2, + route3, + ).AddComputedRoutesIDs(apiRoutesID, adminRoutesID), out) + require.Equal(t, doubleMap(t, + apiSvc, route1, + apiSvc, route2, + apiSvc, route3, + adminSvc, route1, + adminSvc, route3, + ), out.RoutesByParentRef) + }) + + // We untrack the first, but we let the third one be a dangling reference + // so that the loader has to fix it up. + deleteRes(route1.Resource.Id, true) + deleteRes(route3.Resource.Id, false) + + testutil.RunStep(t, "delete first and third route", func(t *testing.T) { + out, err := LoadResourcesForComputedRoutes(ctx, loggerFor, rt.Client, mapper, apiRoutesID) + require.NoError(t, err) + + prototest.AssertDeepEqual(t, NewRelatedResources().AddResources( + apiSvc, + fooSvc, + barSvc, + route2, + ).AddComputedRoutesIDs(apiRoutesID), out) + require.Equal(t, doubleMap(t, + apiSvc, route2, + ), out.RoutesByParentRef) + }) + + barFailover := writeFailover("bar", &pbcatalog.FailoverPolicy{ + Config: &pbcatalog.FailoverConfig{ + Destinations: []*pbcatalog.FailoverDestination{{ + Ref: newRef(catalog.ServiceType, "admin"), + }}, + }, + }) + + testutil.RunStep(t, "add a failover", func(t *testing.T) { + out, err := LoadResourcesForComputedRoutes(ctx, loggerFor, rt.Client, mapper, apiRoutesID) + require.NoError(t, err) + + prototest.AssertDeepEqual(t, NewRelatedResources().AddResources( + apiSvc, + fooSvc, + barSvc, + adminSvc, + route2, + barFailover, + ).AddComputedRoutesIDs(apiRoutesID), out) + require.Equal(t, doubleMap(t, + apiSvc, route2, + ), out.RoutesByParentRef) + }) + + fooDestPolicy := writeDestPolicy("foo", &pbmesh.DestinationPolicy{ + PortConfigs: map[string]*pbmesh.DestinationConfig{ + "www": { + ConnectTimeout: durationpb.New(55 * time.Second), + }, + }, + }) + + testutil.RunStep(t, "add a dest policy", func(t *testing.T) { + out, err := LoadResourcesForComputedRoutes(ctx, loggerFor, rt.Client, mapper, apiRoutesID) + require.NoError(t, err) + + prototest.AssertDeepEqual(t, NewRelatedResources().AddResources( + apiSvc, + fooSvc, + barSvc, + adminSvc, + route2, + barFailover, + fooDestPolicy, + ).AddComputedRoutesIDs(apiRoutesID), out) + require.Equal(t, doubleMap(t, + apiSvc, route2, + ), out.RoutesByParentRef) + }) +} + +func newRef(typ *pbresource.Type, name string) *pbresource.Reference { + return rtest.Resource(typ, name).Reference("") +} + +func defaultTenancy() *pbresource.Tenancy { + return &pbresource.Tenancy{ + Partition: "default", + Namespace: "default", + PeerName: "local", + } +} + +func doubleMap(t *testing.T, list ...decodedResource) map[resource.ReferenceKey]map[resource.ReferenceKey]struct{} { + if len(list)%2 != 0 { + t.Fatalf("list must have an even number of references") + } + out := make(map[resource.ReferenceKey]map[resource.ReferenceKey]struct{}) + for i := 0; i < len(list); i += 2 { + svcRK := resource.NewReferenceKey(list[i].GetResource().Id) + routeRK := resource.NewReferenceKey(list[i+1].GetResource().Id) + + m, ok := out[svcRK] + if !ok { + m = make(map[resource.ReferenceKey]struct{}) + out[svcRK] = m + } + m[routeRK] = struct{}{} + } + return out +} diff --git a/internal/mesh/internal/controllers/routes/loader/memoized.go b/internal/mesh/internal/controllers/routes/loader/memoized.go new file mode 100644 index 00000000000..f876e8ed6b3 --- /dev/null +++ b/internal/mesh/internal/controllers/routes/loader/memoized.go @@ -0,0 +1,164 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package loader + +import ( + "context" + "fmt" + + "github.com/hashicorp/consul/internal/catalog" + "github.com/hashicorp/consul/internal/mesh/internal/types" + "github.com/hashicorp/consul/internal/resource" + pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v1alpha1" + pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" + "github.com/hashicorp/consul/proto-public/pbresource" +) + +type memoizingLoader struct { + client pbresource.ResourceServiceClient + + httpRoutes map[resource.ReferenceKey]*types.DecodedHTTPRoute + grpcRoutes map[resource.ReferenceKey]*types.DecodedGRPCRoute + tcpRoutes map[resource.ReferenceKey]*types.DecodedTCPRoute + destPolicies map[resource.ReferenceKey]*types.DecodedDestinationPolicy + failoverPolicies map[resource.ReferenceKey]*types.DecodedFailoverPolicy + services map[resource.ReferenceKey]*types.DecodedService +} + +func newMemoizingLoader(client pbresource.ResourceServiceClient) *memoizingLoader { + if client == nil { + panic("client is required") + } + return &memoizingLoader{ + client: client, + httpRoutes: make(map[resource.ReferenceKey]*types.DecodedHTTPRoute), + grpcRoutes: make(map[resource.ReferenceKey]*types.DecodedGRPCRoute), + tcpRoutes: make(map[resource.ReferenceKey]*types.DecodedTCPRoute), + destPolicies: make(map[resource.ReferenceKey]*types.DecodedDestinationPolicy), + failoverPolicies: make(map[resource.ReferenceKey]*types.DecodedFailoverPolicy), + services: make(map[resource.ReferenceKey]*types.DecodedService), + } +} + +// TODO: figure out how to code-gen all of these + +func (m *memoizingLoader) GetHTTPRoute(ctx context.Context, id *pbresource.ID) (*types.DecodedHTTPRoute, error) { + if !resource.EqualType(id.Type, types.HTTPRouteType) { + return nil, fmt.Errorf("expected *mesh.HTTPRoute, not %s", resource.TypeToString(id.Type)) + } + + rk := resource.NewReferenceKey(id) + + if cached, ok := m.httpRoutes[rk]; ok { + return cached, nil // cached value may be nil + } + + dec, err := resource.GetDecodedResource[pbmesh.HTTPRoute, *pbmesh.HTTPRoute](ctx, m.client, id) + if err != nil { + return nil, err + } + + m.httpRoutes[rk] = dec + return dec, nil +} + +func (m *memoizingLoader) GetGRPCRoute(ctx context.Context, id *pbresource.ID) (*types.DecodedGRPCRoute, error) { + if !resource.EqualType(id.Type, types.GRPCRouteType) { + return nil, fmt.Errorf("expected *mesh.GRPCRoute, not %s", resource.TypeToString(id.Type)) + } + + rk := resource.NewReferenceKey(id) + + if cached, ok := m.grpcRoutes[rk]; ok { + return cached, nil // cached value may be nil + } + + dec, err := resource.GetDecodedResource[pbmesh.GRPCRoute, *pbmesh.GRPCRoute](ctx, m.client, id) + if err != nil { + return nil, err + } + + m.grpcRoutes[rk] = dec + return dec, nil +} + +func (m *memoizingLoader) GetTCPRoute(ctx context.Context, id *pbresource.ID) (*types.DecodedTCPRoute, error) { + if !resource.EqualType(id.Type, types.TCPRouteType) { + return nil, fmt.Errorf("expected *mesh.TCPRoute, not %s", resource.TypeToString(id.Type)) + } + + rk := resource.NewReferenceKey(id) + + if cached, ok := m.tcpRoutes[rk]; ok { + return cached, nil // cached value may be nil + } + + dec, err := resource.GetDecodedResource[pbmesh.TCPRoute, *pbmesh.TCPRoute](ctx, m.client, id) + if err != nil { + return nil, err + } + + m.tcpRoutes[rk] = dec + return dec, nil +} + +func (m *memoizingLoader) GetDestinationPolicy(ctx context.Context, id *pbresource.ID) (*types.DecodedDestinationPolicy, error) { + if !resource.EqualType(id.Type, types.DestinationPolicyType) { + return nil, fmt.Errorf("expected *mesh.DestinationPolicy, not %s", resource.TypeToString(id.Type)) + } + + rk := resource.NewReferenceKey(id) + + if cached, ok := m.destPolicies[rk]; ok { + return cached, nil // cached value may be nil + } + + dec, err := resource.GetDecodedResource[pbmesh.DestinationPolicy, *pbmesh.DestinationPolicy](ctx, m.client, id) + if err != nil { + return nil, err + } + + m.destPolicies[rk] = dec + return dec, nil +} + +func (m *memoizingLoader) GetFailoverPolicy(ctx context.Context, id *pbresource.ID) (*types.DecodedFailoverPolicy, error) { + if !resource.EqualType(id.Type, catalog.FailoverPolicyType) { + return nil, fmt.Errorf("expected *catalog.FailoverPolicy, not %s", resource.TypeToString(id.Type)) + } + + rk := resource.NewReferenceKey(id) + + if cached, ok := m.failoverPolicies[rk]; ok { + return cached, nil // cached value may be nil + } + + dec, err := resource.GetDecodedResource[pbcatalog.FailoverPolicy, *pbcatalog.FailoverPolicy](ctx, m.client, id) + if err != nil { + return nil, err + } + + m.failoverPolicies[rk] = dec + return dec, nil +} + +func (m *memoizingLoader) GetService(ctx context.Context, id *pbresource.ID) (*types.DecodedService, error) { + if !resource.EqualType(id.Type, catalog.ServiceType) { + return nil, fmt.Errorf("expected *catalog.Service, not %s", resource.TypeToString(id.Type)) + } + + rk := resource.NewReferenceKey(id) + + if cached, ok := m.services[rk]; ok { + return cached, nil // cached value may be nil + } + + dec, err := resource.GetDecodedResource[pbcatalog.Service, *pbcatalog.Service](ctx, m.client, id) + if err != nil { + return nil, err + } + + m.services[rk] = dec + return dec, nil +} diff --git a/internal/mesh/internal/controllers/routes/loader/related.go b/internal/mesh/internal/controllers/routes/loader/related.go new file mode 100644 index 00000000000..35640a55756 --- /dev/null +++ b/internal/mesh/internal/controllers/routes/loader/related.go @@ -0,0 +1,204 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package loader + +import ( + "fmt" + + "github.com/hashicorp/consul/internal/catalog" + "github.com/hashicorp/consul/internal/mesh/internal/types" + "github.com/hashicorp/consul/internal/resource" + "github.com/hashicorp/consul/proto-public/pbresource" +) + +// RelatedResources is a spiritual successor of *configentry.DiscoveryChainSet +type RelatedResources struct { + ComputedRoutesList []*pbresource.ID + // RoutesByParentRef is a map of a parent Service to the xRoutes that compose it. + RoutesByParentRef map[resource.ReferenceKey]map[resource.ReferenceKey]struct{} + HTTPRoutes map[resource.ReferenceKey]*types.DecodedHTTPRoute + GRPCRoutes map[resource.ReferenceKey]*types.DecodedGRPCRoute + TCPRoutes map[resource.ReferenceKey]*types.DecodedTCPRoute + Services map[resource.ReferenceKey]*types.DecodedService + FailoverPolicies map[resource.ReferenceKey]*types.DecodedFailoverPolicy + DestinationPolicies map[resource.ReferenceKey]*types.DecodedDestinationPolicy +} + +func NewRelatedResources() *RelatedResources { + return &RelatedResources{ + RoutesByParentRef: make(map[resource.ReferenceKey]map[resource.ReferenceKey]struct{}), + HTTPRoutes: make(map[resource.ReferenceKey]*types.DecodedHTTPRoute), + GRPCRoutes: make(map[resource.ReferenceKey]*types.DecodedGRPCRoute), + TCPRoutes: make(map[resource.ReferenceKey]*types.DecodedTCPRoute), + Services: make(map[resource.ReferenceKey]*types.DecodedService), + FailoverPolicies: make(map[resource.ReferenceKey]*types.DecodedFailoverPolicy), + DestinationPolicies: make(map[resource.ReferenceKey]*types.DecodedDestinationPolicy), + } +} + +func (r *RelatedResources) AddComputedRoutesIDs(list ...*pbresource.ID) *RelatedResources { + for _, id := range list { + r.AddComputedRoutesID(id) + } + return r +} + +func (r *RelatedResources) AddComputedRoutesID(id *pbresource.ID) { + if !resource.EqualType(id.Type, types.ComputedRoutesType) { + panic(fmt.Sprintf("expected *mesh.ComputedRoutes, not %s", resource.TypeToString(id.Type))) + } + r.ComputedRoutesList = append(r.ComputedRoutesList, id) +} + +func (r *RelatedResources) AddResources(list ...decodedResource) *RelatedResources { + for _, res := range list { + _ = r.AddResource(res) + } + return r +} + +type decodedResource interface { + GetResource() *pbresource.Resource +} + +func (r *RelatedResources) AddResource(res decodedResource) bool { + if res == nil { + return false + } + + switch dec := res.(type) { + case *types.DecodedHTTPRoute: + r.addRouteSetEntries(dec.Resource, dec.Data) + return addResource(dec.Resource.Id, dec, r.HTTPRoutes) + case *types.DecodedGRPCRoute: + r.addRouteSetEntries(dec.Resource, dec.Data) + return addResource(dec.Resource.Id, dec, r.GRPCRoutes) + case *types.DecodedTCPRoute: + r.addRouteSetEntries(dec.Resource, dec.Data) + return addResource(dec.Resource.Id, dec, r.TCPRoutes) + case *types.DecodedService: + return addResource(dec.Resource.Id, dec, r.Services) + case *types.DecodedFailoverPolicy: + return addResource(dec.Resource.Id, dec, r.FailoverPolicies) + case *types.DecodedDestinationPolicy: + return addResource(dec.Resource.Id, dec, r.DestinationPolicies) + default: + panic(fmt.Sprintf("unknown decoded resource type: %T", res)) + } +} + +func (r *RelatedResources) addRouteSetEntries( + res *pbresource.Resource, + xroute types.XRouteData, +) { + if res == nil || xroute == nil { + return + } + + routeRK := resource.NewReferenceKey(res.Id) + + for _, parentRef := range xroute.GetParentRefs() { + if parentRef.Ref == nil || !types.IsServiceType(parentRef.Ref.Type) { + continue + } + svcRK := resource.NewReferenceKey(parentRef.Ref) + + r.addRouteByParentRef(svcRK, routeRK) + } +} + +func (r *RelatedResources) addRouteByParentRef(svcRK, xRouteRK resource.ReferenceKey) { + m, ok := r.RoutesByParentRef[svcRK] + if !ok { + m = make(map[resource.ReferenceKey]struct{}) + r.RoutesByParentRef[svcRK] = m + } + m[xRouteRK] = struct{}{} +} + +type RouteWalkFunc func( + rk resource.ReferenceKey, + res *pbresource.Resource, + route types.XRouteData, +) + +func (r *RelatedResources) WalkRoutes(fn RouteWalkFunc) { + for rk, route := range r.HTTPRoutes { + fn(rk, route.Resource, route.Data) + } + for rk, route := range r.GRPCRoutes { + fn(rk, route.Resource, route.Data) + } + for rk, route := range r.TCPRoutes { + fn(rk, route.Resource, route.Data) + } +} + +func (r *RelatedResources) WalkRoutesForParentRef(parentRef *pbresource.Reference, fn RouteWalkFunc) { + if !resource.EqualType(parentRef.Type, catalog.ServiceType) { + panic(fmt.Sprintf("expected *catalog.Service, not %s", resource.TypeToString(parentRef.Type))) + } + routeMap := r.RoutesByParentRef[resource.NewReferenceKey(parentRef)] + if len(routeMap) == 0 { + return + } + + for rk := range routeMap { + if route, ok := r.HTTPRoutes[rk]; ok { + fn(rk, route.Resource, route.Data) + continue + } + if route, ok := r.GRPCRoutes[rk]; ok { + fn(rk, route.Resource, route.Data) + continue + } + if route, ok := r.TCPRoutes[rk]; ok { + fn(rk, route.Resource, route.Data) + continue + } + } +} + +func (r *RelatedResources) GetService(ref resource.ReferenceOrID) *types.DecodedService { + return r.Services[resource.NewReferenceKey(ref)] +} + +func (r *RelatedResources) GetFailoverPolicy(ref resource.ReferenceOrID) *types.DecodedFailoverPolicy { + return r.FailoverPolicies[resource.NewReferenceKey(ref)] +} + +func (r *RelatedResources) GetFailoverPolicyForService(ref resource.ReferenceOrID) *types.DecodedFailoverPolicy { + failRef := &pbresource.Reference{ + Type: catalog.FailoverPolicyType, + Tenancy: ref.GetTenancy(), + Name: ref.GetName(), + } + return r.GetFailoverPolicy(failRef) +} + +func (r *RelatedResources) GetDestinationPolicy(ref resource.ReferenceOrID) *types.DecodedDestinationPolicy { + return r.DestinationPolicies[resource.NewReferenceKey(ref)] +} + +func (r *RelatedResources) GetDestinationPolicyForService(ref resource.ReferenceOrID) *types.DecodedDestinationPolicy { + destRef := &pbresource.Reference{ + Type: types.DestinationPolicyType, + Tenancy: ref.GetTenancy(), + Name: ref.GetName(), + } + return r.GetDestinationPolicy(destRef) +} + +func addResource[V any](id *pbresource.ID, res *V, m map[resource.ReferenceKey]*V) bool { + if res == nil { + return false + } + + rk := resource.NewReferenceKey(id) + if _, ok := m[rk]; ok { + return false + } + m[rk] = res + return true +} diff --git a/internal/mesh/internal/controllers/routes/pending_status.go b/internal/mesh/internal/controllers/routes/pending_status.go new file mode 100644 index 00000000000..64de22b5282 --- /dev/null +++ b/internal/mesh/internal/controllers/routes/pending_status.go @@ -0,0 +1,92 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package routes + +import ( + "context" + + "github.com/hashicorp/consul/internal/controller" + "github.com/hashicorp/consul/internal/resource" + "github.com/hashicorp/consul/proto-public/pbresource" +) + +type PendingResourceStatusUpdate struct { + ID *pbresource.ID + Generation string + CurrStatus *pbresource.Status + + NewConditions []*pbresource.Condition +} + +type PendingStatuses map[resource.ReferenceKey]*PendingResourceStatusUpdate + +func (p PendingStatuses) AddConditions( + rk resource.ReferenceKey, + res *pbresource.Resource, + newConditions []*pbresource.Condition, +) { + state, ok := p[rk] + if !ok { + state = &PendingResourceStatusUpdate{ + ID: res.Id, + Generation: res.Generation, + CurrStatus: res.Status[StatusKey], + } + p[rk] = state + } + + state.NewConditions = append(state.NewConditions, newConditions...) +} + +func UpdatePendingStatuses( + ctx context.Context, + rt controller.Runtime, + pending PendingStatuses, +) error { + for _, state := range pending { + logger := rt.Logger.With("resource", resource.IDToString(state.ID)) + + var newStatus *pbresource.Status + if len(state.NewConditions) > 0 { + newStatus = &pbresource.Status{ + ObservedGeneration: state.Generation, + Conditions: state.NewConditions, + } + } else { + newStatus = &pbresource.Status{ + ObservedGeneration: state.Generation, + Conditions: []*pbresource.Condition{ + ConditionOK, + }, + } + } + if resource.EqualStatus(state.CurrStatus, newStatus, false) { + logger.Trace( + "resource's status is unchanged", + "conditions", newStatus.Conditions, + ) + } else { + _, err := rt.Client.WriteStatus(ctx, &pbresource.WriteStatusRequest{ + Id: state.ID, + Key: StatusKey, + Status: newStatus, + }) + + if err != nil { + logger.Error( + "error encountered when attempting to update the resource's status", + "error", err, + ) + return err + } + + logger.Trace( + "resource's status was updated", + "conditions", newStatus.Conditions, + ) + } + } + + return nil +} diff --git a/internal/mesh/internal/controllers/routes/sort_rules.go b/internal/mesh/internal/controllers/routes/sort_rules.go new file mode 100644 index 00000000000..fc0b4990436 --- /dev/null +++ b/internal/mesh/internal/controllers/routes/sort_rules.go @@ -0,0 +1,220 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package routes + +import ( + "sort" + + "github.com/oklog/ulid" + + "github.com/hashicorp/consul/internal/mesh/internal/types" + "github.com/hashicorp/consul/internal/resource" + pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" +) + +func gammaSortRouteRules(node *inputRouteNode) { + switch { + case resource.EqualType(node.RouteType, types.HTTPRouteType): + gammaSortHTTPRouteRules(node.HTTPRules) + case resource.EqualType(node.RouteType, types.GRPCRouteType): + // TODO + case resource.EqualType(node.RouteType, types.TCPRouteType): + // TODO + default: + panic("impossible") + } +} + +func gammaSortHTTPRouteRules(rules []*pbmesh.InterpretedHTTPRouteRule) { + // First generate a parallel slice. + sortable := &sortableHTTPRouteRules{ + rules: rules, + derivedInfo: make([]*derivedHTTPRouteRuleInfo, 0, len(rules)), + } + + for _, rule := range rules { + var sr derivedHTTPRouteRuleInfo + for _, m := range rule.Matches { + if m.Path != nil { + switch m.Path.Type { + case pbmesh.PathMatchType_PATH_MATCH_TYPE_EXACT: + sr.hasPathExact = true + case pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX: + sr.hasPathPrefix = true + v := len(m.Path.Value) + if v > sr.pathPrefixLength { + sr.pathPrefixLength = v + } + } + } + + if m.Method != "" { + sr.hasMethod = true + } + if v := len(m.Headers); v > sr.numHeaders { + sr.numHeaders = v + } + if v := len(m.QueryParams); v > sr.numQueryParams { + sr.numQueryParams = v + } + } + sortable.derivedInfo = append(sortable.derivedInfo, &sr) + } + + // Similar to + // "agent/consul/discoverychain/gateway_httproute.go" + // compareHTTPRules + + // Sort this by the GAMMA spec. We assume the caller has pre-sorted this + // for tiebreakers based on resource metadata. + + sort.Stable(sortable) +} + +type derivedHTTPRouteRuleInfo struct { + // sortable fields extracted from route + hasPathExact bool + hasPathPrefix bool + pathPrefixLength int + hasMethod bool + numHeaders int + numQueryParams int +} + +type sortableHTTPRouteRules struct { + rules []*pbmesh.InterpretedHTTPRouteRule + derivedInfo []*derivedHTTPRouteRuleInfo +} + +var _ sort.Interface = (*sortableHTTPRouteRules)(nil) + +func (r *sortableHTTPRouteRules) Len() int { return len(r.rules) } + +func (r *sortableHTTPRouteRules) Swap(i, j int) { + r.rules[i], r.rules[j] = r.rules[j], r.rules[i] + r.derivedInfo[i], r.derivedInfo[j] = r.derivedInfo[j], r.derivedInfo[i] +} + +func (r *sortableHTTPRouteRules) Less(i, j int) bool { + a := r.derivedInfo[i] + b := r.derivedInfo[j] + + // (1) “Exact” path match. + switch { + case a.hasPathExact && b.hasPathExact: + // NEXT TIE BREAK + case a.hasPathExact && !b.hasPathExact: + return true + case !a.hasPathExact && b.hasPathExact: + return false + } + + // (2) “Prefix” path match with largest number of characters. + switch { + case a.hasPathPrefix && b.hasPathPrefix: + if a.pathPrefixLength != b.pathPrefixLength { + return a.pathPrefixLength > b.pathPrefixLength + } + // NEXT TIE BREAK + case a.hasPathPrefix && !b.hasPathPrefix: + return true + case !a.hasPathPrefix && b.hasPathPrefix: + return false + } + + // (3) Method match. + switch { + case a.hasMethod && b.hasMethod: + // NEXT TIE BREAK + case a.hasMethod && !b.hasMethod: + return true + case !a.hasMethod && b.hasMethod: + return false + } + + // (4) Largest number of header matches. + switch { + case a.numHeaders == b.numHeaders: + // NEXT TIE BREAK + case a.numHeaders > b.numHeaders: + return true + case a.numHeaders < b.numHeaders: + return false + } + + // (5) Largest number of query param matches. + return a.numQueryParams > b.numQueryParams + // +} + +func gammaInitialSortWrappedRoutes(routes []*inputRouteNode) { + if len(routes) < 2 { + return + } + + // First sort the input routes by the final criteria, so we can let the + // stable sort take care of the ultimate tiebreakers. + sort.SliceStable(routes, func(i, j int) bool { + var ( + resA = routes[i].OriginalResource() + resB = routes[j].OriginalResource() + + genA = resA.Generation + genB = resB.Generation + ) + + // (END-1) The oldest Route based on creation timestamp. + // + // Because these are ULIDs, we should be able to lexicographically sort + // them to determine the oldest, but we also need to have a further + // tiebreaker AFTER per-gamma so we cannot. + aULID, aErr := ulid.Parse(genA) + bULID, bErr := ulid.Parse(genB) + if aErr == nil && bErr == nil { + aTime := aULID.Time() + bTime := bULID.Time() + + switch { + case aTime < bTime: + return true + case aTime > bTime: + return false + default: + // NEXT TIE BREAK + } + } + + // (END-2) The Route appearing first in alphabetical order by “{namespace}/{name}”. + var ( + tenancyA = resA.Id.Tenancy + tenancyB = resB.Id.Tenancy + + nsA = tenancyA.Namespace + nsB = tenancyB.Namespace + ) + + if nsA == "" { + nsA = "default" + } + if nsB == "" { + nsB = "default" + } + switch { + case nsA < nsB: + return true + case nsA > nsB: + return false + default: + // NEXT TIE BREAK + } + + return resA.Id.Name < resB.Id.Name + + // We get this for free b/c of the stable sort. + // + // If ties still exist within an HTTPRoute, matching precedence MUST + // be granted to the FIRST matching rule (in list order) with a match + // meeting the above criteria. + }) +} diff --git a/internal/mesh/internal/controllers/routes/status.go b/internal/mesh/internal/controllers/routes/status.go new file mode 100644 index 00000000000..ef57592cbd8 --- /dev/null +++ b/internal/mesh/internal/controllers/routes/status.go @@ -0,0 +1,40 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package routes + +import ( + "github.com/hashicorp/consul/proto-public/pbresource" +) + +const ( + StatusKey = "consul.io/routes-controller" + StatusConditionHealthy = "healthy" + + MeshConfigHealthyMessage = "Routing information is valid" + MeshConfigUnhealthyMessagePrefix = "Routing information is not valid: " +) + +var ( + ConditionMeshPassing = &pbresource.Condition{ + Type: StatusConditionHealthy, + State: pbresource.Condition_STATE_TRUE, + Reason: OKReason, + Message: MeshConfigHealthyMessage, + } +) + +func ConditionMeshError(reason, message string) *pbresource.Condition { + if reason == "" { + panic("reason is required") + } + if message == "" { + panic("message is required") + } + return &pbresource.Condition{ + Type: StatusConditionHealthy, + State: pbresource.Condition_STATE_FALSE, + Reason: reason, + Message: MeshConfigUnhealthyMessagePrefix + message, + } +} diff --git a/internal/mesh/internal/controllers/routes/status_xroute.go b/internal/mesh/internal/controllers/routes/status_xroute.go new file mode 100644 index 00000000000..c298c15a374 --- /dev/null +++ b/internal/mesh/internal/controllers/routes/status_xroute.go @@ -0,0 +1,133 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package routes + +import ( + "fmt" + + "github.com/hashicorp/consul/internal/resource" + "github.com/hashicorp/consul/proto-public/pbresource" +) + +const ( + StatusConditionValid = "valid" + + OKReason = "Ok" + + MissingParentRefReason = "MissingParentRef" + MissingBackendRefReason = "MissingBackendRef" + + ParentRefOutsideMeshReason = "ParentRefOutsideMesh" + BackendRefOutsideMeshReason = "BackendRefOutsideMesh" + + UnknownParentRefPortReason = "UnknownParentRefPort" + UnknownBackendRefPortReason = "UnknownBackendRefPort" + + ConflictNotBoundToParentRefReason = "ConflictNotBoundToParentRef" +) + +var ( + ConditionOK = &pbresource.Condition{ + Type: StatusConditionValid, + State: pbresource.Condition_STATE_TRUE, + Reason: OKReason, + // TODO: needs message? + } +) + +func ConditionMissingParentRef(ref *pbresource.Reference) *pbresource.Condition { + return conditionMissingRef(ref, false) +} + +func ConditionMissingBackendRef(ref *pbresource.Reference) *pbresource.Condition { + return conditionMissingRef(ref, true) +} + +func conditionMissingRef(ref *pbresource.Reference, forBackend bool) *pbresource.Condition { + reason := MissingParentRefReason + short := "parent" + if forBackend { + reason = MissingBackendRefReason + short = "backend" + } + return &pbresource.Condition{ + Type: StatusConditionValid, + State: pbresource.Condition_STATE_FALSE, + Reason: reason, + Message: fmt.Sprintf( + "service for %s ref %q does not exist", + short, + resource.ReferenceToString(ref), + ), + } +} + +func ConditionParentRefOutsideMesh(ref *pbresource.Reference) *pbresource.Condition { + return conditionRefOutsideMesh(ref, false) +} + +func ConditionBackendRefOutsideMesh(ref *pbresource.Reference) *pbresource.Condition { + return conditionRefOutsideMesh(ref, true) +} + +func conditionRefOutsideMesh(ref *pbresource.Reference, forBackend bool) *pbresource.Condition { + reason := ParentRefOutsideMeshReason + short := "parent" + if forBackend { + reason = BackendRefOutsideMeshReason + short = "backend" + } + return &pbresource.Condition{ + Type: StatusConditionValid, + State: pbresource.Condition_STATE_FALSE, + Reason: reason, + Message: fmt.Sprintf( + "service for %s ref %q does not expose a mesh port", + short, + resource.ReferenceToString(ref), + ), + } +} + +func ConditionUnknownParentRefPort(ref *pbresource.Reference, port string) *pbresource.Condition { + return conditionUnknownRefPort(ref, port, false) +} + +func ConditionUnknownBackendRefPort(ref *pbresource.Reference, port string) *pbresource.Condition { + return conditionUnknownRefPort(ref, port, true) +} + +func conditionUnknownRefPort(ref *pbresource.Reference, port string, forBackend bool) *pbresource.Condition { + reason := UnknownParentRefPortReason + short := "parent" + if forBackend { + reason = UnknownBackendRefPortReason + short = "backend" + } + return &pbresource.Condition{ + Type: StatusConditionValid, + State: pbresource.Condition_STATE_FALSE, + Reason: reason, + Message: fmt.Sprintf( + "service for %s ref %q does not expose port %q", + short, + resource.ReferenceToString(ref), + port, + ), + } +} + +func ConditionConflictNotBoundToParentRef(ref *pbresource.Reference, port string, realType *pbresource.Type) *pbresource.Condition { + return &pbresource.Condition{ + Type: StatusConditionValid, + State: pbresource.Condition_STATE_FALSE, + Reason: ConflictNotBoundToParentRefReason, + Message: fmt.Sprintf( + "Existing routes of type %q are bound to parent ref %q on port %q preventing this from binding", + resource.TypeToString(realType), + resource.ReferenceToString(ref), + port, + ), + } +} diff --git a/internal/mesh/internal/controllers/routes/util.go b/internal/mesh/internal/controllers/routes/util.go new file mode 100644 index 00000000000..59fa2735165 --- /dev/null +++ b/internal/mesh/internal/controllers/routes/util.go @@ -0,0 +1,30 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package routes + +import ( + "google.golang.org/protobuf/proto" + + "github.com/hashicorp/consul/internal/controller" + "github.com/hashicorp/consul/internal/mesh/internal/controllers/routes/xroutemapper" +) + +func protoSliceClone[V any, PV interface { + proto.Message + *V +}](in []PV) []PV { + if in == nil { + return nil + } + out := make([]PV, 0, len(in)) + for _, v := range in { + out = append(out, proto.Clone(v).(PV)) + } + return out +} + +// Deprecated: xroutemapper.DeduplicateRequests +func deduplicate(reqs []controller.Request) []controller.Request { + return xroutemapper.DeduplicateRequests(reqs) +} diff --git a/internal/mesh/internal/controllers/routes/xroutemapper/util.go b/internal/mesh/internal/controllers/routes/xroutemapper/util.go new file mode 100644 index 00000000000..4239a34c90d --- /dev/null +++ b/internal/mesh/internal/controllers/routes/xroutemapper/util.go @@ -0,0 +1,102 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package xroutemapper + +import ( + "github.com/hashicorp/consul/internal/controller" + "github.com/hashicorp/consul/internal/mesh/internal/types" + "github.com/hashicorp/consul/internal/resource" + pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" + "github.com/hashicorp/consul/proto-public/pbresource" +) + +func DeduplicateRequests(reqs []controller.Request) []controller.Request { + out := make([]controller.Request, 0, len(reqs)) + seen := make(map[resID]struct{}) + + for _, req := range reqs { + rid := resID{ + ReferenceKey: resource.NewReferenceKey(req.ID), + UID: req.ID.Uid, + } + if _, ok := seen[rid]; !ok { + out = append(out, req) + seen[rid] = struct{}{} + } + } + + return out +} + +type resID struct { + resource.ReferenceKey + UID string +} + +func parentRefSliceToRefSlice(parentRefs []*pbmesh.ParentReference) []*pbresource.Reference { + if parentRefs == nil { + return nil + } + parents := make([]*pbresource.Reference, 0, len(parentRefs)) + for _, parentRef := range parentRefs { + if parentRef.Ref != nil && types.IsServiceType(parentRef.Ref.Type) { + parents = append(parents, parentRef.Ref) + } + } + return parents +} + +func backendRefSliceToRefSlice(backendRefs []*pbmesh.BackendReference) []*pbresource.Reference { + if backendRefs == nil { + return nil + } + backends := make([]*pbresource.Reference, 0, len(backendRefs)) + for _, backendRef := range backendRefs { + if backendRef.Ref != nil && types.IsServiceType(backendRef.Ref.Type) { + backends = append(backends, backendRef.Ref) + } + } + return backends +} + +func changeType(id *pbresource.ID, typ *pbresource.Type) *pbresource.ID { + return &pbresource.ID{ + Type: typ, + Tenancy: id.Tenancy, + Name: id.Name, + } +} + +func changeTypeForSlice(list []*pbresource.ID, typ *pbresource.Type) []*pbresource.ID { + if list == nil { + return nil + } + out := make([]*pbresource.ID, 0, len(list)) + for _, id := range list { + out = append(out, changeType(id, typ)) + } + return out +} + +func makeControllerRequests[V resource.ReferenceOrID]( + typ *pbresource.Type, + refs []V, +) []controller.Request { + if len(refs) == 0 { + return nil + } + + out := make([]controller.Request, 0, len(refs)) + for _, ref := range refs { + out = append(out, controller.Request{ + ID: &pbresource.ID{ + Type: typ, + Tenancy: ref.GetTenancy(), + Name: ref.GetName(), + }, + }) + } + + return out +} diff --git a/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper.go b/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper.go new file mode 100644 index 00000000000..3250257b709 --- /dev/null +++ b/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper.go @@ -0,0 +1,311 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package xroutemapper + +import ( + "context" + "fmt" + + "google.golang.org/protobuf/proto" + + "github.com/hashicorp/consul/internal/catalog" + "github.com/hashicorp/consul/internal/controller" + "github.com/hashicorp/consul/internal/mesh/internal/types" + "github.com/hashicorp/consul/internal/resource" + "github.com/hashicorp/consul/internal/resource/mappers/bimapper" + pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v1alpha1" + pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" + "github.com/hashicorp/consul/proto-public/pbresource" +) + +// Mapper tracks the following relationships: +// +// - xRoute <-> ParentRef Service +// - xRoute <-> BackendRef Service +// - FailoverPolicy <-> DestRef Service +// +// It is the job of the controller, loader, and mapper to keep the mappings up +// to date whenever new data is loaded. Notably because the dep mapper events +// do not signal when data is deleted, it is the job of the reconcile load of +// the data causing the event to notice something has been deleted and to +// untrack it here. +type Mapper struct { + httpRouteParentMapper *bimapper.Mapper + grpcRouteParentMapper *bimapper.Mapper + tcpRouteParentMapper *bimapper.Mapper + + httpRouteBackendMapper *bimapper.Mapper + grpcRouteBackendMapper *bimapper.Mapper + tcpRouteBackendMapper *bimapper.Mapper + + failMapper catalog.FailoverPolicyMapper +} + +// New creates a new Mapper. +func New() *Mapper { + return &Mapper{ + httpRouteParentMapper: bimapper.New(types.HTTPRouteType, catalog.ServiceType), + grpcRouteParentMapper: bimapper.New(types.GRPCRouteType, catalog.ServiceType), + tcpRouteParentMapper: bimapper.New(types.TCPRouteType, catalog.ServiceType), + + httpRouteBackendMapper: bimapper.New(types.HTTPRouteType, catalog.ServiceType), + grpcRouteBackendMapper: bimapper.New(types.GRPCRouteType, catalog.ServiceType), + tcpRouteBackendMapper: bimapper.New(types.TCPRouteType, catalog.ServiceType), + + failMapper: catalog.NewFailoverPolicyMapper(), + } +} + +func (m *Mapper) getRouteBiMappers(typ *pbresource.Type) (parent, backend *bimapper.Mapper) { + switch { + case resource.EqualType(types.HTTPRouteType, typ): + return m.httpRouteParentMapper, m.httpRouteBackendMapper + case resource.EqualType(types.GRPCRouteType, typ): + return m.grpcRouteParentMapper, m.grpcRouteBackendMapper + case resource.EqualType(types.TCPRouteType, typ): + return m.tcpRouteParentMapper, m.tcpRouteBackendMapper + default: + panic("unknown xroute type: " + resource.TypeToString(typ)) + } +} + +func (m *Mapper) walkRouteParentBiMappers(fn func(bm *bimapper.Mapper)) { + for _, bm := range []*bimapper.Mapper{ + m.httpRouteParentMapper, + m.grpcRouteParentMapper, + m.tcpRouteParentMapper, + } { + fn(bm) + } +} + +func (m *Mapper) walkRouteBackendBiMappers(fn func(bm *bimapper.Mapper)) { + for _, bm := range []*bimapper.Mapper{ + m.httpRouteBackendMapper, + m.grpcRouteBackendMapper, + m.tcpRouteBackendMapper, + } { + fn(bm) + } +} + +// TrackXRoute indexes the xRoute->parentRefService and +// xRoute->backendRefService relationship. +func (m *Mapper) TrackXRoute(id *pbresource.ID, xroute types.XRouteData) { + parent, backend := m.getRouteBiMappers(id.Type) + if parent == nil || backend == nil { + return + } + + parentRefs := parentRefSliceToRefSlice(xroute.GetParentRefs()) + backendRefs := backendRefSliceToRefSlice(xroute.GetUnderlyingBackendRefs()) + + parent.TrackItem(id, parentRefs) + backend.TrackItem(id, backendRefs) +} + +// UntrackXRoute undoes TrackXRoute. +func (m *Mapper) UntrackXRoute(id *pbresource.ID) { + parent, backend := m.getRouteBiMappers(id.Type) + if parent == nil || backend == nil { + return + } + + parent.UntrackItem(id) + backend.UntrackItem(id) +} + +// RouteIDsByParentServiceRef returns xRoute IDs that have a direct parentRef link to +// the provided service. +func (m *Mapper) RouteIDsByParentServiceRef(ref *pbresource.Reference) []*pbresource.ID { + var out []*pbresource.ID + m.walkRouteParentBiMappers(func(bm *bimapper.Mapper) { + got := bm.ItemsForLink(resource.IDFromReference(ref)) + out = append(out, got...) + }) + return out +} + +// RouteIDsByBackendServiceRef returns xRoute IDs that have a direct backendRef +// link to the provided service. +func (m *Mapper) RouteIDsByBackendServiceRef(ref *pbresource.Reference) []*pbresource.ID { + var out []*pbresource.ID + m.walkRouteBackendBiMappers(func(bm *bimapper.Mapper) { + got := bm.ItemsForLink(resource.IDFromReference(ref)) + out = append(out, got...) + }) + return out +} + +// ParentServiceRefsByRouteID is the opposite of RouteIDsByParentServiceRef. +func (m *Mapper) ParentServiceRefsByRouteID(item *pbresource.ID) []*pbresource.Reference { + parent, _ := m.getRouteBiMappers(item.Type) + if parent == nil { + return nil + } + return parent.LinksForItem(item) +} + +// BackendServiceRefsByRouteID is the opposite of RouteIDsByBackendServiceRef. +func (m *Mapper) BackendServiceRefsByRouteID(item *pbresource.ID) []*pbresource.Reference { + _, backend := m.getRouteBiMappers(item.Type) + if backend == nil { + return nil + } + return backend.LinksForItem(item) +} + +// MapHTTPRoute will map HTTPRoute changes to ComputedRoutes changes. +func (m *Mapper) MapHTTPRoute(ctx context.Context, rt controller.Runtime, res *pbresource.Resource) ([]controller.Request, error) { + return mapXRouteToComputedRoutes[pbmesh.HTTPRoute, *pbmesh.HTTPRoute](ctx, rt, res, m) +} + +// MapGRPCRoute will map GRPCRoute changes to ComputedRoutes changes. +func (m *Mapper) MapGRPCRoute(ctx context.Context, rt controller.Runtime, res *pbresource.Resource) ([]controller.Request, error) { + return mapXRouteToComputedRoutes[pbmesh.GRPCRoute, *pbmesh.GRPCRoute](ctx, rt, res, m) +} + +// MapTCPRoute will map TCPRoute changes to ComputedRoutes changes. +func (m *Mapper) MapTCPRoute(ctx context.Context, rt controller.Runtime, res *pbresource.Resource) ([]controller.Request, error) { + return mapXRouteToComputedRoutes[pbmesh.TCPRoute, *pbmesh.TCPRoute](ctx, rt, res, m) +} + +// mapXRouteToComputedRoutes will map xRoute changes to ComputedRoutes changes. +func mapXRouteToComputedRoutes[V any, PV interface { + proto.Message + *V + types.XRouteWithRefs +}](ctx context.Context, rt controller.Runtime, res *pbresource.Resource, m *Mapper) ([]controller.Request, error) { + dec, err := resource.Decode[V, PV](res) + if err != nil { + return nil, fmt.Errorf("error unmarshalling xRoute: %w", err) + } + + route := dec.Data + + m.TrackXRoute(res.Id, route) + + return DeduplicateRequests(makeControllerRequests( + types.ComputedRoutesType, + parentRefSliceToRefSlice(route.GetParentRefs()), + )), nil +} + +func (m *Mapper) MapFailoverPolicy( + ctx context.Context, + rt controller.Runtime, + res *pbresource.Resource, +) ([]controller.Request, error) { + if !types.IsFailoverPolicyType(res.Id.Type) { + return nil, fmt.Errorf("type is not a failover policy type: %s", res.Id.Type) + } + + dec, err := resource.Decode[pbcatalog.FailoverPolicy, *pbcatalog.FailoverPolicy](res) + if err != nil { + return nil, fmt.Errorf("error unmarshalling failover policy: %w", err) + } + + m.failMapper.TrackFailover(dec) + + // Since this is name-aligned, just switch the type and find routes that + // will route any traffic to this destination service. + svcID := changeType(res.Id, catalog.ServiceType) + + return m.mapXRouteDirectServiceRefToComputedRoutesByID(ctx, rt, svcID) +} + +func (m *Mapper) TrackFailoverPolicy(failover *types.DecodedFailoverPolicy) { + if failover != nil { + m.failMapper.TrackFailover(failover) + } +} + +func (m *Mapper) UntrackFailoverPolicy(failoverPolicyID *pbresource.ID) { + m.failMapper.UntrackFailover(failoverPolicyID) +} + +func (m *Mapper) MapDestinationPolicy( + ctx context.Context, + rt controller.Runtime, + res *pbresource.Resource, +) ([]controller.Request, error) { + if !types.IsDestinationPolicyType(res.Id.Type) { + return nil, fmt.Errorf("type is not a destination policy type: %s", res.Id.Type) + } + + // Since this is name-aligned, just switch the type and find routes that + // will route any traffic to this destination service. + svcID := changeType(res.Id, catalog.ServiceType) + + return m.mapXRouteDirectServiceRefToComputedRoutesByID(ctx, rt, svcID) +} + +func (m *Mapper) MapService( + ctx context.Context, + rt controller.Runtime, + res *pbresource.Resource, +) ([]controller.Request, error) { + // Ultimately we want to wake up a ComputedRoutes if either of the + // following exist: + // + // 1. xRoute[parentRef=OUTPUT_EVENT; backendRef=INPUT_EVENT] + // 2. xRoute[parentRef=OUTPUT_EVENT; backendRef=SOMETHING], FailoverPolicy[name=SOMETHING, destRef=INPUT_EVENT] + + // (case 2) First find all failover policies that have a reference to our input service. + failPolicyIDs := m.failMapper.FailoverIDsByService(res.Id) + effectiveServiceIDs := changeTypeForSlice(failPolicyIDs, catalog.ServiceType) + + // (case 1) Do the direct mapping also. + effectiveServiceIDs = append(effectiveServiceIDs, res.Id) + + var reqs []controller.Request + for _, svcID := range effectiveServiceIDs { + got, err := m.mapXRouteDirectServiceRefToComputedRoutesByID(ctx, rt, svcID) + if err != nil { + return nil, err + } + reqs = append(reqs, got...) + } + + return DeduplicateRequests(reqs), nil +} + +// NOTE: this function does not interrogate down into failover policies +func (m *Mapper) mapXRouteDirectServiceRefToComputedRoutesByID( + ctx context.Context, + rt controller.Runtime, + svcID *pbresource.ID, +) ([]controller.Request, error) { + if !types.IsServiceType(svcID.Type) { + return nil, fmt.Errorf("type is not a service type: %s", svcID.Type) + } + + // return 1 hit for the name aligned mesh config + primaryReq := controller.Request{ + ID: changeType(svcID, types.ComputedRoutesType), + } + + svcRef := resource.Reference(svcID, "") + + // Find all routes with an explicit backend ref to this service. + // + // the "name aligned" inclusion above should handle the implicit default + // destination implied by a parent ref without us having to do much more. + routeIDs := m.RouteIDsByBackendServiceRef(svcRef) + + out := make([]controller.Request, 0, 1+len(routeIDs)) // estimated + out = append(out, primaryReq) + + for _, routeID := range routeIDs { + // Find all parent refs of this route. + svcRefs := m.ParentServiceRefsByRouteID(routeID) + + out = append(out, makeControllerRequests( + types.ComputedRoutesType, + svcRefs, + )...) + } + + return DeduplicateRequests(out), nil +} diff --git a/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper_test.go b/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper_test.go new file mode 100644 index 00000000000..d3b825099e9 --- /dev/null +++ b/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper_test.go @@ -0,0 +1,587 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package xroutemapper + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/durationpb" + + "github.com/hashicorp/consul/internal/catalog" + "github.com/hashicorp/consul/internal/controller" + "github.com/hashicorp/consul/internal/mesh/internal/types" + "github.com/hashicorp/consul/internal/resource" + rtest "github.com/hashicorp/consul/internal/resource/resourcetest" + pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v1alpha1" + pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" + "github.com/hashicorp/consul/proto-public/pbresource" + "github.com/hashicorp/consul/proto/private/prototest" + "github.com/hashicorp/consul/sdk/testutil" +) + +func TestMapper_HTTPRoute_Tracking(t *testing.T) { + testMapper_Tracking(t, types.HTTPRouteType, func(t *testing.T, parentRefs []*pbmesh.ParentReference, backendRefs []*pbmesh.BackendReference) proto.Message { + route := &pbmesh.HTTPRoute{ + ParentRefs: parentRefs, + } + for _, backendRef := range backendRefs { + route.Rules = append(route.Rules, &pbmesh.HTTPRouteRule{ + BackendRefs: []*pbmesh.HTTPBackendRef{ + {BackendRef: backendRef}, + }, + }) + } + return route + }) +} + +func TestMapper_GRPCRoute_Tracking(t *testing.T) { + testMapper_Tracking(t, types.GRPCRouteType, func(t *testing.T, parentRefs []*pbmesh.ParentReference, backendRefs []*pbmesh.BackendReference) proto.Message { + route := &pbmesh.GRPCRoute{ + ParentRefs: parentRefs, + } + for _, backendRef := range backendRefs { + route.Rules = append(route.Rules, &pbmesh.GRPCRouteRule{ + BackendRefs: []*pbmesh.GRPCBackendRef{ + {BackendRef: backendRef}, + }, + }) + } + return route + }) +} + +func TestMapper_TCPRoute_Tracking(t *testing.T) { + testMapper_Tracking(t, types.TCPRouteType, func(t *testing.T, parentRefs []*pbmesh.ParentReference, backendRefs []*pbmesh.BackendReference) proto.Message { + route := &pbmesh.TCPRoute{ + ParentRefs: parentRefs, + } + for _, backendRef := range backendRefs { + route.Rules = append(route.Rules, &pbmesh.TCPRouteRule{ + BackendRefs: []*pbmesh.TCPBackendRef{ + {BackendRef: backendRef}, + }, + }) + } + return route + }) +} + +func testMapper_Tracking(t *testing.T, typ *pbresource.Type, newRoute func(t *testing.T, parentRefs []*pbmesh.ParentReference, backendRefs []*pbmesh.BackendReference) proto.Message) { + registry := resource.NewRegistry() + types.Register(registry) + catalog.RegisterTypes(registry) + + newService := func(name string) *pbresource.Resource { + svc := rtest.Resource(catalog.ServiceType, name). + WithData(t, &pbcatalog.Service{}).Build() + rtest.ValidateAndNormalize(t, registry, svc) + return svc + } + + newDestPolicy := func(name string, dur time.Duration) *pbresource.Resource { + policy := rtest.Resource(types.DestinationPolicyType, name). + WithData(t, &pbmesh.DestinationPolicy{ + PortConfigs: map[string]*pbmesh.DestinationConfig{ + "http": { + ConnectTimeout: durationpb.New(dur), + }, + }, + }).Build() + rtest.ValidateAndNormalize(t, registry, policy) + return policy + } + + newFailPolicy := func(name string, refs ...*pbresource.Reference) *pbresource.Resource { + var dests []*pbcatalog.FailoverDestination + for _, ref := range refs { + dests = append(dests, &pbcatalog.FailoverDestination{ + Ref: ref, + }) + } + policy := rtest.Resource(catalog.FailoverPolicyType, name). + WithData(t, &pbcatalog.FailoverPolicy{ + Config: &pbcatalog.FailoverConfig{ + Destinations: dests, + }, + }).Build() + rtest.ValidateAndNormalize(t, registry, policy) + return policy + } + + apiComputedRoutes := newID(types.ComputedRoutesType, "api") + wwwComputedRoutes := newID(types.ComputedRoutesType, "www") + barComputedRoutes := newID(types.ComputedRoutesType, "bar") + fooComputedRoutes := newID(types.ComputedRoutesType, "foo") + zimComputedRoutes := newID(types.ComputedRoutesType, "zim") + girComputedRoutes := newID(types.ComputedRoutesType, "gir") + + m := New() + + var ( + apiSvc = newService("api") + wwwSvc = newService("www") + barSvc = newService("bar") + fooSvc = newService("foo") + zimSvc = newService("zim") + girSvc = newService("gir") + + apiSvcRef = resource.Reference(apiSvc.Id, "") + wwwSvcRef = resource.Reference(wwwSvc.Id, "") + barSvcRef = resource.Reference(barSvc.Id, "") + fooSvcRef = resource.Reference(fooSvc.Id, "") + zimSvcRef = resource.Reference(zimSvc.Id, "") + girSvcRef = resource.Reference(girSvc.Id, "") + + apiDest = newDestPolicy("api", 55*time.Second) + wwwDest = newDestPolicy("www", 123*time.Second) + + // Start out easy and don't have failover policies that reference other services. + apiFail = newFailPolicy("api", newRef(catalog.ServiceType, "api")) + wwwFail = newFailPolicy("www", newRef(catalog.ServiceType, "www")) + barFail = newFailPolicy("bar", newRef(catalog.ServiceType, "bar")) + ) + + testutil.RunStep(t, "only name aligned defaults", func(t *testing.T) { + requireTracking(t, m, apiSvc, apiComputedRoutes) + requireTracking(t, m, wwwSvc, wwwComputedRoutes) + requireTracking(t, m, barSvc, barComputedRoutes) + requireTracking(t, m, fooSvc, fooComputedRoutes) + requireTracking(t, m, zimSvc, zimComputedRoutes) + requireTracking(t, m, girSvc, girComputedRoutes) + + requireTracking(t, m, apiDest, apiComputedRoutes) + requireTracking(t, m, wwwDest, wwwComputedRoutes) + + // This will track the failover policies. + requireTracking(t, m, apiFail, apiComputedRoutes) + requireTracking(t, m, wwwFail, wwwComputedRoutes) + requireTracking(t, m, barFail, barComputedRoutes) + + // verify other helper methods + for _, ref := range []*pbresource.Reference{apiSvcRef, wwwSvcRef, barSvcRef, fooSvcRef, zimSvcRef, girSvcRef} { + require.Empty(t, m.RouteIDsByBackendServiceRef(ref)) + require.Empty(t, m.RouteIDsByParentServiceRef(ref)) + } + }) + + var ( + route1 *pbresource.Resource + ) + testutil.RunStep(t, "track a name-aligned xroute", func(t *testing.T) { + // First route will also not cross any services. + route1 := rtest.Resource(typ, "route-1").WithData(t, newRoute(t, + []*pbmesh.ParentReference{ + {Ref: newRef(catalog.ServiceType, "api")}, + }, + []*pbmesh.BackendReference{ + newBackendRef("api"), + }, + )).Build() + rtest.ValidateAndNormalize(t, registry, route1) + + requireTracking(t, m, route1, apiComputedRoutes) + + // Now 'api' references should trigger more, but be duplicate-suppressed. + requireTracking(t, m, apiSvc, apiComputedRoutes) + requireTracking(t, m, wwwSvc, wwwComputedRoutes) + requireTracking(t, m, barSvc, barComputedRoutes) + requireTracking(t, m, fooSvc, fooComputedRoutes) + requireTracking(t, m, zimSvc, zimComputedRoutes) + requireTracking(t, m, girSvc, girComputedRoutes) + + requireTracking(t, m, apiDest, apiComputedRoutes) + requireTracking(t, m, wwwDest, wwwComputedRoutes) + + requireTracking(t, m, apiFail, apiComputedRoutes) + requireTracking(t, m, wwwFail, wwwComputedRoutes) + requireTracking(t, m, barFail, barComputedRoutes) + + // verify other helper methods + prototest.AssertElementsMatch(t, []*pbresource.Reference{apiSvcRef}, m.BackendServiceRefsByRouteID(route1.Id)) + prototest.AssertElementsMatch(t, []*pbresource.Reference{apiSvcRef}, m.ParentServiceRefsByRouteID(route1.Id)) + + prototest.AssertElementsMatch(t, []*pbresource.ID{route1.Id}, m.RouteIDsByBackendServiceRef(apiSvcRef)) + prototest.AssertElementsMatch(t, []*pbresource.ID{route1.Id}, m.RouteIDsByParentServiceRef(apiSvcRef)) + + for _, ref := range []*pbresource.Reference{wwwSvcRef, barSvcRef, fooSvcRef, zimSvcRef, girSvcRef} { + require.Empty(t, m.RouteIDsByBackendServiceRef(ref)) + require.Empty(t, m.RouteIDsByParentServiceRef(ref)) + } + }) + + testutil.RunStep(t, "make the route cross services", func(t *testing.T) { + route1 = rtest.Resource(typ, "route-1").WithData(t, newRoute(t, + []*pbmesh.ParentReference{ + {Ref: newRef(catalog.ServiceType, "api")}, + }, + []*pbmesh.BackendReference{ + newBackendRef("www"), + }, + )).Build() + rtest.ValidateAndNormalize(t, registry, route1) + + // Now witness the update. + requireTracking(t, m, route1, apiComputedRoutes) + + // Now 'api' references should trigger different things. + requireTracking(t, m, apiSvc, apiComputedRoutes) + requireTracking(t, m, wwwSvc, wwwComputedRoutes, apiComputedRoutes) + requireTracking(t, m, barSvc, barComputedRoutes) + requireTracking(t, m, fooSvc, fooComputedRoutes) + requireTracking(t, m, zimSvc, zimComputedRoutes) + requireTracking(t, m, girSvc, girComputedRoutes) + + requireTracking(t, m, apiDest, apiComputedRoutes) + requireTracking(t, m, wwwDest, wwwComputedRoutes, apiComputedRoutes) + + requireTracking(t, m, apiFail, apiComputedRoutes) + requireTracking(t, m, wwwFail, wwwComputedRoutes, apiComputedRoutes) + requireTracking(t, m, barFail, barComputedRoutes) + + // verify other helper methods + prototest.AssertElementsMatch(t, []*pbresource.Reference{wwwSvcRef}, m.BackendServiceRefsByRouteID(route1.Id)) + prototest.AssertElementsMatch(t, []*pbresource.Reference{apiSvcRef}, m.ParentServiceRefsByRouteID(route1.Id)) + + require.Empty(t, m.RouteIDsByBackendServiceRef(apiSvcRef)) + prototest.AssertElementsMatch(t, []*pbresource.ID{route1.Id}, m.RouteIDsByParentServiceRef(apiSvcRef)) + + prototest.AssertElementsMatch(t, []*pbresource.ID{route1.Id}, m.RouteIDsByBackendServiceRef(wwwSvcRef)) + require.Empty(t, m.RouteIDsByParentServiceRef(wwwSvcRef)) + + for _, ref := range []*pbresource.Reference{barSvcRef, fooSvcRef, zimSvcRef, girSvcRef} { + require.Empty(t, m.RouteIDsByBackendServiceRef(ref)) + require.Empty(t, m.RouteIDsByParentServiceRef(ref)) + } + }) + + var ( + route2 *pbresource.Resource + ) + testutil.RunStep(t, "make another route sharing a parent with the first", func(t *testing.T) { + route2 = rtest.Resource(typ, "route-2").WithData(t, newRoute(t, + []*pbmesh.ParentReference{ + {Ref: newRef(catalog.ServiceType, "api")}, + {Ref: newRef(catalog.ServiceType, "foo")}, + }, + []*pbmesh.BackendReference{ + newBackendRef("bar"), + }, + )).Build() + rtest.ValidateAndNormalize(t, registry, route1) + + // Now witness a route with multiple parents, overlapping the other route. + requireTracking(t, m, route2, apiComputedRoutes, fooComputedRoutes) + + requireTracking(t, m, apiSvc, apiComputedRoutes) + requireTracking(t, m, wwwSvc, wwwComputedRoutes, apiComputedRoutes) + requireTracking(t, m, barSvc, barComputedRoutes, apiComputedRoutes, fooComputedRoutes) + + requireTracking(t, m, fooSvc, fooComputedRoutes) + requireTracking(t, m, zimSvc, zimComputedRoutes) + requireTracking(t, m, girSvc, girComputedRoutes) + + requireTracking(t, m, apiDest, apiComputedRoutes) + requireTracking(t, m, wwwDest, wwwComputedRoutes, apiComputedRoutes) + + requireTracking(t, m, apiFail, apiComputedRoutes) + requireTracking(t, m, wwwFail, wwwComputedRoutes, apiComputedRoutes) + requireTracking(t, m, barFail, barComputedRoutes, apiComputedRoutes, fooComputedRoutes) + + requireTracking(t, m, route1, apiComputedRoutes) + // skip re-verifying route2 + // requireTracking(t, m, route2, apiComputedRoutes, fooComputedRoutes) + + // verify other helper methods + prototest.AssertElementsMatch(t, []*pbresource.Reference{wwwSvcRef}, m.BackendServiceRefsByRouteID(route1.Id)) + prototest.AssertElementsMatch(t, []*pbresource.Reference{apiSvcRef}, m.ParentServiceRefsByRouteID(route1.Id)) + + prototest.AssertElementsMatch(t, []*pbresource.Reference{barSvcRef}, m.BackendServiceRefsByRouteID(route2.Id)) + prototest.AssertElementsMatch(t, []*pbresource.Reference{apiSvcRef, fooSvcRef}, m.ParentServiceRefsByRouteID(route2.Id)) + + require.Empty(t, m.RouteIDsByBackendServiceRef(apiSvcRef)) + prototest.AssertElementsMatch(t, []*pbresource.ID{route1.Id, route2.Id}, m.RouteIDsByParentServiceRef(apiSvcRef)) + + prototest.AssertElementsMatch(t, []*pbresource.ID{route1.Id}, m.RouteIDsByBackendServiceRef(wwwSvcRef)) + require.Empty(t, m.RouteIDsByParentServiceRef(wwwSvcRef)) + + prototest.AssertElementsMatch(t, []*pbresource.ID{route2.Id}, m.RouteIDsByBackendServiceRef(barSvcRef)) + require.Empty(t, m.RouteIDsByParentServiceRef(barSvcRef)) + + require.Empty(t, m.RouteIDsByBackendServiceRef(fooSvcRef)) + prototest.AssertElementsMatch(t, []*pbresource.ID{route2.Id}, m.RouteIDsByParentServiceRef(fooSvcRef)) + + for _, ref := range []*pbresource.Reference{zimSvcRef, girSvcRef} { + require.Empty(t, m.RouteIDsByBackendServiceRef(ref)) + require.Empty(t, m.RouteIDsByParentServiceRef(ref)) + } + }) + + testutil.RunStep(t, "update the failover policy to cross services", func(t *testing.T) { + apiFail = newFailPolicy("api", + newRef(catalog.ServiceType, "foo"), + newRef(catalog.ServiceType, "zim")) + requireTracking(t, m, apiFail, apiComputedRoutes) + + requireTracking(t, m, apiSvc, apiComputedRoutes) + requireTracking(t, m, wwwSvc, wwwComputedRoutes, apiComputedRoutes) + requireTracking(t, m, barSvc, barComputedRoutes, apiComputedRoutes, fooComputedRoutes) + + requireTracking(t, m, fooSvc, fooComputedRoutes, apiComputedRoutes) + requireTracking(t, m, zimSvc, zimComputedRoutes, apiComputedRoutes) + requireTracking(t, m, girSvc, girComputedRoutes) + + requireTracking(t, m, apiDest, apiComputedRoutes) + requireTracking(t, m, wwwDest, wwwComputedRoutes, apiComputedRoutes) + + // skipping verification of apiFail b/c it happened above already + // requireTracking(t, m, apiFail, apiComputedRoutes) + requireTracking(t, m, wwwFail, wwwComputedRoutes, apiComputedRoutes) + requireTracking(t, m, barFail, barComputedRoutes, apiComputedRoutes, fooComputedRoutes) + + requireTracking(t, m, route1, apiComputedRoutes) + requireTracking(t, m, route2, apiComputedRoutes, fooComputedRoutes) + + // verify other helper methods + prototest.AssertElementsMatch(t, []*pbresource.Reference{wwwSvcRef}, m.BackendServiceRefsByRouteID(route1.Id)) + prototest.AssertElementsMatch(t, []*pbresource.Reference{apiSvcRef}, m.ParentServiceRefsByRouteID(route1.Id)) + + prototest.AssertElementsMatch(t, []*pbresource.Reference{barSvcRef}, m.BackendServiceRefsByRouteID(route2.Id)) + prototest.AssertElementsMatch(t, []*pbresource.Reference{apiSvcRef, fooSvcRef}, m.ParentServiceRefsByRouteID(route2.Id)) + + require.Empty(t, m.RouteIDsByBackendServiceRef(apiSvcRef)) + prototest.AssertElementsMatch(t, []*pbresource.ID{route1.Id, route2.Id}, m.RouteIDsByParentServiceRef(apiSvcRef)) + + prototest.AssertElementsMatch(t, []*pbresource.ID{route1.Id}, m.RouteIDsByBackendServiceRef(wwwSvcRef)) + require.Empty(t, m.RouteIDsByParentServiceRef(wwwSvcRef)) + + prototest.AssertElementsMatch(t, []*pbresource.ID{route2.Id}, m.RouteIDsByBackendServiceRef(barSvcRef)) + require.Empty(t, m.RouteIDsByParentServiceRef(barSvcRef)) + + require.Empty(t, m.RouteIDsByBackendServiceRef(fooSvcRef)) + prototest.AssertElementsMatch(t, []*pbresource.ID{route2.Id}, m.RouteIDsByParentServiceRef(fooSvcRef)) + + for _, ref := range []*pbresource.Reference{zimSvcRef, girSvcRef} { + require.Empty(t, m.RouteIDsByBackendServiceRef(ref)) + require.Empty(t, m.RouteIDsByParentServiceRef(ref)) + } + }) + + testutil.RunStep(t, "set a new failover policy for a service in route2", func(t *testing.T) { + barFail = newFailPolicy("bar", + newRef(catalog.ServiceType, "gir")) + requireTracking(t, m, barFail, barComputedRoutes, apiComputedRoutes, fooComputedRoutes) + + requireTracking(t, m, apiSvc, apiComputedRoutes) + requireTracking(t, m, wwwSvc, wwwComputedRoutes, apiComputedRoutes) + requireTracking(t, m, barSvc, barComputedRoutes, apiComputedRoutes, fooComputedRoutes) + + requireTracking(t, m, fooSvc, fooComputedRoutes, apiComputedRoutes) + requireTracking(t, m, zimSvc, zimComputedRoutes, apiComputedRoutes) + requireTracking(t, m, girSvc, girComputedRoutes, barComputedRoutes, apiComputedRoutes, fooComputedRoutes) + + requireTracking(t, m, apiDest, apiComputedRoutes) + requireTracking(t, m, wwwDest, wwwComputedRoutes, apiComputedRoutes) + + requireTracking(t, m, apiFail, apiComputedRoutes) + requireTracking(t, m, wwwFail, wwwComputedRoutes, apiComputedRoutes) + // skipping verification of barFail b/c it happened above already + // requireTracking(t, m, barFail, barComputedRoutes, apiComputedRoutes, fooComputedRoutes) + + requireTracking(t, m, route1, apiComputedRoutes) + requireTracking(t, m, route2, apiComputedRoutes, fooComputedRoutes) + + // verify other helper methods + prototest.AssertElementsMatch(t, []*pbresource.Reference{wwwSvcRef}, m.BackendServiceRefsByRouteID(route1.Id)) + prototest.AssertElementsMatch(t, []*pbresource.Reference{apiSvcRef}, m.ParentServiceRefsByRouteID(route1.Id)) + + prototest.AssertElementsMatch(t, []*pbresource.Reference{barSvcRef}, m.BackendServiceRefsByRouteID(route2.Id)) + prototest.AssertElementsMatch(t, []*pbresource.Reference{apiSvcRef, fooSvcRef}, m.ParentServiceRefsByRouteID(route2.Id)) + + require.Empty(t, m.RouteIDsByBackendServiceRef(apiSvcRef)) + prototest.AssertElementsMatch(t, []*pbresource.ID{route1.Id, route2.Id}, m.RouteIDsByParentServiceRef(apiSvcRef)) + + prototest.AssertElementsMatch(t, []*pbresource.ID{route1.Id}, m.RouteIDsByBackendServiceRef(wwwSvcRef)) + require.Empty(t, m.RouteIDsByParentServiceRef(wwwSvcRef)) + + prototest.AssertElementsMatch(t, []*pbresource.ID{route2.Id}, m.RouteIDsByBackendServiceRef(barSvcRef)) + require.Empty(t, m.RouteIDsByParentServiceRef(barSvcRef)) + + require.Empty(t, m.RouteIDsByBackendServiceRef(fooSvcRef)) + prototest.AssertElementsMatch(t, []*pbresource.ID{route2.Id}, m.RouteIDsByParentServiceRef(fooSvcRef)) + + for _, ref := range []*pbresource.Reference{zimSvcRef, girSvcRef} { + require.Empty(t, m.RouteIDsByBackendServiceRef(ref)) + require.Empty(t, m.RouteIDsByParentServiceRef(ref)) + } + }) + + testutil.RunStep(t, "delete first route", func(t *testing.T) { + m.UntrackXRoute(route1.Id) + route1 = nil + + requireTracking(t, m, apiSvc, apiComputedRoutes) + requireTracking(t, m, wwwSvc, wwwComputedRoutes) + requireTracking(t, m, barSvc, barComputedRoutes, apiComputedRoutes, fooComputedRoutes) + + requireTracking(t, m, fooSvc, fooComputedRoutes, apiComputedRoutes) + requireTracking(t, m, zimSvc, zimComputedRoutes, apiComputedRoutes) + requireTracking(t, m, girSvc, girComputedRoutes, barComputedRoutes, apiComputedRoutes, fooComputedRoutes) + + requireTracking(t, m, apiDest, apiComputedRoutes) + requireTracking(t, m, wwwDest, wwwComputedRoutes) + + requireTracking(t, m, apiFail, apiComputedRoutes) + requireTracking(t, m, wwwFail, wwwComputedRoutes) + requireTracking(t, m, barFail, barComputedRoutes, apiComputedRoutes, fooComputedRoutes) + + requireTracking(t, m, route2, apiComputedRoutes, fooComputedRoutes) + + // verify other helper methods + prototest.AssertElementsMatch(t, []*pbresource.Reference{barSvcRef}, m.BackendServiceRefsByRouteID(route2.Id)) + prototest.AssertElementsMatch(t, []*pbresource.Reference{apiSvcRef, fooSvcRef}, m.ParentServiceRefsByRouteID(route2.Id)) + + require.Empty(t, m.RouteIDsByBackendServiceRef(apiSvcRef)) + prototest.AssertElementsMatch(t, []*pbresource.ID{route2.Id}, m.RouteIDsByParentServiceRef(apiSvcRef)) + + prototest.AssertElementsMatch(t, []*pbresource.ID{route2.Id}, m.RouteIDsByBackendServiceRef(barSvcRef)) + require.Empty(t, m.RouteIDsByParentServiceRef(barSvcRef)) + + require.Empty(t, m.RouteIDsByBackendServiceRef(fooSvcRef)) + prototest.AssertElementsMatch(t, []*pbresource.ID{route2.Id}, m.RouteIDsByParentServiceRef(fooSvcRef)) + + for _, ref := range []*pbresource.Reference{wwwSvcRef, zimSvcRef, girSvcRef} { + require.Empty(t, m.RouteIDsByBackendServiceRef(ref)) + require.Empty(t, m.RouteIDsByParentServiceRef(ref)) + } + }) + + testutil.RunStep(t, "delete all failover", func(t *testing.T) { + m.UntrackFailoverPolicy(apiFail.Id) + m.UntrackFailoverPolicy(wwwFail.Id) + m.UntrackFailoverPolicy(barFail.Id) + + apiFail = nil + wwwFail = nil + barFail = nil + + requireTracking(t, m, apiSvc, apiComputedRoutes) + requireTracking(t, m, wwwSvc, wwwComputedRoutes) + requireTracking(t, m, barSvc, barComputedRoutes, apiComputedRoutes, fooComputedRoutes) + + requireTracking(t, m, fooSvc, fooComputedRoutes) + requireTracking(t, m, zimSvc, zimComputedRoutes) + requireTracking(t, m, girSvc, girComputedRoutes) + + requireTracking(t, m, apiDest, apiComputedRoutes) + requireTracking(t, m, wwwDest, wwwComputedRoutes) + + requireTracking(t, m, route2, apiComputedRoutes, fooComputedRoutes) + + // verify other helper methods + prototest.AssertElementsMatch(t, []*pbresource.Reference{barSvcRef}, m.BackendServiceRefsByRouteID(route2.Id)) + prototest.AssertElementsMatch(t, []*pbresource.Reference{apiSvcRef, fooSvcRef}, m.ParentServiceRefsByRouteID(route2.Id)) + + require.Empty(t, m.RouteIDsByBackendServiceRef(apiSvcRef)) + prototest.AssertElementsMatch(t, []*pbresource.ID{route2.Id}, m.RouteIDsByParentServiceRef(apiSvcRef)) + + prototest.AssertElementsMatch(t, []*pbresource.ID{route2.Id}, m.RouteIDsByBackendServiceRef(barSvcRef)) + require.Empty(t, m.RouteIDsByParentServiceRef(barSvcRef)) + + require.Empty(t, m.RouteIDsByBackendServiceRef(fooSvcRef)) + prototest.AssertElementsMatch(t, []*pbresource.ID{route2.Id}, m.RouteIDsByParentServiceRef(fooSvcRef)) + + for _, ref := range []*pbresource.Reference{wwwSvcRef, zimSvcRef, girSvcRef} { + require.Empty(t, m.RouteIDsByBackendServiceRef(ref)) + require.Empty(t, m.RouteIDsByParentServiceRef(ref)) + } + }) + + testutil.RunStep(t, "delete second route", func(t *testing.T) { + m.UntrackXRoute(route2.Id) + route2 = nil + + requireTracking(t, m, apiSvc, apiComputedRoutes) + requireTracking(t, m, wwwSvc, wwwComputedRoutes) + requireTracking(t, m, barSvc, barComputedRoutes) + + requireTracking(t, m, fooSvc, fooComputedRoutes) + requireTracking(t, m, zimSvc, zimComputedRoutes) + requireTracking(t, m, girSvc, girComputedRoutes) + + requireTracking(t, m, apiDest, apiComputedRoutes) + requireTracking(t, m, wwwDest, wwwComputedRoutes) + + // verify other helper methods + for _, ref := range []*pbresource.Reference{apiSvcRef, wwwSvcRef, barSvcRef, fooSvcRef, zimSvcRef, girSvcRef} { + require.Empty(t, m.RouteIDsByBackendServiceRef(ref)) + require.Empty(t, m.RouteIDsByParentServiceRef(ref)) + } + }) +} + +func requireTracking( + t *testing.T, + mapper *Mapper, + res *pbresource.Resource, + computedRoutesIDs ...*pbresource.ID, +) { + t.Helper() + + require.NotNil(t, res) + + var ( + reqs []controller.Request + err error + ) + switch { + case resource.EqualType(types.HTTPRouteType, res.Id.Type): + reqs, err = mapper.MapHTTPRoute(context.Background(), controller.Runtime{}, res) + case resource.EqualType(types.GRPCRouteType, res.Id.Type): + reqs, err = mapper.MapGRPCRoute(context.Background(), controller.Runtime{}, res) + case resource.EqualType(types.TCPRouteType, res.Id.Type): + reqs, err = mapper.MapTCPRoute(context.Background(), controller.Runtime{}, res) + case resource.EqualType(types.DestinationPolicyType, res.Id.Type): + reqs, err = mapper.MapDestinationPolicy(context.Background(), controller.Runtime{}, res) + case resource.EqualType(catalog.FailoverPolicyType, res.Id.Type): + reqs, err = mapper.MapFailoverPolicy(context.Background(), controller.Runtime{}, res) + case resource.EqualType(catalog.ServiceType, res.Id.Type): + reqs, err = mapper.MapService(context.Background(), controller.Runtime{}, res) + default: + t.Fatalf("unhandled resource type: %s", resource.TypeToString(res.Id.Type)) + } + + require.NoError(t, err) + require.Len(t, reqs, len(computedRoutesIDs)) + for _, computedRoutesID := range computedRoutesIDs { + require.NotNil(t, computedRoutesID) + prototest.AssertContainsElement(t, reqs, controller.Request{ID: computedRoutesID}) + } +} + +func newBackendRef(name string) *pbmesh.BackendReference { + return &pbmesh.BackendReference{ + Ref: newRef(catalog.ServiceType, name), + } +} + +func newRef(typ *pbresource.Type, name string) *pbresource.Reference { + return rtest.Resource(typ, name).Reference("") +} + +func newID(typ *pbresource.Type, name string) *pbresource.ID { + return rtest.Resource(typ, name).ID() +} + +func defaultTenancy() *pbresource.Tenancy { + return &pbresource.Tenancy{ + Partition: "default", + Namespace: "default", + PeerName: "local", + } +} diff --git a/internal/mesh/internal/types/util.go b/internal/mesh/internal/types/util.go index 5a0e45d0201..2841a314e15 100644 --- a/internal/mesh/internal/types/util.go +++ b/internal/mesh/internal/types/util.go @@ -4,8 +4,11 @@ package types import ( + "fmt" + "github.com/hashicorp/consul/internal/catalog" "github.com/hashicorp/consul/internal/resource" + pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" "github.com/hashicorp/consul/proto-public/pbresource" ) @@ -50,3 +53,22 @@ func IsComputedRoutesType(typ *pbresource.Type) bool { } return false } + +// TODO: fix this format +func BackendRefToString(backendRef *pbmesh.BackendReference) string { + ref := backendRef.Ref + + s := fmt.Sprintf( + "%s/%s/%s?port=%s", + resource.TypeToString(ref.Type), + resource.TenancyToString(ref.Tenancy), + ref.Name, + backendRef.Port, + ) + + if backendRef.Datacenter != "" { + s += "&dc=" + backendRef.Datacenter + } + + return s +} diff --git a/internal/mesh/internal/types/xroute.go b/internal/mesh/internal/types/xroute.go index 572e7f53138..4a0840ab01e 100644 --- a/internal/mesh/internal/types/xroute.go +++ b/internal/mesh/internal/types/xroute.go @@ -13,6 +13,7 @@ import ( "github.com/hashicorp/consul/internal/catalog" "github.com/hashicorp/consul/internal/resource" pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" + "github.com/hashicorp/consul/proto-public/pbresource" ) type XRouteData interface { @@ -25,6 +26,59 @@ type XRouteWithRefs interface { GetUnderlyingBackendRefs() []*pbmesh.BackendReference } +type DecodedXRoute struct { + Resource *pbresource.Resource + HTTP *pbmesh.HTTPRoute + GRPC *pbmesh.GRPCRoute + TCP *pbmesh.TCPRoute +} + +var _ XRouteWithRefs = (*DecodedXRoute)(nil) + +func (d *DecodedXRoute) ToDecodedHTTPRoute() *DecodedHTTPRoute { + return &DecodedHTTPRoute{Resource: d.Resource, Data: d.HTTP} +} + +func (d *DecodedXRoute) ToDecodedGRPCRoute() *DecodedGRPCRoute { + return &DecodedGRPCRoute{Resource: d.Resource, Data: d.GRPC} +} + +func (d *DecodedXRoute) ToDecodedTCPRoute() *DecodedTCPRoute { + return &DecodedTCPRoute{Resource: d.Resource, Data: d.TCP} +} + +func (d *DecodedXRoute) GetParentRefs() []*pbmesh.ParentReference { + if d == nil { + return nil + } + switch { + case d.HTTP != nil: + return d.HTTP.GetParentRefs() + case d.GRPC != nil: + return d.GRPC.GetParentRefs() + case d.TCP != nil: + return d.TCP.GetParentRefs() + default: + return nil + } +} + +func (d *DecodedXRoute) GetUnderlyingBackendRefs() []*pbmesh.BackendReference { + if d == nil { + return nil + } + switch { + case d.HTTP != nil: + return d.HTTP.GetUnderlyingBackendRefs() + case d.GRPC != nil: + return d.GRPC.GetUnderlyingBackendRefs() + case d.TCP != nil: + return d.TCP.GetUnderlyingBackendRefs() + default: + return nil + } +} + type portedRefKey struct { Key resource.ReferenceKey Port string diff --git a/proto-public/pbmesh/v1alpha1/computed_routes.pb.go b/proto-public/pbmesh/v1alpha1/computed_routes.pb.go index 605e4fbd679..ec0838d1cdf 100644 --- a/proto-public/pbmesh/v1alpha1/computed_routes.pb.go +++ b/proto-public/pbmesh/v1alpha1/computed_routes.pb.go @@ -743,7 +743,9 @@ type BackendTargetDetails struct { unknownFields protoimpl.UnknownFields // identity info - BackendRef *BackendReference `protobuf:"bytes,1,opt,name=backend_ref,json=backendRef,proto3" json:"backend_ref,omitempty"` + BackendRef *BackendReference `protobuf:"bytes,1,opt,name=backend_ref,json=backendRef,proto3" json:"backend_ref,omitempty"` + // NullRouteTraffic indicates that this backend target should + // have all traffic error with a 5xx error or equivalent. NullRouteTraffic bool `protobuf:"varint,2,opt,name=null_route_traffic,json=nullRouteTraffic,proto3" json:"null_route_traffic,omitempty"` Service *v1alpha1.Service `protobuf:"bytes,3,opt,name=service,proto3" json:"service,omitempty"` FailoverPolicy *v1alpha1.FailoverPolicy `protobuf:"bytes,4,opt,name=failover_policy,json=failoverPolicy,proto3" json:"failover_policy,omitempty"` diff --git a/proto-public/pbmesh/v1alpha1/computed_routes.proto b/proto-public/pbmesh/v1alpha1/computed_routes.proto index d48d417d682..5fe9d884a82 100644 --- a/proto-public/pbmesh/v1alpha1/computed_routes.proto +++ b/proto-public/pbmesh/v1alpha1/computed_routes.proto @@ -91,6 +91,8 @@ message BackendTargetDetails { // identity info BackendReference backend_ref = 1; + // NullRouteTraffic indicates that this backend target should + // have all traffic error with a 5xx error or equivalent. bool null_route_traffic = 2; hashicorp.consul.catalog.v1alpha1.Service service = 3; From 4c180d7c41e13ec930662f47144a0d83bcfb12f3 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Mon, 14 Aug 2023 10:31:25 -0500 Subject: [PATCH 02/49] add more tests and fix a few bugs --- internal/mesh/exports.go | 6 + .../internal/controllers/routes/controller.go | 3 + .../controllers/routes/controller_test.go | 574 +++++++++++++++++- .../internal/controllers/routes/generate.go | 370 +++++++---- .../routes/{cross.go => ref_validation.go} | 74 ++- .../controllers/routes/ref_validation_test.go | 231 +++++++ .../internal/controllers/routes/sort_rules.go | 4 +- .../controllers/routes/status_xroute.go | 35 +- .../mesh/internal/controllers/routes/util.go | 13 +- .../mesh/internal/types/computed_routes.go | 5 + internal/mesh/internal/types/util.go | 26 +- internal/mesh/internal/types/xroute.go | 20 + .../pbmesh/v1alpha1/computed_routes.pb.go | 85 ++- .../pbmesh/v1alpha1/computed_routes.proto | 4 - 14 files changed, 1227 insertions(+), 223 deletions(-) rename internal/mesh/internal/controllers/routes/{cross.go => ref_validation.go} (63%) create mode 100644 internal/mesh/internal/controllers/routes/ref_validation_test.go diff --git a/internal/mesh/exports.go b/internal/mesh/exports.go index dc3c31649ba..fa7cd048592 100644 --- a/internal/mesh/exports.go +++ b/internal/mesh/exports.go @@ -70,6 +70,12 @@ var ( SidecarProxyStatusReasonNonMeshProtocolDestinationPort = status.StatusReasonNonMeshProtocolDestinationPort ) +const ( + // Important constants + + NullRouteBackend = types.NullRouteBackend +) + // RegisterTypes adds all resource types within the "mesh" API group // to the given type registry func RegisterTypes(r resource.Registry) { diff --git a/internal/mesh/internal/controllers/routes/controller.go b/internal/mesh/internal/controllers/routes/controller.go index 859b11e26fa..c995e6ac705 100644 --- a/internal/mesh/internal/controllers/routes/controller.go +++ b/internal/mesh/internal/controllers/routes/controller.go @@ -100,17 +100,20 @@ func ensureComputedRoutesIsSynced( prev *types.DecodedComputedRoutes, ) error { if result.Data == nil { + logger.Info("DELETE WRITE") return deleteComputedRoutes(ctx, logger, client, prev) } // Upsert the resource if changed. if prev != nil { if proto.Equal(prev.Data, result.Data) { + logger.Info("SKIPPING WRITE") return nil // no change } result.ID = prev.Resource.Id } + logger.Info("UPSERT WRITE") return upsertComputedRoutes(ctx, logger, client, result.ID, result.OwnerID, result.Data) } diff --git a/internal/mesh/internal/controllers/routes/controller_test.go b/internal/mesh/internal/controllers/routes/controller_test.go index 24787b32bd8..672d2853fea 100644 --- a/internal/mesh/internal/controllers/routes/controller_test.go +++ b/internal/mesh/internal/controllers/routes/controller_test.go @@ -5,6 +5,7 @@ package routes import ( "context" + "fmt" "testing" "github.com/stretchr/testify/require" @@ -20,6 +21,7 @@ import ( "github.com/hashicorp/consul/proto-public/pbresource" "github.com/hashicorp/consul/proto/private/prototest" "github.com/hashicorp/consul/sdk/testutil" + "github.com/hashicorp/consul/sdk/testutil/retry" ) type controllerSuite struct { @@ -52,10 +54,22 @@ func (suite *controllerSuite) TestController() { mgr.SetRaftLeader(true) go mgr.Run(suite.ctx) + backendName := func(name, port string) string { + return fmt.Sprintf("catalog.v1alpha1.Service/default.local.default/%s?port=%s", name, port) + } + + var ( + apiServiceRef = rtest.Resource(catalog.ServiceType, "api").Reference("") + fooServiceRef = rtest.Resource(catalog.ServiceType, "foo").Reference("") + barServiceRef = rtest.Resource(catalog.ServiceType, "bar").Reference("") + + computedRoutesID = rtest.Resource(types.ComputedRoutesType, "api").ID() + ) + // Start out by creating a single port service and let it create the // default mesh config for tcp. - serviceData := &pbcatalog.Service{ + apiServiceData := &pbcatalog.Service{ Workloads: &pbcatalog.WorkloadSelector{ Prefixes: []string{"api-"}, }, @@ -67,40 +81,557 @@ func (suite *controllerSuite) TestController() { } _ = rtest.Resource(catalog.ServiceType, "api"). - WithData(suite.T(), serviceData). + WithData(suite.T(), apiServiceData). Write(suite.T(), suite.client) + var lastVersion string testutil.RunStep(suite.T(), "default tcp route", func(t *testing.T) { // Check that the mesh config resource exists and it has one port that is the default. - computedRoutesID := rtest.Resource(types.ComputedRoutesType, "api").ID() - computedRoutes := suite.client.WaitForNewVersion(suite.T(), computedRoutesID, "") + expect := &pbmesh.ComputedRoutes{ + PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ + "tcp": { + UsingDefaultConfig: true, + Config: &pbmesh.ComputedPortRoutes_Tcp{ + Tcp: &pbmesh.InterpretedTCPRoute{ + ParentRef: newParentRef(apiServiceRef, "tcp"), + Rules: []*pbmesh.InterpretedTCPRouteRule{{ + BackendRefs: []*pbmesh.InterpretedTCPBackendRef{{ + BackendTarget: backendName("api", "tcp"), + }}, + }}, + }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("api", "tcp"): { + BackendRef: newBackendRef(apiServiceRef, "tcp", ""), + Service: apiServiceData, + }, + }, + }, + }, + } + + lastVersion = requireNewComputedRoutesVersion(t, suite.client, computedRoutesID, "", expect) + }) - apiServiceRef := rtest.Resource(catalog.ServiceType, "api").Reference("") + // Let the default http/http2/grpc routes get created. + apiServiceData = &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"api-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "tcp", Protocol: pbcatalog.Protocol_PROTOCOL_TCP}, + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + {TargetPort: "http2", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP2}, + {TargetPort: "grpc", Protocol: pbcatalog.Protocol_PROTOCOL_GRPC}, + }, + } + + _ = rtest.Resource(catalog.ServiceType, "api"). + WithData(suite.T(), apiServiceData). + Write(suite.T(), suite.client) + + // also create the fooService so we can point to it. + fooServiceData := &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"foo-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "tcp", Protocol: pbcatalog.Protocol_PROTOCOL_TCP}, + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + {TargetPort: "http2", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP2}, + {TargetPort: "grpc", Protocol: pbcatalog.Protocol_PROTOCOL_GRPC}, + }, + } + + _ = rtest.Resource(catalog.ServiceType, "foo"). + WithData(suite.T(), fooServiceData). + Write(suite.T(), suite.client) + + testutil.RunStep(suite.T(), "default other routes", func(t *testing.T) { + // Check that the mesh config resource exists and it has one port that is the default. expect := &pbmesh.ComputedRoutes{ PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ "tcp": { + UsingDefaultConfig: true, Config: &pbmesh.ComputedPortRoutes_Tcp{ Tcp: &pbmesh.InterpretedTCPRoute{ ParentRef: newParentRef(apiServiceRef, "tcp"), Rules: []*pbmesh.InterpretedTCPRouteRule{{ BackendRefs: []*pbmesh.InterpretedTCPBackendRef{{ - BackendTarget: "catalog.v1alpha1.Service/default.local.default/api?port=tcp", + BackendTarget: backendName("api", "tcp"), }}, }}, }, }, Targets: map[string]*pbmesh.BackendTargetDetails{ - "catalog.v1alpha1.Service/default.local.default/api?port=tcp": { + backendName("api", "tcp"): { BackendRef: newBackendRef(apiServiceRef, "tcp", ""), - Service: serviceData, + Service: apiServiceData, + }, + }, + }, + "http": { + UsingDefaultConfig: true, + Config: &pbmesh.ComputedPortRoutes_Http{ + Http: &pbmesh.InterpretedHTTPRoute{ + ParentRef: newParentRef(apiServiceRef, "http"), + Rules: []*pbmesh.InterpretedHTTPRouteRule{{ + Matches: []*pbmesh.HTTPRouteMatch{{ + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, + Value: "/", + }, + }}, + BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendTarget: backendName("api", "http"), + }}, + }}, + }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("api", "http"): { + BackendRef: newBackendRef(apiServiceRef, "http", ""), + Service: apiServiceData, + }, + }, + }, + "http2": { + UsingDefaultConfig: true, + Config: &pbmesh.ComputedPortRoutes_Http{ + Http: &pbmesh.InterpretedHTTPRoute{ + ParentRef: newParentRef(apiServiceRef, "http2"), + Rules: []*pbmesh.InterpretedHTTPRouteRule{{ + Matches: []*pbmesh.HTTPRouteMatch{{ + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, + Value: "/", + }, + }}, + BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendTarget: backendName("api", "http2"), + }}, + }}, + }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("api", "http2"): { + BackendRef: newBackendRef(apiServiceRef, "http2", ""), + Service: apiServiceData, + }, + }, + }, + "grpc": { + UsingDefaultConfig: true, + Config: &pbmesh.ComputedPortRoutes_Grpc{ + Grpc: &pbmesh.InterpretedGRPCRoute{ + ParentRef: newParentRef(apiServiceRef, "grpc"), + Rules: []*pbmesh.InterpretedGRPCRouteRule{{ + Matches: []*pbmesh.GRPCRouteMatch{{}}, + BackendRefs: []*pbmesh.InterpretedGRPCBackendRef{{ + BackendTarget: backendName("api", "grpc"), + }}, + }}, + }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("api", "grpc"): { + BackendRef: newBackendRef(apiServiceRef, "grpc", ""), + Service: apiServiceData, + }, + }, + }, + }, + } + + lastVersion = requireNewComputedRoutesVersion(t, suite.client, computedRoutesID, lastVersion, expect) + }) + + // Customize each route type. + + tcpRoute1 := &pbmesh.TCPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + newParentRef(newRef(catalog.ServiceType, "api"), "tcp"), + }, + Rules: []*pbmesh.TCPRouteRule{{ + BackendRefs: []*pbmesh.TCPBackendRef{{ + BackendRef: newBackendRef(fooServiceRef, "", ""), + }}, + }}, + } + _ = rtest.Resource(types.TCPRouteType, "api-tcp-route1"). + WithData(suite.T(), tcpRoute1). + Write(suite.T(), suite.client) + + httpRoute1 := &pbmesh.HTTPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + newParentRef(newRef(catalog.ServiceType, "api"), "http"), + newParentRef(newRef(catalog.ServiceType, "api"), "http2"), + }, + Rules: []*pbmesh.HTTPRouteRule{{ + BackendRefs: []*pbmesh.HTTPBackendRef{{ + BackendRef: newBackendRef(fooServiceRef, "", ""), + }}, + }}, + } + _ = rtest.Resource(types.HTTPRouteType, "api-http-route1"). + WithData(suite.T(), httpRoute1). + Write(suite.T(), suite.client) + + grpcRoute1 := &pbmesh.GRPCRoute{ + ParentRefs: []*pbmesh.ParentReference{ + newParentRef(newRef(catalog.ServiceType, "api"), "grpc"), + }, + Rules: []*pbmesh.GRPCRouteRule{{ + BackendRefs: []*pbmesh.GRPCBackendRef{{ + BackendRef: newBackendRef(fooServiceRef, "", ""), + }}, + }}, + } + _ = rtest.Resource(types.GRPCRouteType, "api-grpc-route1"). + WithData(suite.T(), grpcRoute1). + Write(suite.T(), suite.client) + + testutil.RunStep(suite.T(), "one of each", func(t *testing.T) { + expect := &pbmesh.ComputedRoutes{ + PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ + "tcp": { + Config: &pbmesh.ComputedPortRoutes_Tcp{ + Tcp: &pbmesh.InterpretedTCPRoute{ + ParentRef: newParentRef(apiServiceRef, "tcp"), + Rules: []*pbmesh.InterpretedTCPRouteRule{{ + BackendRefs: []*pbmesh.InterpretedTCPBackendRef{{ + BackendTarget: backendName("foo", "tcp"), + }}, + }}, + }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("foo", "tcp"): { + BackendRef: newBackendRef(fooServiceRef, "tcp", ""), + Service: fooServiceData, + }, + }, + }, + "http": { + Config: &pbmesh.ComputedPortRoutes_Http{ + Http: &pbmesh.InterpretedHTTPRoute{ + ParentRef: newParentRef(apiServiceRef, "http"), + Rules: []*pbmesh.InterpretedHTTPRouteRule{ + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendTarget: backendName("foo", "http"), + }}, + }, + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, + }, + }, + }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("foo", "http"): { + BackendRef: newBackendRef(fooServiceRef, "http", ""), + Service: fooServiceData, + }, + }, + }, + "grpc": { + Config: &pbmesh.ComputedPortRoutes_Grpc{ + Grpc: &pbmesh.InterpretedGRPCRoute{ + ParentRef: newParentRef(apiServiceRef, "grpc"), + Rules: []*pbmesh.InterpretedGRPCRouteRule{ + { + Matches: []*pbmesh.GRPCRouteMatch{{}}, + BackendRefs: []*pbmesh.InterpretedGRPCBackendRef{{ + BackendTarget: backendName("foo", "grpc"), + }}, + }, + { + Matches: []*pbmesh.GRPCRouteMatch{{}}, + BackendRefs: []*pbmesh.InterpretedGRPCBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, + }, + }, + }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("foo", "grpc"): { + BackendRef: newBackendRef(fooServiceRef, "grpc", ""), + Service: fooServiceData, + }, + }, + }, + "http2": { + Config: &pbmesh.ComputedPortRoutes_Http{ + Http: &pbmesh.InterpretedHTTPRoute{ + ParentRef: newParentRef(apiServiceRef, "http2"), + Rules: []*pbmesh.InterpretedHTTPRouteRule{ + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendTarget: backendName("foo", "http2"), + }}, + }, + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, + }, + }, + }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("foo", "http2"): { + BackendRef: newBackendRef(fooServiceRef, "http2", ""), + Service: fooServiceData, }, }, }, }, } - suite.requireComputedRoutes(expect, computedRoutes) + lastVersion = requireNewComputedRoutesVersion(t, suite.client, computedRoutesID, lastVersion, expect) + }) + + // Add another route, with a bad mapping. + + tcpRoute2 := &pbmesh.TCPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + newParentRef(newRef(catalog.ServiceType, "api"), "tcp"), + }, + Rules: []*pbmesh.TCPRouteRule{{ + BackendRefs: []*pbmesh.TCPBackendRef{{ + BackendRef: newBackendRef(barServiceRef, "", ""), + }}, + }}, + } + _ = rtest.Resource(types.TCPRouteType, "api-tcp-route2"). + WithData(suite.T(), tcpRoute2). + Write(suite.T(), suite.client) + + httpRoute2 := &pbmesh.HTTPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + newParentRef(newRef(catalog.ServiceType, "api"), "http"), + newParentRef(newRef(catalog.ServiceType, "api"), "http2"), + }, + Rules: []*pbmesh.HTTPRouteRule{{ + Matches: []*pbmesh.HTTPRouteMatch{{ + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, + Value: "/healthz", + }, + }}, + BackendRefs: []*pbmesh.HTTPBackendRef{{ + BackendRef: newBackendRef(barServiceRef, "", ""), + }}, + }}, + } + _ = rtest.Resource(types.HTTPRouteType, "api-http-route2"). + WithData(suite.T(), httpRoute2). + Write(suite.T(), suite.client) + + grpcRoute2 := &pbmesh.GRPCRoute{ + ParentRefs: []*pbmesh.ParentReference{ + newParentRef(newRef(catalog.ServiceType, "api"), "grpc"), + }, + Rules: []*pbmesh.GRPCRouteRule{{ + Matches: []*pbmesh.GRPCRouteMatch{{ + Method: &pbmesh.GRPCMethodMatch{ + Type: pbmesh.GRPCMethodMatchType_GRPC_METHOD_MATCH_TYPE_EXACT, + Service: "billing", + Method: "charge", + }, + }}, + BackendRefs: []*pbmesh.GRPCBackendRef{{ + BackendRef: newBackendRef(barServiceRef, "", ""), + }}, + }}, + } + _ = rtest.Resource(types.GRPCRouteType, "api-grpc-route2"). + WithData(suite.T(), grpcRoute2). + Write(suite.T(), suite.client) + + testutil.RunStep(suite.T(), "one good one bad route", func(t *testing.T) { + expect := &pbmesh.ComputedRoutes{ + PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ + "tcp": { + Config: &pbmesh.ComputedPortRoutes_Tcp{ + Tcp: &pbmesh.InterpretedTCPRoute{ + ParentRef: newParentRef(apiServiceRef, "tcp"), + Rules: []*pbmesh.InterpretedTCPRouteRule{ + { + BackendRefs: []*pbmesh.InterpretedTCPBackendRef{{ + BackendTarget: backendName("foo", "tcp"), + }}, + }, + { + BackendRefs: []*pbmesh.InterpretedTCPBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, + }, + }, + }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("foo", "tcp"): { + BackendRef: newBackendRef(fooServiceRef, "tcp", ""), + Service: fooServiceData, + }, + }, + }, + "http": { + Config: &pbmesh.ComputedPortRoutes_Http{ + Http: &pbmesh.InterpretedHTTPRoute{ + ParentRef: newParentRef(apiServiceRef, "http"), + Rules: []*pbmesh.InterpretedHTTPRouteRule{ + { + Matches: []*pbmesh.HTTPRouteMatch{{ + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, + Value: "/healthz", + }, + }}, + BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, + }, + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendTarget: backendName("foo", "http"), + }}, + }, + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, + }, + }, + }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("foo", "http"): { + BackendRef: newBackendRef(fooServiceRef, "http", ""), + Service: fooServiceData, + }, + }, + }, + "grpc": { + Config: &pbmesh.ComputedPortRoutes_Grpc{ + Grpc: &pbmesh.InterpretedGRPCRoute{ + ParentRef: newParentRef(apiServiceRef, "grpc"), + Rules: []*pbmesh.InterpretedGRPCRouteRule{ + { + Matches: []*pbmesh.GRPCRouteMatch{{}}, + BackendRefs: []*pbmesh.InterpretedGRPCBackendRef{{ + BackendTarget: backendName("foo", "grpc"), + }}, + }, + { + Matches: []*pbmesh.GRPCRouteMatch{{ + Method: &pbmesh.GRPCMethodMatch{ + Type: pbmesh.GRPCMethodMatchType_GRPC_METHOD_MATCH_TYPE_EXACT, + Service: "billing", + Method: "charge", + }, + }}, + BackendRefs: []*pbmesh.InterpretedGRPCBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, + }, + { + Matches: []*pbmesh.GRPCRouteMatch{{}}, + BackendRefs: []*pbmesh.InterpretedGRPCBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, + }, + }, + }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("foo", "grpc"): { + BackendRef: newBackendRef(fooServiceRef, "grpc", ""), + Service: fooServiceData, + }, + }, + }, + "http2": { + Config: &pbmesh.ComputedPortRoutes_Http{ + Http: &pbmesh.InterpretedHTTPRoute{ + ParentRef: newParentRef(apiServiceRef, "http2"), + Rules: []*pbmesh.InterpretedHTTPRouteRule{ + { + Matches: []*pbmesh.HTTPRouteMatch{{ + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, + Value: "/healthz", + }, + }}, + BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, + }, + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendTarget: backendName("foo", "http2"), + }}, + }, + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, + }, + }, + }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("foo", "http2"): { + BackendRef: newBackendRef(fooServiceRef, "http2", ""), + Service: fooServiceData, + }, + }, + }, + }, + } + + lastVersion = requireNewComputedRoutesVersion(t, suite.client, computedRoutesID, lastVersion, expect) + }) + + // Remove the mesh port from api service. + + apiServiceData = &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"api-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "tcp", Protocol: pbcatalog.Protocol_PROTOCOL_TCP}, + {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + {TargetPort: "http2", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP2}, + {TargetPort: "grpc", Protocol: pbcatalog.Protocol_PROTOCOL_GRPC}, + }, + } + + _ = rtest.Resource(catalog.ServiceType, "api"). + WithData(suite.T(), apiServiceData). + Write(suite.T(), suite.client) + + testutil.RunStep(suite.T(), "entire generated resource is deleted", func(t *testing.T) { + retry.Run(t, func(r *retry.R) { + suite.client.RequireResourceNotFound(r, computedRoutesID) + }) }) } @@ -120,11 +651,26 @@ func newBackendRef(ref *pbresource.Reference, port string, datacenter string) *p } } -func (suite *controllerSuite) requireComputedRoutes(expected *pbmesh.ComputedRoutes, resource *pbresource.Resource) { - suite.T().Helper() - var mc pbmesh.ComputedRoutes - require.NoError(suite.T(), resource.Data.UnmarshalTo(&mc)) - prototest.AssertDeepEqual(suite.T(), expected, &mc) +func requireNewComputedRoutesVersion( + t *testing.T, + client *rtest.Client, + id *pbresource.ID, + version string, + expected *pbmesh.ComputedRoutes, +) string { + t.Helper() + + var nextVersion string + retry.Run(t, func(r *retry.R) { + res := client.WaitForNewVersion(r, id, version) + + var mc pbmesh.ComputedRoutes + require.NoError(r, res.Data.UnmarshalTo(&mc)) + prototest.AssertDeepEqual(r, expected, &mc) + + nextVersion = res.Version + }) + return nextVersion } func TestController(t *testing.T) { diff --git a/internal/mesh/internal/controllers/routes/generate.go b/internal/mesh/internal/controllers/routes/generate.go index 453c0dc750d..c057d0fc635 100644 --- a/internal/mesh/internal/controllers/routes/generate.go +++ b/internal/mesh/internal/controllers/routes/generate.go @@ -92,85 +92,80 @@ func compile( res *pbresource.Resource, xroute types.XRouteData, ) { - port := "" - found := false + logger.Info("RBOYER working out route", "r", resource.IDToString(res.Id)) + var ports []string for _, ref := range xroute.GetParentRefs() { if resource.ReferenceOrIDMatch(ref.Ref, parentServiceRef) { - found = true - port = ref.Port - break + ports = append(ports, ref.Port) } } - if !found { + if len(ports) == 0 { return // not relevant to this mesh config - } - var node *inputRouteNode - switch route := xroute.(type) { - case *pbmesh.HTTPRoute: - node = compileHTTPRouteNode(port, res, route) - case *pbmesh.GRPCRoute: - node = compileGRPCRouteNode(port, res, route) - case *pbmesh.TCPRoute: - node = compileTCPRouteNode(port, res, route) - default: - return // unknown xroute type (impossible) - } + for _, port := range ports { + // Check if the user provided port is actually valid. + nullRouteTraffic := (parentServiceDec == nil) + if port != "" { + if _, ok := allowedPortProtocols[port]; !ok { + nullRouteTraffic = true + } + } - // Check if the user provided port is actually valid. - if port != "" { - if _, ok := allowedPortProtocols[port]; !ok { - for _, details := range node.NewTargets { - details.NullRouteTraffic = true + var node *inputRouteNode + switch route := xroute.(type) { + case *pbmesh.HTTPRoute: + if nullRouteTraffic { + node := newInputRouteNode(port) + setupDefaultHTTPRouteNode(node, types.NullRouteBackend) + } else { + node = compileHTTPRouteNode(parentServiceRef, port, res, route, related) + } + case *pbmesh.GRPCRoute: + if nullRouteTraffic { + node := newInputRouteNode(port) + setupDefaultGRPCRouteNode(node, types.NullRouteBackend) + } else { + node = compileGRPCRouteNode(parentServiceRef, port, res, route, related) } + case *pbmesh.TCPRoute: + if nullRouteTraffic { + node := newInputRouteNode(port) + setupDefaultTCPRouteNode(node, types.NullRouteBackend) + } else { + node = compileTCPRouteNode(parentServiceRef, port, res, route, related) + } + default: + return // unknown xroute type (impossible) } - } - if node.ParentPort == "" { - // Do a port explosion. - for port := range allowedPortProtocols { - nodeCopy := node.Clone() - nodeCopy.ParentPort = port - routeNodesByPort[nodeCopy.ParentPort] = append(routeNodesByPort[nodeCopy.ParentPort], nodeCopy) + if node.ParentPort == "" { + // Do a port explosion. + for port := range allowedPortProtocols { + nodeCopy := node.Clone() + nodeCopy.ParentPort = port + routeNodesByPort[nodeCopy.ParentPort] = append(routeNodesByPort[nodeCopy.ParentPort], nodeCopy) + } + } else { + routeNodesByPort[node.ParentPort] = append(routeNodesByPort[node.ParentPort], node) } - } else { - routeNodesByPort[node.ParentPort] = append(routeNodesByPort[node.ParentPort], node) } }) - // Fill in defaults where there was no xroute defined + // Fill in defaults where there was no xroute defined at all. for port, protocol := range allowedPortProtocols { - // always add the parent as a possible target due to catch-all - defaultBackendRef := &pbmesh.BackendReference{ - Ref: parentServiceRef, - Port: port, - } - - routeNode, ok := routeNodesByPort[port] - if ok { - // TODO: ignore this whole section? - - // TODO: figure out how to add the catchall route/match section like in v1. - // - // Just add it to one of them. - defaultBackendTarget := routeNode[0].AddTarget(defaultBackendRef, &pbmesh.BackendTargetDetails{ - BackendRef: defaultBackendRef, - // TODO - }) - _ = defaultBackendTarget - } else { + if _, ok := routeNodesByPort[port]; !ok { var typ *pbresource.Type switch protocol { case pbcatalog.Protocol_PROTOCOL_HTTP2: - fallthrough // to HTTP + typ = types.HTTPRouteType case pbcatalog.Protocol_PROTOCOL_HTTP: typ = types.HTTPRouteType case pbcatalog.Protocol_PROTOCOL_GRPC: typ = types.GRPCRouteType case pbcatalog.Protocol_PROTOCOL_TCP: - fallthrough // to default + typ = types.TCPRouteType default: typ = types.TCPRouteType } @@ -214,16 +209,18 @@ func compile( // Now we can do the big sort. gammaSortRouteRules(top) - // Inject catch-all rules to ensure stray requests will ultimately end - // up routing to the parent ref. + // Inject catch-all rules to ensure stray requests will explicitly be blackholed. if !top.Default { - defaultRouteNode := createDefaultRouteNode(parentServiceRef, port, top.RouteType) - top.AppendRulesFrom(defaultRouteNode) - top.AddTargetsFrom(defaultRouteNode) + if !types.IsTCPRouteType(top.RouteType) { + // There are no match criteria on a TCPRoute, so never a need + // to add a catch-all. + appendDefaultRouteNode(top, types.NullRouteBackend) + } } mc := &pbmesh.ComputedPortRoutes{ - Targets: top.NewTargets, + UsingDefaultConfig: top.Default, + Targets: top.NewTargets, } parentRef := &pbmesh.ParentReference{ Ref: parentServiceRef, @@ -261,6 +258,8 @@ func compile( meshConfig.PortedConfigs[port] = mc + // TODO: prune dead targets from targets map + for _, details := range mc.Targets { svcRef := details.BackendRef.Ref @@ -269,19 +268,18 @@ func compile( destConfig := related.GetDestinationPolicy(svcRef) if svc == nil { - details.NullRouteTraffic = true - } else { - details.Service = svc.Data + panic("impossible at this point; should already have been handled before getting here") + } + details.Service = svc.Data - if failoverPolicy != nil { - details.FailoverPolicy = catalog.SimplifyFailoverPolicy( - svc.Data, - failoverPolicy.Data, - ) - } - if destConfig != nil { - details.DestinationPolicy = destConfig.Data - } + if failoverPolicy != nil { + details.FailoverPolicy = catalog.SimplifyFailoverPolicy( + svc.Data, + failoverPolicy.Data, + ) + } + if destConfig != nil { + details.DestinationPolicy = destConfig.Data } } @@ -301,10 +299,13 @@ func compile( } func compileHTTPRouteNode( + parentServiceRef *pbresource.Reference, port string, res *pbresource.Resource, route *pbmesh.HTTPRoute, + serviceGetter serviceGetter, ) *inputRouteNode { + route = protoClone(route) node := newInputRouteNode(port) dec := &types.DecodedHTTPRoute{ @@ -329,33 +330,55 @@ func compileHTTPRouteNode( // If no matches are specified, the default is a prefix path match // on “/”, which has the effect of matching every HTTP request. if len(irule.Matches) == 0 { - irule.Matches = []*pbmesh.HTTPRouteMatch{{ - Path: &pbmesh.HTTPPathMatch{ - Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, - Value: "/", - }, - }} + irule.Matches = defaultHTTPRouteMatches() } for _, match := range irule.Matches { if match.Path == nil { // Path specifies a HTTP request path matcher. If this // field is not specified, a default prefix match on // the “/” path is provided. - match.Path = &pbmesh.HTTPPathMatch{ - Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, - Value: "/", - } + match.Path = defaultHTTPPathMatch() } } for _, backendRef := range rule.BackendRefs { - details := &pbmesh.BackendTargetDetails{ - BackendRef: backendRef.BackendRef, - // TODO + // Infer port name from parent ref. + if backendRef.BackendRef.Port == "" { + backendRef.BackendRef.Port = port + } + + var ( + backendTarget string + backendSvc = serviceGetter.GetService(backendRef.BackendRef.Ref) + ) + + if backendSvc == nil { + backendTarget = types.NullRouteBackend + } else { + found := false + inMesh := false + for _, port := range backendSvc.Data.Ports { + if port.Protocol == pbcatalog.Protocol_PROTOCOL_MESH { + inMesh = true + continue // skip + } + if port.TargetPort == backendRef.BackendRef.Port { + found = true + } + } + + if inMesh && found { + details := &pbmesh.BackendTargetDetails{ + BackendRef: backendRef.BackendRef, + // TODO + } + backendTarget = node.AddTarget(backendRef.BackendRef, details) + } else { + backendTarget = types.NullRouteBackend + } } - key := node.AddTarget(backendRef.BackendRef, details) ibr := &pbmesh.InterpretedHTTPBackendRef{ - BackendTarget: key, + BackendTarget: backendTarget, Weight: backendRef.Weight, Filters: backendRef.Filters, } @@ -365,15 +388,18 @@ func compileHTTPRouteNode( node.HTTPRules = append(node.HTTPRules, irule) } - // TODO: finish populating backendrefs using parentrefs if target unspecified return node } func compileGRPCRouteNode( + parentServiceRef *pbresource.Reference, port string, res *pbresource.Resource, route *pbmesh.GRPCRoute, + serviceGetter serviceGetter, ) *inputRouteNode { + route = protoClone(route) + node := newInputRouteNode(port) dec := &types.DecodedGRPCRoute{ Resource: res, @@ -396,17 +422,47 @@ func compileGRPCRouteNode( // // If no matches are specified, the implementation MUST match every gRPC request. if len(irule.Matches) == 0 { - irule.Matches = []*pbmesh.GRPCRouteMatch{{}} + irule.Matches = defaultGRPCRouteMatches() } for _, backendRef := range rule.BackendRefs { - details := &pbmesh.BackendTargetDetails{ - BackendRef: backendRef.BackendRef, - // TODO + // Infer port name from parent ref. + if backendRef.BackendRef.Port == "" { + backendRef.BackendRef.Port = port + } + + var ( + backendTarget string + backendSvc = serviceGetter.GetService(backendRef.BackendRef.Ref) + ) + if backendSvc == nil { + backendTarget = types.NullRouteBackend + } else { + found := false + inMesh := false + for _, port := range backendSvc.Data.Ports { + if port.Protocol == pbcatalog.Protocol_PROTOCOL_MESH { + inMesh = true + continue // skip + } + if port.TargetPort == backendRef.BackendRef.Port { + found = true + } + } + + if inMesh && found { + details := &pbmesh.BackendTargetDetails{ + BackendRef: backendRef.BackendRef, + // TODO + } + backendTarget = node.AddTarget(backendRef.BackendRef, details) + } else { + backendTarget = types.NullRouteBackend + } } - key := node.AddTarget(backendRef.BackendRef, details) + ibr := &pbmesh.InterpretedGRPCBackendRef{ - BackendTarget: key, + BackendTarget: backendTarget, Weight: backendRef.Weight, Filters: backendRef.Filters, } @@ -416,15 +472,18 @@ func compileGRPCRouteNode( node.GRPCRules = append(node.GRPCRules, irule) } - // TODO: finish populating backendrefs using parentrefs if target unspecified return node } func compileTCPRouteNode( + parentServiceRef *pbresource.Reference, port string, res *pbresource.Resource, route *pbmesh.TCPRoute, + serviceGetter serviceGetter, ) *inputRouteNode { + route = protoClone(route) + node := newInputRouteNode(port) dec := &types.DecodedTCPRoute{ Resource: res, @@ -442,13 +501,43 @@ func compileTCPRouteNode( // https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.TCPRoute for _, backendRef := range rule.BackendRefs { - details := &pbmesh.BackendTargetDetails{ - BackendRef: backendRef.BackendRef, - // TODO + // Infer port name from parent ref. + if backendRef.BackendRef.Port == "" { + backendRef.BackendRef.Port = port } - key := node.AddTarget(backendRef.BackendRef, details) + + var ( + backendTarget string + backendSvc = serviceGetter.GetService(backendRef.BackendRef.Ref) + ) + if backendSvc == nil { + backendTarget = types.NullRouteBackend + } else { + found := false + inMesh := false + for _, port := range backendSvc.Data.Ports { + if port.Protocol == pbcatalog.Protocol_PROTOCOL_MESH { + inMesh = true + continue // skip + } + if port.TargetPort == backendRef.BackendRef.Port { + found = true + } + } + + if inMesh && found { + details := &pbmesh.BackendTargetDetails{ + BackendRef: backendRef.BackendRef, + // TODO + } + backendTarget = node.AddTarget(backendRef.BackendRef, details) + } else { + backendTarget = types.NullRouteBackend + } + } + ibr := &pbmesh.InterpretedTCPBackendRef{ - BackendTarget: key, + BackendTarget: backendTarget, Weight: backendRef.Weight, } irule.BackendRefs = append(irule.BackendRefs, ibr) @@ -457,7 +546,6 @@ func compileTCPRouteNode( node.TCPRules = append(node.TCPRules, irule) } - // TODO: finish populating backendrefs using parentrefs if target unspecified return node } @@ -493,22 +581,41 @@ func createDefaultRouteNode( return routeNode } +func appendDefaultRouteNode( + routeNode *inputRouteNode, + defaultBackendTarget string, +) { + switch { + case resource.EqualType(types.HTTPRouteType, routeNode.RouteType): + appendDefaultHTTPRouteRule(routeNode, defaultBackendTarget) + case resource.EqualType(types.GRPCRouteType, routeNode.RouteType): + appendDefaultGRPCRouteRule(routeNode, defaultBackendTarget) + case resource.EqualType(types.TCPRouteType, routeNode.RouteType): + fallthrough + default: + // skip: unnecessary since + appendDefaultTCPRouteRule(routeNode, defaultBackendTarget) + } +} + func setupDefaultHTTPRouteNode( routeNode *inputRouteNode, defaultBackendTarget string, ) { routeNode.RouteType = types.HTTPRouteType - routeNode.HTTPRules = []*pbmesh.InterpretedHTTPRouteRule{{ - Matches: []*pbmesh.HTTPRouteMatch{{ - Path: &pbmesh.HTTPPathMatch{ - Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, - Value: "/", - }, - }}, + appendDefaultHTTPRouteRule(routeNode, defaultBackendTarget) +} + +func appendDefaultHTTPRouteRule( + routeNode *inputRouteNode, + backendTarget string, +) { + routeNode.HTTPRules = append(routeNode.HTTPRules, &pbmesh.InterpretedHTTPRouteRule{ + Matches: defaultHTTPRouteMatches(), BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ - BackendTarget: defaultBackendTarget, + BackendTarget: backendTarget, }}, - }} + }) } func setupDefaultGRPCRouteNode( @@ -516,12 +623,19 @@ func setupDefaultGRPCRouteNode( defaultBackendTarget string, ) { routeNode.RouteType = types.GRPCRouteType - routeNode.GRPCRules = []*pbmesh.InterpretedGRPCRouteRule{{ - Matches: []*pbmesh.GRPCRouteMatch{{}}, + appendDefaultGRPCRouteRule(routeNode, defaultBackendTarget) +} + +func appendDefaultGRPCRouteRule( + routeNode *inputRouteNode, + backendTarget string, +) { + routeNode.GRPCRules = append(routeNode.GRPCRules, &pbmesh.InterpretedGRPCRouteRule{ + Matches: defaultGRPCRouteMatches(), BackendRefs: []*pbmesh.InterpretedGRPCBackendRef{{ - BackendTarget: defaultBackendTarget, + BackendTarget: backendTarget, }}, - }} + }) } func setupDefaultTCPRouteNode( @@ -529,9 +643,33 @@ func setupDefaultTCPRouteNode( defaultBackendTarget string, ) { routeNode.RouteType = types.TCPRouteType - routeNode.TCPRules = []*pbmesh.InterpretedTCPRouteRule{{ + appendDefaultTCPRouteRule(routeNode, defaultBackendTarget) +} + +func appendDefaultTCPRouteRule( + routeNode *inputRouteNode, + backendTarget string, +) { + routeNode.TCPRules = append(routeNode.TCPRules, &pbmesh.InterpretedTCPRouteRule{ BackendRefs: []*pbmesh.InterpretedTCPBackendRef{{ - BackendTarget: defaultBackendTarget, + BackendTarget: backendTarget, }}, + }) +} + +func defaultHTTPRouteMatches() []*pbmesh.HTTPRouteMatch { + return []*pbmesh.HTTPRouteMatch{{ + Path: defaultHTTPPathMatch(), }} } + +func defaultHTTPPathMatch() *pbmesh.HTTPPathMatch { + return &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, + Value: "/", + } +} + +func defaultGRPCRouteMatches() []*pbmesh.GRPCRouteMatch { + return []*pbmesh.GRPCRouteMatch{{}} +} diff --git a/internal/mesh/internal/controllers/routes/cross.go b/internal/mesh/internal/controllers/routes/ref_validation.go similarity index 63% rename from internal/mesh/internal/controllers/routes/cross.go rename to internal/mesh/internal/controllers/routes/ref_validation.go index 5c2f8e7fa10..81a52dad967 100644 --- a/internal/mesh/internal/controllers/routes/cross.go +++ b/internal/mesh/internal/controllers/routes/ref_validation.go @@ -24,15 +24,18 @@ func ValidateXRouteReferences(related *loader.RelatedResources, pending PendingS parentRefs := route.GetParentRefs() backendRefs := route.GetUnderlyingBackendRefs() - conditions := computeNewRouteRefConditions(related, res, parentRefs, backendRefs) + conditions := computeNewRouteRefConditions(related, parentRefs, backendRefs) pending.AddConditions(rk, res, conditions) }) } +type serviceGetter interface { + GetService(ref resource.ReferenceOrID) *types.DecodedService +} + func computeNewRouteRefConditions( - related *loader.RelatedResources, - routeRes *pbresource.Resource, + related serviceGetter, parentRefs []*pbmesh.ParentReference, backendRefs []*pbmesh.BackendReference, ) []*pbresource.Condition { @@ -48,15 +51,29 @@ func computeNewRouteRefConditions( continue } if svc := related.GetService(parentRef.Ref); svc != nil { - allowedPorts, hasMesh := getAllowedPorts(svc) - if hasMesh { - if parentRef.Port != "" { - if _, found := allowedPorts[parentRef.Port]; !found { - conditions = append(conditions, ConditionUnknownParentRefPort(parentRef.Ref, parentRef.Port)) + found := false + usingMesh := false + hasMesh := false + for _, port := range svc.Data.Ports { + if port.Protocol == pbcatalog.Protocol_PROTOCOL_MESH { + hasMesh = true + } + if port.TargetPort == parentRef.Port { + found = true + if port.Protocol == pbcatalog.Protocol_PROTOCOL_MESH { + usingMesh = true } } - } else { + } + switch { + case !hasMesh: conditions = append(conditions, ConditionParentRefOutsideMesh(parentRef.Ref)) + case !found: + if parentRef.Port != "" { + conditions = append(conditions, ConditionUnknownParentRefPort(parentRef.Ref, parentRef.Port)) + } + case usingMesh: + conditions = append(conditions, ConditionParentRefUsingMeshPort(parentRef.Ref, parentRef.Port)) } } else { conditions = append(conditions, ConditionMissingParentRef(parentRef.Ref)) @@ -71,15 +88,29 @@ func computeNewRouteRefConditions( continue } if svc := related.GetService(backendRef.Ref); svc != nil { - allowedPorts, hasMesh := getAllowedPorts(svc) - if hasMesh { - if backendRef.Port != "" { - if _, found := allowedPorts[backendRef.Port]; !found { - conditions = append(conditions, ConditionUnknownBackendRefPort(backendRef.Ref, backendRef.Port)) + found := false + usingMesh := false + hasMesh := false + for _, port := range svc.Data.Ports { + if port.Protocol == pbcatalog.Protocol_PROTOCOL_MESH { + hasMesh = true + } + if port.TargetPort == backendRef.Port { + found = true + if port.Protocol == pbcatalog.Protocol_PROTOCOL_MESH { + usingMesh = true } } - } else { + } + switch { + case !hasMesh: conditions = append(conditions, ConditionBackendRefOutsideMesh(backendRef.Ref)) + case !found: + if backendRef.Port != "" { + conditions = append(conditions, ConditionUnknownBackendRefPort(backendRef.Ref, backendRef.Port)) + } + case usingMesh: + conditions = append(conditions, ConditionBackendRefUsingMeshPort(backendRef.Ref, backendRef.Port)) } } else { conditions = append(conditions, ConditionMissingBackendRef(backendRef.Ref)) @@ -88,16 +119,3 @@ func computeNewRouteRefConditions( return conditions } - -func getAllowedPorts(svc *types.DecodedService) (map[string]pbcatalog.Protocol, bool) { - allowedPortProtocols := make(map[string]pbcatalog.Protocol) - hasMesh := false - for _, port := range svc.Data.Ports { - if port.Protocol == pbcatalog.Protocol_PROTOCOL_MESH { - hasMesh = true - continue // skip - } - allowedPortProtocols[port.TargetPort] = port.Protocol - } - return allowedPortProtocols, hasMesh -} diff --git a/internal/mesh/internal/controllers/routes/ref_validation_test.go b/internal/mesh/internal/controllers/routes/ref_validation_test.go new file mode 100644 index 00000000000..b6a697ef137 --- /dev/null +++ b/internal/mesh/internal/controllers/routes/ref_validation_test.go @@ -0,0 +1,231 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package routes + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/hashicorp/consul/internal/catalog" + "github.com/hashicorp/consul/internal/mesh/internal/types" + "github.com/hashicorp/consul/internal/resource" + rtest "github.com/hashicorp/consul/internal/resource/resourcetest" + pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v1alpha1" + pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" + "github.com/hashicorp/consul/proto-public/pbresource" + "github.com/hashicorp/consul/proto/private/prototest" +) + +func TestComputeNewRouteRefConditions(t *testing.T) { + registry := resource.NewRegistry() + types.Register(registry) + catalog.RegisterTypes(registry) + + newService := func(name string, ports map[string]pbcatalog.Protocol) *types.DecodedService { + var portSlice []*pbcatalog.ServicePort + for name, proto := range ports { + portSlice = append(portSlice, &pbcatalog.ServicePort{ + TargetPort: name, + Protocol: proto, + }) + } + svc := rtest.Resource(catalog.ServiceType, name). + WithData(t, &pbcatalog.Service{Ports: portSlice}). + Build() + rtest.ValidateAndNormalize(t, registry, svc) + + dec, err := resource.Decode[pbcatalog.Service, *pbcatalog.Service](svc) + require.NoError(t, err) + return dec + } + _ = newService + + t.Run("no refs", func(t *testing.T) { + sg := newTestServiceGetter() + got := computeNewRouteRefConditions(sg, nil, nil) + require.Empty(t, got) + }) + + t.Run("parent refs", func(t *testing.T) { + t.Run("with no service", func(t *testing.T) { + sg := newTestServiceGetter() + got := computeNewRouteRefConditions(sg, []*pbmesh.ParentReference{ + newParentRef(newRef(catalog.ServiceType, "api"), ""), + }, nil) + require.Len(t, got, 1) + prototest.AssertContainsElement(t, got, ConditionMissingParentRef( + newRef(catalog.ServiceType, "api"), + )) + }) + + t.Run("with service but no mesh port", func(t *testing.T) { + sg := newTestServiceGetter(newService("api", map[string]pbcatalog.Protocol{ + "http": pbcatalog.Protocol_PROTOCOL_HTTP, + })) + got := computeNewRouteRefConditions(sg, []*pbmesh.ParentReference{ + newParentRef(newRef(catalog.ServiceType, "api"), ""), + }, nil) + require.Len(t, got, 1) + prototest.AssertContainsElement(t, got, ConditionParentRefOutsideMesh( + newRef(catalog.ServiceType, "api"), + )) + }) + + t.Run("with service but using mesh port", func(t *testing.T) { + sg := newTestServiceGetter(newService("api", map[string]pbcatalog.Protocol{ + "http": pbcatalog.Protocol_PROTOCOL_HTTP, + "mesh": pbcatalog.Protocol_PROTOCOL_MESH, + })) + got := computeNewRouteRefConditions(sg, []*pbmesh.ParentReference{ + newParentRef(newRef(catalog.ServiceType, "api"), "mesh"), + }, nil) + require.Len(t, got, 1) + prototest.AssertContainsElement(t, got, ConditionParentRefUsingMeshPort( + newRef(catalog.ServiceType, "api"), + "mesh", + )) + }) + + t.Run("with service and using missing port", func(t *testing.T) { + sg := newTestServiceGetter(newService("api", map[string]pbcatalog.Protocol{ + "http": pbcatalog.Protocol_PROTOCOL_HTTP, + "mesh": pbcatalog.Protocol_PROTOCOL_MESH, + })) + got := computeNewRouteRefConditions(sg, []*pbmesh.ParentReference{ + newParentRef(newRef(catalog.ServiceType, "api"), "web"), + }, nil) + require.Len(t, got, 1) + prototest.AssertContainsElement(t, got, ConditionUnknownParentRefPort( + newRef(catalog.ServiceType, "api"), + "web", + )) + }) + + t.Run("with service and using empty port", func(t *testing.T) { + sg := newTestServiceGetter(newService("api", map[string]pbcatalog.Protocol{ + "http": pbcatalog.Protocol_PROTOCOL_HTTP, + "mesh": pbcatalog.Protocol_PROTOCOL_MESH, + })) + got := computeNewRouteRefConditions(sg, []*pbmesh.ParentReference{ + newParentRef(newRef(catalog.ServiceType, "api"), ""), + }, nil) + require.Empty(t, got) + }) + + t.Run("with service and using correct port", func(t *testing.T) { + sg := newTestServiceGetter(newService("api", map[string]pbcatalog.Protocol{ + "http": pbcatalog.Protocol_PROTOCOL_HTTP, + "mesh": pbcatalog.Protocol_PROTOCOL_MESH, + })) + got := computeNewRouteRefConditions(sg, []*pbmesh.ParentReference{ + newParentRef(newRef(catalog.ServiceType, "api"), "http"), + }, nil) + require.Empty(t, got) + }) + }) + + t.Run("backend refs", func(t *testing.T) { + t.Run("with no service", func(t *testing.T) { + sg := newTestServiceGetter() + got := computeNewRouteRefConditions(sg, nil, []*pbmesh.BackendReference{ + newBackendRef(newRef(catalog.ServiceType, "api"), "", ""), + }) + require.Len(t, got, 1) + prototest.AssertContainsElement(t, got, ConditionMissingBackendRef( + newRef(catalog.ServiceType, "api"), + )) + }) + + t.Run("with service but no mesh port", func(t *testing.T) { + sg := newTestServiceGetter(newService("api", map[string]pbcatalog.Protocol{ + "http": pbcatalog.Protocol_PROTOCOL_HTTP, + })) + got := computeNewRouteRefConditions(sg, nil, []*pbmesh.BackendReference{ + newBackendRef(newRef(catalog.ServiceType, "api"), "", ""), + }) + require.Len(t, got, 1) + prototest.AssertContainsElement(t, got, ConditionBackendRefOutsideMesh( + newRef(catalog.ServiceType, "api"), + )) + }) + + t.Run("with service but using mesh port", func(t *testing.T) { + sg := newTestServiceGetter(newService("api", map[string]pbcatalog.Protocol{ + "http": pbcatalog.Protocol_PROTOCOL_HTTP, + "mesh": pbcatalog.Protocol_PROTOCOL_MESH, + })) + got := computeNewRouteRefConditions(sg, nil, []*pbmesh.BackendReference{ + newBackendRef(newRef(catalog.ServiceType, "api"), "mesh", ""), + }) + require.Len(t, got, 1) + prototest.AssertContainsElement(t, got, ConditionBackendRefUsingMeshPort( + newRef(catalog.ServiceType, "api"), + "mesh", + )) + }) + + t.Run("with service and using missing port", func(t *testing.T) { + sg := newTestServiceGetter(newService("api", map[string]pbcatalog.Protocol{ + "http": pbcatalog.Protocol_PROTOCOL_HTTP, + "mesh": pbcatalog.Protocol_PROTOCOL_MESH, + })) + got := computeNewRouteRefConditions(sg, nil, []*pbmesh.BackendReference{ + newBackendRef(newRef(catalog.ServiceType, "api"), "web", ""), + }) + require.Len(t, got, 1) + prototest.AssertContainsElement(t, got, ConditionUnknownBackendRefPort( + newRef(catalog.ServiceType, "api"), + "web", + )) + }) + + t.Run("with service and using empty port", func(t *testing.T) { + sg := newTestServiceGetter(newService("api", map[string]pbcatalog.Protocol{ + "http": pbcatalog.Protocol_PROTOCOL_HTTP, + "mesh": pbcatalog.Protocol_PROTOCOL_MESH, + })) + got := computeNewRouteRefConditions(sg, nil, []*pbmesh.BackendReference{ + newBackendRef(newRef(catalog.ServiceType, "api"), "", ""), + }) + require.Empty(t, got) + }) + + t.Run("with service and using correct port", func(t *testing.T) { + sg := newTestServiceGetter(newService("api", map[string]pbcatalog.Protocol{ + "http": pbcatalog.Protocol_PROTOCOL_HTTP, + "mesh": pbcatalog.Protocol_PROTOCOL_MESH, + })) + got := computeNewRouteRefConditions(sg, nil, []*pbmesh.BackendReference{ + newBackendRef(newRef(catalog.ServiceType, "api"), "http", ""), + }) + require.Empty(t, got) + }) + }) +} + +func newRef(typ *pbresource.Type, name string) *pbresource.Reference { + return rtest.Resource(typ, name).Reference("") +} + +type testServiceGetter struct { + services map[resource.ReferenceKey]*types.DecodedService +} + +func newTestServiceGetter(svcs ...*types.DecodedService) serviceGetter { + g := &testServiceGetter{ + services: make(map[resource.ReferenceKey]*types.DecodedService), + } + for _, svc := range svcs { + g.services[resource.NewReferenceKey(svc.Resource.Id)] = svc + } + return g +} + +func (g *testServiceGetter) GetService(ref resource.ReferenceOrID) *types.DecodedService { + if g.services == nil { + return nil + } + return g.services[resource.NewReferenceKey(ref)] +} diff --git a/internal/mesh/internal/controllers/routes/sort_rules.go b/internal/mesh/internal/controllers/routes/sort_rules.go index fc0b4990436..f199e2a7213 100644 --- a/internal/mesh/internal/controllers/routes/sort_rules.go +++ b/internal/mesh/internal/controllers/routes/sort_rules.go @@ -18,9 +18,9 @@ func gammaSortRouteRules(node *inputRouteNode) { case resource.EqualType(node.RouteType, types.HTTPRouteType): gammaSortHTTPRouteRules(node.HTTPRules) case resource.EqualType(node.RouteType, types.GRPCRouteType): - // TODO + // TODO(rb): do a determinstic sort of something case resource.EqualType(node.RouteType, types.TCPRouteType): - // TODO + // TODO(rb): do a determinstic sort of something default: panic("impossible") } diff --git a/internal/mesh/internal/controllers/routes/status_xroute.go b/internal/mesh/internal/controllers/routes/status_xroute.go index c298c15a374..d134b4896d1 100644 --- a/internal/mesh/internal/controllers/routes/status_xroute.go +++ b/internal/mesh/internal/controllers/routes/status_xroute.go @@ -11,7 +11,9 @@ import ( ) const ( - StatusConditionValid = "valid" + StatusConditionAccepted = "accepted" + // Deprecated: see StatusConditionAccepted + StatusConditionValid = StatusConditionAccepted OKReason = "Ok" @@ -21,6 +23,9 @@ const ( ParentRefOutsideMeshReason = "ParentRefOutsideMesh" BackendRefOutsideMeshReason = "BackendRefOutsideMesh" + ParentRefUsingMeshPortReason = "ParentRefUsingMeshPort" + BackendRefUsingMeshPortReason = "BackendRefUsingMeshPort" + UnknownParentRefPortReason = "UnknownParentRefPort" UnknownBackendRefPortReason = "UnknownBackendRefPort" @@ -36,6 +41,34 @@ var ( } ) +func ConditionParentRefUsingMeshPort(ref *pbresource.Reference, port string) *pbresource.Condition { + return conditionRefUsingMeshPort(ref, port, false) +} + +func ConditionBackendRefUsingMeshPort(ref *pbresource.Reference, port string) *pbresource.Condition { + return conditionRefUsingMeshPort(ref, port, true) +} + +func conditionRefUsingMeshPort(ref *pbresource.Reference, port string, forBackend bool) *pbresource.Condition { + reason := ParentRefUsingMeshPortReason + short := "parent" + if forBackend { + reason = BackendRefUsingMeshPortReason + short = "backend" + } + return &pbresource.Condition{ + Type: StatusConditionAccepted, + State: pbresource.Condition_STATE_FALSE, + Reason: reason, + Message: fmt.Sprintf( + "service for %s ref %q uses port %q which is a special unroutable mesh port", + short, + resource.ReferenceToString(ref), + port, + ), + } +} + func ConditionMissingParentRef(ref *pbresource.Reference) *pbresource.Condition { return conditionMissingRef(ref, false) } diff --git a/internal/mesh/internal/controllers/routes/util.go b/internal/mesh/internal/controllers/routes/util.go index 59fa2735165..288a462ff09 100644 --- a/internal/mesh/internal/controllers/routes/util.go +++ b/internal/mesh/internal/controllers/routes/util.go @@ -10,16 +10,17 @@ import ( "github.com/hashicorp/consul/internal/mesh/internal/controllers/routes/xroutemapper" ) -func protoSliceClone[V any, PV interface { - proto.Message - *V -}](in []PV) []PV { +func protoClone[T proto.Message](v T) T { + return proto.Clone(v).(T) +} + +func protoSliceClone[T proto.Message](in []T) []T { if in == nil { return nil } - out := make([]PV, 0, len(in)) + out := make([]T, 0, len(in)) for _, v := range in { - out = append(out, proto.Clone(v).(PV)) + out = append(out, protoClone[T](v)) } return out } diff --git a/internal/mesh/internal/types/computed_routes.go b/internal/mesh/internal/types/computed_routes.go index 23d6f50499a..e0c58026834 100644 --- a/internal/mesh/internal/types/computed_routes.go +++ b/internal/mesh/internal/types/computed_routes.go @@ -13,6 +13,11 @@ import ( const ( ComputedRoutesKind = "ComputedRoutes" + + // NullRouteBackend is the sentinel string used in ComputedRoutes backend + // targets to indicate that traffic arriving at this destination should + // fail in a protocol-specific way (i.e. HTTP is 5xx) + NullRouteBackend = "NULL-ROUTE" ) var ( diff --git a/internal/mesh/internal/types/util.go b/internal/mesh/internal/types/util.go index 2841a314e15..22a2d49344c 100644 --- a/internal/mesh/internal/types/util.go +++ b/internal/mesh/internal/types/util.go @@ -13,10 +13,30 @@ import ( ) func IsRouteType(typ *pbresource.Type) bool { + return IsHTTPRouteType(typ) || + IsGRPCRouteType(typ) || + IsTCPRouteType(typ) +} + +func IsHTTPRouteType(typ *pbresource.Type) bool { + switch { + case resource.EqualType(typ, HTTPRouteType): + return true + } + return false +} + +func IsGRPCRouteType(typ *pbresource.Type) bool { + switch { + case resource.EqualType(typ, GRPCRouteType): + return true + } + return false +} + +func IsTCPRouteType(typ *pbresource.Type) bool { switch { - case resource.EqualType(typ, HTTPRouteType), - resource.EqualType(typ, GRPCRouteType), - resource.EqualType(typ, TCPRouteType): + case resource.EqualType(typ, TCPRouteType): return true } return false diff --git a/internal/mesh/internal/types/xroute.go b/internal/mesh/internal/types/xroute.go index 4a0840ab01e..0b8af6be42a 100644 --- a/internal/mesh/internal/types/xroute.go +++ b/internal/mesh/internal/types/xroute.go @@ -135,6 +135,16 @@ func validateParentRefs(parentRefs []*pbmesh.ParentReference) error { )) } + if parent.Ref.Name == "" { + merr = multierror.Append(merr, resource.ErrInvalidField{ + Name: "ref", + Wrapped: resource.ErrInvalidField{ + Name: "name", + Wrapped: resource.ErrMissing, + }, + }) + } + prk := portedRefKey{ Key: resource.NewReferenceKey(parent.Ref), Port: parent.Port, @@ -229,6 +239,16 @@ func validateBackendRef(backendRef *pbmesh.BackendReference) []error { }) } + if backendRef.Ref.Name == "" { + errs = append(errs, resource.ErrInvalidField{ + Name: "ref", + Wrapped: resource.ErrInvalidField{ + Name: "name", + Wrapped: resource.ErrMissing, + }, + }) + } + if backendRef.Ref.Section != "" { errs = append(errs, resource.ErrInvalidField{ Name: "ref", diff --git a/proto-public/pbmesh/v1alpha1/computed_routes.pb.go b/proto-public/pbmesh/v1alpha1/computed_routes.pb.go index ec0838d1cdf..0983a654b23 100644 --- a/proto-public/pbmesh/v1alpha1/computed_routes.pb.go +++ b/proto-public/pbmesh/v1alpha1/computed_routes.pb.go @@ -743,10 +743,7 @@ type BackendTargetDetails struct { unknownFields protoimpl.UnknownFields // identity info - BackendRef *BackendReference `protobuf:"bytes,1,opt,name=backend_ref,json=backendRef,proto3" json:"backend_ref,omitempty"` - // NullRouteTraffic indicates that this backend target should - // have all traffic error with a 5xx error or equivalent. - NullRouteTraffic bool `protobuf:"varint,2,opt,name=null_route_traffic,json=nullRouteTraffic,proto3" json:"null_route_traffic,omitempty"` + BackendRef *BackendReference `protobuf:"bytes,1,opt,name=backend_ref,json=backendRef,proto3" json:"backend_ref,omitempty"` Service *v1alpha1.Service `protobuf:"bytes,3,opt,name=service,proto3" json:"service,omitempty"` FailoverPolicy *v1alpha1.FailoverPolicy `protobuf:"bytes,4,opt,name=failover_policy,json=failoverPolicy,proto3" json:"failover_policy,omitempty"` DestinationPolicy *DestinationPolicy `protobuf:"bytes,5,opt,name=destination_policy,json=destinationPolicy,proto3" json:"destination_policy,omitempty"` @@ -791,13 +788,6 @@ func (x *BackendTargetDetails) GetBackendRef() *BackendReference { return nil } -func (x *BackendTargetDetails) GetNullRouteTraffic() bool { - if x != nil { - return x.NullRouteTraffic - } - return false -} - func (x *BackendTargetDetails) GetService() *v1alpha1.Service { if x != nil { return x.Service @@ -1015,51 +1005,48 @@ var file_pbmesh_v1alpha1_computed_routes_proto_rawDesc = []byte{ 0x6e, 0x64, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, - 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x9b, 0x03, 0x0a, 0x14, 0x42, 0x61, 0x63, 0x6b, 0x65, + 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0xed, 0x02, 0x0a, 0x14, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x51, 0x0a, 0x0b, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x0a, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, - 0x65, 0x66, 0x12, 0x2c, 0x0a, 0x12, 0x6e, 0x75, 0x6c, 0x6c, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, - 0x5f, 0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, - 0x6e, 0x75, 0x6c, 0x6c, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, - 0x12, 0x44, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x2a, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, + 0x65, 0x66, 0x12, 0x44, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, + 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, + 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5a, 0x0a, 0x0f, 0x66, 0x61, 0x69, 0x6c, + 0x6f, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x31, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x07, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5a, 0x0a, 0x0f, 0x66, 0x61, 0x69, 0x6c, 0x6f, 0x76, - 0x65, 0x72, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x31, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, - 0x75, 0x6c, 0x2e, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x31, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x6f, 0x76, 0x65, 0x72, 0x50, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x52, 0x0e, 0x66, 0x61, 0x69, 0x6c, 0x6f, 0x76, 0x65, 0x72, 0x50, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x12, 0x60, 0x0a, 0x12, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, - 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, - 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, - 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x52, 0x11, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x42, 0x9b, 0x02, 0x0a, 0x22, 0x63, 0x6f, 0x6d, 0x2e, 0x68, 0x61, 0x73, - 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, - 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x42, 0x13, 0x43, 0x6f, 0x6d, - 0x70, 0x75, 0x74, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, - 0x50, 0x01, 0x5a, 0x45, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, - 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2f, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2d, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2f, 0x70, 0x62, 0x6d, - 0x65, 0x73, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x3b, 0x6d, 0x65, 0x73, - 0x68, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0xa2, 0x02, 0x03, 0x48, 0x43, 0x4d, 0xaa, - 0x02, 0x1e, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x43, 0x6f, 0x6e, 0x73, - 0x75, 0x6c, 0x2e, 0x4d, 0x65, 0x73, 0x68, 0x2e, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, - 0xca, 0x02, 0x1e, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, - 0x73, 0x75, 0x6c, 0x5c, 0x4d, 0x65, 0x73, 0x68, 0x5c, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, - 0x31, 0xe2, 0x02, 0x2a, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, - 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x4d, 0x65, 0x73, 0x68, 0x5c, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, - 0x21, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x3a, 0x3a, 0x43, 0x6f, 0x6e, 0x73, - 0x75, 0x6c, 0x3a, 0x3a, 0x4d, 0x65, 0x73, 0x68, 0x3a, 0x3a, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x6f, 0x76, 0x65, 0x72, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0e, 0x66, 0x61, 0x69, 0x6c, 0x6f, 0x76, 0x65, 0x72, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x12, 0x60, 0x0a, 0x12, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x31, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, + 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x2e, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x52, 0x11, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x42, 0x9b, 0x02, 0x0a, 0x22, 0x63, 0x6f, 0x6d, 0x2e, 0x68, + 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, + 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x42, 0x13, 0x43, + 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x45, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x75, + 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2d, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2f, 0x70, + 0x62, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x3b, 0x6d, + 0x65, 0x73, 0x68, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0xa2, 0x02, 0x03, 0x48, 0x43, + 0x4d, 0xaa, 0x02, 0x1e, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x43, 0x6f, + 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x4d, 0x65, 0x73, 0x68, 0x2e, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x31, 0xca, 0x02, 0x1e, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, + 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x4d, 0x65, 0x73, 0x68, 0x5c, 0x56, 0x31, 0x61, 0x6c, 0x70, + 0x68, 0x61, 0x31, 0xe2, 0x02, 0x2a, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, + 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x4d, 0x65, 0x73, 0x68, 0x5c, 0x56, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0xea, 0x02, 0x21, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x3a, 0x3a, 0x43, 0x6f, + 0x6e, 0x73, 0x75, 0x6c, 0x3a, 0x3a, 0x4d, 0x65, 0x73, 0x68, 0x3a, 0x3a, 0x56, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/proto-public/pbmesh/v1alpha1/computed_routes.proto b/proto-public/pbmesh/v1alpha1/computed_routes.proto index 5fe9d884a82..cea0d9f67ac 100644 --- a/proto-public/pbmesh/v1alpha1/computed_routes.proto +++ b/proto-public/pbmesh/v1alpha1/computed_routes.proto @@ -91,10 +91,6 @@ message BackendTargetDetails { // identity info BackendReference backend_ref = 1; - // NullRouteTraffic indicates that this backend target should - // have all traffic error with a 5xx error or equivalent. - bool null_route_traffic = 2; - hashicorp.consul.catalog.v1alpha1.Service service = 3; hashicorp.consul.catalog.v1alpha1.FailoverPolicy failover_policy = 4; DestinationPolicy destination_policy = 5; From b328d3a8507130669bc0dd6dfb5e678a4f102621 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Mon, 14 Aug 2023 15:17:48 -0500 Subject: [PATCH 03/49] rename troublesome function --- .../mesh/internal/controllers/routes/intermediate.go | 2 +- internal/mesh/internal/types/util.go | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/internal/mesh/internal/controllers/routes/intermediate.go b/internal/mesh/internal/controllers/routes/intermediate.go index 820d06593cf..8feb26264da 100644 --- a/internal/mesh/internal/controllers/routes/intermediate.go +++ b/internal/mesh/internal/controllers/routes/intermediate.go @@ -47,7 +47,7 @@ func newInputRouteNode(port string) *inputRouteNode { func (n *inputRouteNode) AddTarget(backendRef *pbmesh.BackendReference, data *pbmesh.BackendTargetDetails) string { n.Default = false - key := types.BackendRefToString(backendRef) + key := types.BackendRefToComputedRoutesTarget(backendRef) if _, ok := n.NewTargets[key]; !ok { n.NewTargets[key] = data diff --git a/internal/mesh/internal/types/util.go b/internal/mesh/internal/types/util.go index 22a2d49344c..8ee18160f81 100644 --- a/internal/mesh/internal/types/util.go +++ b/internal/mesh/internal/types/util.go @@ -74,8 +74,14 @@ func IsComputedRoutesType(typ *pbresource.Type) bool { return false } -// TODO: fix this format -func BackendRefToString(backendRef *pbmesh.BackendReference) string { +// BackendRefToComputedRoutesTarget turns the provided BackendReference into an +// opaque string format suitable for use as a map key and reference in a +// standalone object or reference. +// +// It is opaque in that the caller should not attempt to parse it, and there is +// no implied storage or wire compatibility concern, since the data is treated +// opaquely at use time. +func BackendRefToComputedRoutesTarget(backendRef *pbmesh.BackendReference) string { ref := backendRef.Ref s := fmt.Sprintf( From 0e07b4374922cbe9f6e22ba4f1ad9c1a18a8348e Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Mon, 14 Aug 2023 15:23:31 -0500 Subject: [PATCH 04/49] clarify how null routing works --- .../pbmesh/v1alpha1/computed_routes.pb.go | 21 +++++++++++++++++++ .../pbmesh/v1alpha1/computed_routes.proto | 21 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/proto-public/pbmesh/v1alpha1/computed_routes.pb.go b/proto-public/pbmesh/v1alpha1/computed_routes.pb.go index 0983a654b23..0a8dd22fb3e 100644 --- a/proto-public/pbmesh/v1alpha1/computed_routes.pb.go +++ b/proto-public/pbmesh/v1alpha1/computed_routes.pb.go @@ -323,6 +323,13 @@ type InterpretedHTTPBackendRef struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + // BackendTarget indicates which key in the targets map provides + // the rest of the configuration. + // + // If this field is set to the empty string, or is the sentinel value + // "NULL-ROUTE" is an indication that all of the traffic destined for this + // backend reference should be null routed in a format appropriate for the + // protocol (i.e. for HTTP use 5xx). BackendTarget string `protobuf:"bytes,1,opt,name=backend_target,json=backendTarget,proto3" json:"backend_target,omitempty"` Weight uint32 `protobuf:"varint,2,opt,name=weight,proto3" json:"weight,omitempty"` Filters []*HTTPRouteFilter `protobuf:"bytes,3,rep,name=filters,proto3" json:"filters,omitempty"` @@ -520,6 +527,13 @@ type InterpretedGRPCBackendRef struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + // BackendTarget indicates which key in the targets map provides + // the rest of the configuration. + // + // If this field is set to the empty string, or is the sentinel value + // "NULL-ROUTE" is an indication that all of the traffic destined for this + // backend reference should be null routed in a format appropriate for the + // protocol (i.e. for HTTP use 5xx). BackendTarget string `protobuf:"bytes,1,opt,name=backend_target,json=backendTarget,proto3" json:"backend_target,omitempty"` Weight uint32 `protobuf:"varint,2,opt,name=weight,proto3" json:"weight,omitempty"` Filters []*GRPCRouteFilter `protobuf:"bytes,3,rep,name=filters,proto3" json:"filters,omitempty"` @@ -687,6 +701,13 @@ type InterpretedTCPBackendRef struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + // BackendTarget indicates which key in the targets map provides + // the rest of the configuration. + // + // If this field is set to the empty string, or is the sentinel value + // "NULL-ROUTE" is an indication that all of the traffic destined for this + // backend reference should be null routed in a format appropriate for the + // protocol (i.e. for HTTP use 5xx). BackendTarget string `protobuf:"bytes,1,opt,name=backend_target,json=backendTarget,proto3" json:"backend_target,omitempty"` Weight uint32 `protobuf:"varint,2,opt,name=weight,proto3" json:"weight,omitempty"` } diff --git a/proto-public/pbmesh/v1alpha1/computed_routes.proto b/proto-public/pbmesh/v1alpha1/computed_routes.proto index cea0d9f67ac..f177152083f 100644 --- a/proto-public/pbmesh/v1alpha1/computed_routes.proto +++ b/proto-public/pbmesh/v1alpha1/computed_routes.proto @@ -46,6 +46,13 @@ message InterpretedHTTPRouteRule { } message InterpretedHTTPBackendRef { + // BackendTarget indicates which key in the targets map provides + // the rest of the configuration. + // + // If this field is set to the empty string, or is the sentinel value + // "NULL-ROUTE" is an indication that all of the traffic destined for this + // backend reference should be null routed in a format appropriate for the + // protocol (i.e. for HTTP use 5xx). string backend_target = 1; uint32 weight = 2; repeated HTTPRouteFilter filters = 3; @@ -66,6 +73,13 @@ message InterpretedGRPCRouteRule { } message InterpretedGRPCBackendRef { + // BackendTarget indicates which key in the targets map provides + // the rest of the configuration. + // + // If this field is set to the empty string, or is the sentinel value + // "NULL-ROUTE" is an indication that all of the traffic destined for this + // backend reference should be null routed in a format appropriate for the + // protocol (i.e. for HTTP use 5xx). string backend_target = 1; uint32 weight = 2; repeated GRPCRouteFilter filters = 3; @@ -83,6 +97,13 @@ message InterpretedTCPRouteRule { // TODO: look into smuggling the target through a different typeURL, or just // skip in favor of letting the caller do their own lookups? message InterpretedTCPBackendRef { + // BackendTarget indicates which key in the targets map provides + // the rest of the configuration. + // + // If this field is set to the empty string, or is the sentinel value + // "NULL-ROUTE" is an indication that all of the traffic destined for this + // backend reference should be null routed in a format appropriate for the + // protocol (i.e. for HTTP use 5xx). string backend_target = 1; uint32 weight = 2; } From a8ad6655f760faf782a99abc7d8ff49cdb17d7c9 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Mon, 14 Aug 2023 15:24:06 -0500 Subject: [PATCH 05/49] remove debugging string --- internal/mesh/internal/controllers/routes/generate.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/mesh/internal/controllers/routes/generate.go b/internal/mesh/internal/controllers/routes/generate.go index c057d0fc635..5e0801a9510 100644 --- a/internal/mesh/internal/controllers/routes/generate.go +++ b/internal/mesh/internal/controllers/routes/generate.go @@ -92,7 +92,6 @@ func compile( res *pbresource.Resource, xroute types.XRouteData, ) { - logger.Info("RBOYER working out route", "r", resource.IDToString(res.Id)) var ports []string for _, ref := range xroute.GetParentRefs() { if resource.ReferenceOrIDMatch(ref.Ref, parentServiceRef) { From 9cb6287f027edbbfce551e452723a000364320b8 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Mon, 14 Aug 2023 15:46:59 -0500 Subject: [PATCH 06/49] test overlap --- .../controllers/routes/controller_test.go | 242 +++++++++++++++++- .../internal/controllers/routes/generate.go | 6 +- 2 files changed, 233 insertions(+), 15 deletions(-) diff --git a/internal/mesh/internal/controllers/routes/controller_test.go b/internal/mesh/internal/controllers/routes/controller_test.go index 672d2853fea..f18abfb18a4 100644 --- a/internal/mesh/internal/controllers/routes/controller_test.go +++ b/internal/mesh/internal/controllers/routes/controller_test.go @@ -262,9 +262,10 @@ func (suite *controllerSuite) TestController() { }}, }}, } - _ = rtest.Resource(types.TCPRouteType, "api-tcp-route1"). + tcpRoute1ID := rtest.Resource(types.TCPRouteType, "api-tcp-route1"). WithData(suite.T(), tcpRoute1). - Write(suite.T(), suite.client) + Write(suite.T(), suite.client). + Id httpRoute1 := &pbmesh.HTTPRoute{ ParentRefs: []*pbmesh.ParentReference{ @@ -277,9 +278,10 @@ func (suite *controllerSuite) TestController() { }}, }}, } - _ = rtest.Resource(types.HTTPRouteType, "api-http-route1"). + httpRoute1ID := rtest.Resource(types.HTTPRouteType, "api-http-route1"). WithData(suite.T(), httpRoute1). - Write(suite.T(), suite.client) + Write(suite.T(), suite.client). + Id grpcRoute1 := &pbmesh.GRPCRoute{ ParentRefs: []*pbmesh.ParentReference{ @@ -291,9 +293,10 @@ func (suite *controllerSuite) TestController() { }}, }}, } - _ = rtest.Resource(types.GRPCRouteType, "api-grpc-route1"). + grpcRoute1ID := rtest.Resource(types.GRPCRouteType, "api-grpc-route1"). WithData(suite.T(), grpcRoute1). - Write(suite.T(), suite.client) + Write(suite.T(), suite.client). + Id testutil.RunStep(suite.T(), "one of each", func(t *testing.T) { expect := &pbmesh.ComputedRoutes{ @@ -401,6 +404,10 @@ func (suite *controllerSuite) TestController() { } lastVersion = requireNewComputedRoutesVersion(t, suite.client, computedRoutesID, lastVersion, expect) + + suite.client.WaitForStatusCondition(t, tcpRoute1ID, StatusKey, ConditionOK) + suite.client.WaitForStatusCondition(t, httpRoute1ID, StatusKey, ConditionOK) + suite.client.WaitForStatusCondition(t, grpcRoute1ID, StatusKey, ConditionOK) }) // Add another route, with a bad mapping. @@ -415,9 +422,10 @@ func (suite *controllerSuite) TestController() { }}, }}, } - _ = rtest.Resource(types.TCPRouteType, "api-tcp-route2"). + tcpRoute2ID := rtest.Resource(types.TCPRouteType, "api-tcp-route2"). WithData(suite.T(), tcpRoute2). - Write(suite.T(), suite.client) + Write(suite.T(), suite.client). + Id httpRoute2 := &pbmesh.HTTPRoute{ ParentRefs: []*pbmesh.ParentReference{ @@ -436,9 +444,10 @@ func (suite *controllerSuite) TestController() { }}, }}, } - _ = rtest.Resource(types.HTTPRouteType, "api-http-route2"). + httpRoute2ID := rtest.Resource(types.HTTPRouteType, "api-http-route2"). WithData(suite.T(), httpRoute2). - Write(suite.T(), suite.client) + Write(suite.T(), suite.client). + Id grpcRoute2 := &pbmesh.GRPCRoute{ ParentRefs: []*pbmesh.ParentReference{ @@ -457,9 +466,10 @@ func (suite *controllerSuite) TestController() { }}, }}, } - _ = rtest.Resource(types.GRPCRouteType, "api-grpc-route2"). + grpcRoute2ID := rtest.Resource(types.GRPCRouteType, "api-grpc-route2"). WithData(suite.T(), grpcRoute2). - Write(suite.T(), suite.client) + Write(suite.T(), suite.client). + Id testutil.RunStep(suite.T(), "one good one bad route", func(t *testing.T) { expect := &pbmesh.ComputedRoutes{ @@ -608,6 +618,200 @@ func (suite *controllerSuite) TestController() { } lastVersion = requireNewComputedRoutesVersion(t, suite.client, computedRoutesID, lastVersion, expect) + + suite.client.WaitForStatusCondition(t, tcpRoute1ID, StatusKey, ConditionOK) + suite.client.WaitForStatusCondition(t, httpRoute1ID, StatusKey, ConditionOK) + suite.client.WaitForStatusCondition(t, grpcRoute1ID, StatusKey, ConditionOK) + + suite.client.WaitForStatusCondition(t, tcpRoute2ID, StatusKey, + ConditionMissingBackendRef(newRef(catalog.ServiceType, "bar"))) + suite.client.WaitForStatusCondition(t, httpRoute2ID, StatusKey, + ConditionMissingBackendRef(newRef(catalog.ServiceType, "bar"))) + suite.client.WaitForStatusCondition(t, grpcRoute2ID, StatusKey, + ConditionMissingBackendRef(newRef(catalog.ServiceType, "bar"))) + }) + + // Update the route2 routes to point to a real service, but overlap in + // their parentrefs with existing ports tied to other xRoutes. + // + // tcp2 -> http1 + // http2 -> grpc1 + // grpc2 -> tcp1 + // + // Also remove customization for the protocol http2. + + tcpRoute2 = &pbmesh.TCPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + newParentRef(newRef(catalog.ServiceType, "api"), "http"), + }, + Rules: []*pbmesh.TCPRouteRule{{ + BackendRefs: []*pbmesh.TCPBackendRef{{ + BackendRef: newBackendRef(fooServiceRef, "", ""), + }}, + }}, + } + rtest.ResourceID(tcpRoute2ID). + WithData(suite.T(), tcpRoute2). + Write(suite.T(), suite.client) + + httpRoute2 = &pbmesh.HTTPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + newParentRef(newRef(catalog.ServiceType, "api"), "grpc"), + }, + Rules: []*pbmesh.HTTPRouteRule{{ + Matches: []*pbmesh.HTTPRouteMatch{{ + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, + Value: "/healthz", + }, + }}, + BackendRefs: []*pbmesh.HTTPBackendRef{{ + BackendRef: newBackendRef(fooServiceRef, "", ""), + }}, + }}, + } + rtest.ResourceID(httpRoute2ID). + WithData(suite.T(), httpRoute2). + Write(suite.T(), suite.client) + + grpcRoute2 = &pbmesh.GRPCRoute{ + ParentRefs: []*pbmesh.ParentReference{ + newParentRef(newRef(catalog.ServiceType, "api"), "tcp"), + }, + Rules: []*pbmesh.GRPCRouteRule{{ + Matches: []*pbmesh.GRPCRouteMatch{{ + Method: &pbmesh.GRPCMethodMatch{ + Type: pbmesh.GRPCMethodMatchType_GRPC_METHOD_MATCH_TYPE_EXACT, + Service: "billing", + Method: "charge", + }, + }}, + BackendRefs: []*pbmesh.GRPCBackendRef{{ + BackendRef: newBackendRef(fooServiceRef, "", ""), + }}, + }}, + } + rtest.ResourceID(grpcRoute2ID). + WithData(suite.T(), grpcRoute2). + Write(suite.T(), suite.client) + + testutil.RunStep(suite.T(), "overlapping xRoutes generate conflicts", func(t *testing.T) { + expect := &pbmesh.ComputedRoutes{ + PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ + "tcp": { + Config: &pbmesh.ComputedPortRoutes_Tcp{ + Tcp: &pbmesh.InterpretedTCPRoute{ + ParentRef: newParentRef(apiServiceRef, "tcp"), + Rules: []*pbmesh.InterpretedTCPRouteRule{{ + BackendRefs: []*pbmesh.InterpretedTCPBackendRef{{ + BackendTarget: backendName("foo", "tcp"), + }}, + }}, + }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("foo", "tcp"): { + BackendRef: newBackendRef(fooServiceRef, "tcp", ""), + Service: fooServiceData, + }, + }, + }, + "http": { + Config: &pbmesh.ComputedPortRoutes_Http{ + Http: &pbmesh.InterpretedHTTPRoute{ + ParentRef: newParentRef(apiServiceRef, "http"), + Rules: []*pbmesh.InterpretedHTTPRouteRule{ + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendTarget: backendName("foo", "http"), + }}, + }, + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, + }, + }, + }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("foo", "http"): { + BackendRef: newBackendRef(fooServiceRef, "http", ""), + Service: fooServiceData, + }, + }, + }, + "grpc": { + Config: &pbmesh.ComputedPortRoutes_Grpc{ + Grpc: &pbmesh.InterpretedGRPCRoute{ + ParentRef: newParentRef(apiServiceRef, "grpc"), + Rules: []*pbmesh.InterpretedGRPCRouteRule{ + { + Matches: []*pbmesh.GRPCRouteMatch{{}}, + BackendRefs: []*pbmesh.InterpretedGRPCBackendRef{{ + BackendTarget: backendName("foo", "grpc"), + }}, + }, + { + Matches: []*pbmesh.GRPCRouteMatch{{}}, + BackendRefs: []*pbmesh.InterpretedGRPCBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, + }, + }, + }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("foo", "grpc"): { + BackendRef: newBackendRef(fooServiceRef, "grpc", ""), + Service: fooServiceData, + }, + }, + }, + "http2": { + Config: &pbmesh.ComputedPortRoutes_Http{ + Http: &pbmesh.InterpretedHTTPRoute{ + ParentRef: newParentRef(apiServiceRef, "http2"), + Rules: []*pbmesh.InterpretedHTTPRouteRule{ + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendTarget: backendName("foo", "http2"), + }}, + }, + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, + }, + }, + }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("foo", "http2"): { + BackendRef: newBackendRef(fooServiceRef, "http2", ""), + Service: fooServiceData, + }, + }, + }, + }, + } + + lastVersion = requireNewComputedRoutesVersion(t, suite.client, computedRoutesID, lastVersion, expect) + + suite.client.WaitForStatusCondition(t, tcpRoute1ID, StatusKey, ConditionOK) + suite.client.WaitForStatusCondition(t, httpRoute1ID, StatusKey, ConditionOK) + suite.client.WaitForStatusCondition(t, grpcRoute1ID, StatusKey, ConditionOK) + + suite.client.WaitForStatusCondition(t, tcpRoute2ID, StatusKey, + ConditionConflictNotBoundToParentRef(newRef(catalog.ServiceType, "api"), "http", types.HTTPRouteType)) + suite.client.WaitForStatusCondition(t, httpRoute2ID, StatusKey, + ConditionConflictNotBoundToParentRef(newRef(catalog.ServiceType, "api"), "grpc", types.GRPCRouteType)) + suite.client.WaitForStatusCondition(t, grpcRoute2ID, StatusKey, + ConditionConflictNotBoundToParentRef(newRef(catalog.ServiceType, "api"), "tcp", types.TCPRouteType)) }) // Remove the mesh port from api service. @@ -631,6 +835,20 @@ func (suite *controllerSuite) TestController() { testutil.RunStep(suite.T(), "entire generated resource is deleted", func(t *testing.T) { retry.Run(t, func(r *retry.R) { suite.client.RequireResourceNotFound(r, computedRoutesID) + + suite.client.WaitForStatusCondition(t, tcpRoute1ID, StatusKey, + ConditionParentRefOutsideMesh(newRef(catalog.ServiceType, "api"))) + suite.client.WaitForStatusCondition(t, httpRoute1ID, StatusKey, + ConditionParentRefOutsideMesh(newRef(catalog.ServiceType, "api"))) + suite.client.WaitForStatusCondition(t, grpcRoute1ID, StatusKey, + ConditionParentRefOutsideMesh(newRef(catalog.ServiceType, "api"))) + + suite.client.WaitForStatusCondition(t, tcpRoute2ID, StatusKey, + ConditionParentRefOutsideMesh(newRef(catalog.ServiceType, "api"))) + suite.client.WaitForStatusCondition(t, httpRoute2ID, StatusKey, + ConditionParentRefOutsideMesh(newRef(catalog.ServiceType, "api"))) + suite.client.WaitForStatusCondition(t, grpcRoute2ID, StatusKey, + ConditionParentRefOutsideMesh(newRef(catalog.ServiceType, "api"))) }) }) diff --git a/internal/mesh/internal/controllers/routes/generate.go b/internal/mesh/internal/controllers/routes/generate.go index 5e0801a9510..c8dd1132613 100644 --- a/internal/mesh/internal/controllers/routes/generate.go +++ b/internal/mesh/internal/controllers/routes/generate.go @@ -116,21 +116,21 @@ func compile( switch route := xroute.(type) { case *pbmesh.HTTPRoute: if nullRouteTraffic { - node := newInputRouteNode(port) + node = newInputRouteNode(port) setupDefaultHTTPRouteNode(node, types.NullRouteBackend) } else { node = compileHTTPRouteNode(parentServiceRef, port, res, route, related) } case *pbmesh.GRPCRoute: if nullRouteTraffic { - node := newInputRouteNode(port) + node = newInputRouteNode(port) setupDefaultGRPCRouteNode(node, types.NullRouteBackend) } else { node = compileGRPCRouteNode(parentServiceRef, port, res, route, related) } case *pbmesh.TCPRoute: if nullRouteTraffic { - node := newInputRouteNode(port) + node = newInputRouteNode(port) setupDefaultTCPRouteNode(node, types.NullRouteBackend) } else { node = compileTCPRouteNode(parentServiceRef, port, res, route, related) From d7de729a53e2faffb1272889407d2714fae1cf31 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Mon, 14 Aug 2023 16:27:02 -0500 Subject: [PATCH 07/49] fix more bugs with conflict --- .../controllers/routes/controller_test.go | 174 ++++++++++++++++-- .../internal/controllers/routes/generate.go | 43 +++-- .../controllers/routes/intermediate.go | 17 -- .../mesh/internal/types/computed_routes.go | 9 +- .../internal/types/computed_routes_test.go | 15 -- 5 files changed, 187 insertions(+), 71 deletions(-) diff --git a/internal/mesh/internal/controllers/routes/controller_test.go b/internal/mesh/internal/controllers/routes/controller_test.go index f18abfb18a4..9f3d8e43dd4 100644 --- a/internal/mesh/internal/controllers/routes/controller_test.go +++ b/internal/mesh/internal/controllers/routes/controller_test.go @@ -814,6 +814,154 @@ func (suite *controllerSuite) TestController() { ConditionConflictNotBoundToParentRef(newRef(catalog.ServiceType, "api"), "tcp", types.TCPRouteType)) }) + // - Delete the bad routes + // - delete the original grpc route + // - create a new grpc route with a later name so it loses the conflict + // battle, and do a wildcard port binding + + suite.client.MustDelete(suite.T(), tcpRoute2ID) + suite.client.MustDelete(suite.T(), httpRoute2ID) + suite.client.MustDelete(suite.T(), grpcRoute1ID) + suite.client.MustDelete(suite.T(), grpcRoute2ID) + + suite.client.WaitForDeletion(suite.T(), tcpRoute2ID) + suite.client.WaitForDeletion(suite.T(), httpRoute2ID) + suite.client.WaitForDeletion(suite.T(), grpcRoute1ID) + suite.client.WaitForDeletion(suite.T(), grpcRoute2ID) + + // Re-create with newarly the same data (wildcard port now) with a newer name. + grpcRoute1 = &pbmesh.GRPCRoute{ + ParentRefs: []*pbmesh.ParentReference{ + newParentRef(newRef(catalog.ServiceType, "api"), ""), + }, + Rules: []*pbmesh.GRPCRouteRule{{ + BackendRefs: []*pbmesh.GRPCBackendRef{{ + BackendRef: newBackendRef(fooServiceRef, "", ""), + }}, + }}, + } + grpcRoute1ID = rtest.Resource(types.GRPCRouteType, "zzz-bad-route"). + WithData(suite.T(), grpcRoute1). + Write(suite.T(), suite.client). + Id + + testutil.RunStep(suite.T(), "overlapping xRoutes due to port wildcarding", func(t *testing.T) { + expect := &pbmesh.ComputedRoutes{ + PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ + "tcp": { + Config: &pbmesh.ComputedPortRoutes_Tcp{ + Tcp: &pbmesh.InterpretedTCPRoute{ + ParentRef: newParentRef(apiServiceRef, "tcp"), + Rules: []*pbmesh.InterpretedTCPRouteRule{{ + BackendRefs: []*pbmesh.InterpretedTCPBackendRef{{ + BackendTarget: backendName("foo", "tcp"), + }}, + }}, + }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("foo", "tcp"): { + BackendRef: newBackendRef(fooServiceRef, "tcp", ""), + Service: fooServiceData, + }, + }, + }, + "http": { + Config: &pbmesh.ComputedPortRoutes_Http{ + Http: &pbmesh.InterpretedHTTPRoute{ + ParentRef: newParentRef(apiServiceRef, "http"), + Rules: []*pbmesh.InterpretedHTTPRouteRule{ + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendTarget: backendName("foo", "http"), + }}, + }, + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, + }, + }, + }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("foo", "http"): { + BackendRef: newBackendRef(fooServiceRef, "http", ""), + Service: fooServiceData, + }, + }, + }, + "grpc": { + Config: &pbmesh.ComputedPortRoutes_Grpc{ + Grpc: &pbmesh.InterpretedGRPCRoute{ + ParentRef: newParentRef(apiServiceRef, "grpc"), + Rules: []*pbmesh.InterpretedGRPCRouteRule{ + { + Matches: []*pbmesh.GRPCRouteMatch{{}}, + BackendRefs: []*pbmesh.InterpretedGRPCBackendRef{{ + BackendTarget: backendName("foo", "grpc"), + }}, + }, + { + Matches: []*pbmesh.GRPCRouteMatch{{}}, + BackendRefs: []*pbmesh.InterpretedGRPCBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, + }, + }, + }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("foo", "grpc"): { + BackendRef: newBackendRef(fooServiceRef, "grpc", ""), + Service: fooServiceData, + }, + }, + }, + "http2": { + Config: &pbmesh.ComputedPortRoutes_Http{ + Http: &pbmesh.InterpretedHTTPRoute{ + ParentRef: newParentRef(apiServiceRef, "http2"), + Rules: []*pbmesh.InterpretedHTTPRouteRule{ + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendTarget: backendName("foo", "http2"), + }}, + }, + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, + }, + }, + }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("foo", "http2"): { + BackendRef: newBackendRef(fooServiceRef, "http2", ""), + Service: fooServiceData, + }, + }, + }, + }, + } + + suite.client.WaitForStatusConditions(t, grpcRoute1ID, StatusKey, + ConditionConflictNotBoundToParentRef(newRef(catalog.ServiceType, "api"), "http", types.HTTPRouteType), + ConditionConflictNotBoundToParentRef(newRef(catalog.ServiceType, "api"), "http2", types.HTTPRouteType), + ConditionConflictNotBoundToParentRef(newRef(catalog.ServiceType, "api"), "tcp", types.TCPRouteType)) + + lastVersion = requireNewComputedRoutesVersion(t, suite.client, computedRoutesID, "" /*no change*/, expect) + + suite.client.WaitForStatusCondition(t, tcpRoute1ID, StatusKey, ConditionOK) + suite.client.WaitForStatusCondition(t, httpRoute1ID, StatusKey, ConditionOK) + + }) + // Remove the mesh port from api service. apiServiceData = &pbcatalog.Service{ @@ -833,25 +981,15 @@ func (suite *controllerSuite) TestController() { Write(suite.T(), suite.client) testutil.RunStep(suite.T(), "entire generated resource is deleted", func(t *testing.T) { - retry.Run(t, func(r *retry.R) { - suite.client.RequireResourceNotFound(r, computedRoutesID) - - suite.client.WaitForStatusCondition(t, tcpRoute1ID, StatusKey, - ConditionParentRefOutsideMesh(newRef(catalog.ServiceType, "api"))) - suite.client.WaitForStatusCondition(t, httpRoute1ID, StatusKey, - ConditionParentRefOutsideMesh(newRef(catalog.ServiceType, "api"))) - suite.client.WaitForStatusCondition(t, grpcRoute1ID, StatusKey, - ConditionParentRefOutsideMesh(newRef(catalog.ServiceType, "api"))) - - suite.client.WaitForStatusCondition(t, tcpRoute2ID, StatusKey, - ConditionParentRefOutsideMesh(newRef(catalog.ServiceType, "api"))) - suite.client.WaitForStatusCondition(t, httpRoute2ID, StatusKey, - ConditionParentRefOutsideMesh(newRef(catalog.ServiceType, "api"))) - suite.client.WaitForStatusCondition(t, grpcRoute2ID, StatusKey, - ConditionParentRefOutsideMesh(newRef(catalog.ServiceType, "api"))) - }) - }) + suite.client.WaitForDeletion(t, computedRoutesID) + suite.client.WaitForStatusCondition(t, tcpRoute1ID, StatusKey, + ConditionParentRefOutsideMesh(newRef(catalog.ServiceType, "api"))) + suite.client.WaitForStatusCondition(t, httpRoute1ID, StatusKey, + ConditionParentRefOutsideMesh(newRef(catalog.ServiceType, "api"))) + suite.client.WaitForStatusCondition(t, grpcRoute1ID, StatusKey, + ConditionParentRefOutsideMesh(newRef(catalog.ServiceType, "api"))) + }) } func newParentRef(ref *pbresource.Reference, port string) *pbmesh.ParentReference { diff --git a/internal/mesh/internal/controllers/routes/generate.go b/internal/mesh/internal/controllers/routes/generate.go index c8dd1132613..9d77a7246e3 100644 --- a/internal/mesh/internal/controllers/routes/generate.go +++ b/internal/mesh/internal/controllers/routes/generate.go @@ -92,10 +92,25 @@ func compile( res *pbresource.Resource, xroute types.XRouteData, ) { - var ports []string + var ( + ports []string + wildcardedPort bool + ) for _, ref := range xroute.GetParentRefs() { if resource.ReferenceOrIDMatch(ref.Ref, parentServiceRef) { ports = append(ports, ref.Port) + if ref.Port == "" { + wildcardedPort = true + break + } + } + } + + // Do a port explosion. + if wildcardedPort { + ports = nil + for port := range allowedPortProtocols { + ports = append(ports, port) } } @@ -104,12 +119,14 @@ func compile( } for _, port := range ports { + if port == "" { + panic("impossible to have an empty port here") + } + // Check if the user provided port is actually valid. nullRouteTraffic := (parentServiceDec == nil) - if port != "" { - if _, ok := allowedPortProtocols[port]; !ok { - nullRouteTraffic = true - } + if _, ok := allowedPortProtocols[port]; !ok { + nullRouteTraffic = true } var node *inputRouteNode @@ -139,16 +156,7 @@ func compile( return // unknown xroute type (impossible) } - if node.ParentPort == "" { - // Do a port explosion. - for port := range allowedPortProtocols { - nodeCopy := node.Clone() - nodeCopy.ParentPort = port - routeNodesByPort[nodeCopy.ParentPort] = append(routeNodesByPort[nodeCopy.ParentPort], nodeCopy) - } - } else { - routeNodesByPort[node.ParentPort] = append(routeNodesByPort[node.ParentPort], node) - } + routeNodesByPort[node.ParentPort] = append(routeNodesByPort[node.ParentPort], node) } }) @@ -178,6 +186,9 @@ func compile( // First sort the input routes by the final criteria, so we can let the // stable sort take care of the ultimate tiebreakers. for port, routeNodes := range routeNodesByPort { + if port == "grpc" { + logger.Info("RBOYER - print routes", "N", len(routeNodes), "r", routeNodes) + } gammaInitialSortWrappedRoutes(routeNodes) // Now that they are sorted by age and name, we can figure out which @@ -189,6 +200,8 @@ func compile( continue } if top.RouteType != routeNode.RouteType { + // This should only happen with user-provided ones, since we + // would never have two synthetic default routes at once. res := routeNode.OriginalResource() pending.AddConditions(resource.NewReferenceKey(res.Id), res, []*pbresource.Condition{ ConditionConflictNotBoundToParentRef( diff --git a/internal/mesh/internal/controllers/routes/intermediate.go b/internal/mesh/internal/controllers/routes/intermediate.go index 8feb26264da..63b9beeca54 100644 --- a/internal/mesh/internal/controllers/routes/intermediate.go +++ b/internal/mesh/internal/controllers/routes/intermediate.go @@ -4,8 +4,6 @@ package routes import ( - "google.golang.org/protobuf/proto" - "github.com/hashicorp/consul/internal/mesh/internal/types" "github.com/hashicorp/consul/internal/resource" pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" @@ -91,18 +89,3 @@ func (n *inputRouteNode) OriginalResource() *pbresource.Resource { panic("impossible") } } - -func (n *inputRouteNode) Clone() *inputRouteNode { - n2 := *n - n2.HTTPRules = protoSliceClone(n.HTTPRules) - n2.GRPCRules = protoSliceClone(n.GRPCRules) - n2.TCPRules = protoSliceClone(n.TCPRules) - - n2.NewTargets = make(map[string]*pbmesh.BackendTargetDetails) - for key, details := range n.NewTargets { - n2.NewTargets[key] = proto.Clone(details).(*pbmesh.BackendTargetDetails) - } - - // only shallow copy the protobuf stuff since we don't touch it - return &n2 -} diff --git a/internal/mesh/internal/types/computed_routes.go b/internal/mesh/internal/types/computed_routes.go index e0c58026834..fc0834abfb6 100644 --- a/internal/mesh/internal/types/computed_routes.go +++ b/internal/mesh/internal/types/computed_routes.go @@ -71,12 +71,9 @@ func ValidateComputedRoutes(res *pbresource.Resource) error { Wrapped: resource.ErrEmpty, })) } - if len(pmc.Targets) == 0 { - merr = multierror.Append(merr, wrapErr(resource.ErrInvalidField{ - Name: "targets", - Wrapped: resource.ErrEmpty, - })) - } + + // TODO(rb): do a deep inspection of the config to verify that all + // xRoute backends ultimately point to an item in the targets map. } return merr diff --git a/internal/mesh/internal/types/computed_routes_test.go b/internal/mesh/internal/types/computed_routes_test.go index 46b2f890a71..8040e2afbd2 100644 --- a/internal/mesh/internal/types/computed_routes_test.go +++ b/internal/mesh/internal/types/computed_routes_test.go @@ -48,26 +48,11 @@ func TestValidateComputedRoutes(t *testing.T) { PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ "http": { Config: nil, - Targets: map[string]*pbmesh.BackendTargetDetails{ - "foo": {}, - }, }, }, }, expectErr: `invalid value of key "http" within ported_configs: invalid "config" field: cannot be empty`, }, - "empty targets": { - routes: &pbmesh.ComputedRoutes{ - PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ - "http": { - Config: &pbmesh.ComputedPortRoutes_Tcp{ - Tcp: &pbmesh.InterpretedTCPRoute{}, - }, - }, - }, - }, - expectErr: `invalid value of key "http" within ported_configs: invalid "targets" field: cannot be empty`, - }, "valid": { routes: &pbmesh.ComputedRoutes{ PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ From f4f0230c3dc2aaf749883b2c98801dda6567ecac Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Mon, 14 Aug 2023 16:28:24 -0500 Subject: [PATCH 08/49] remove stray comment --- internal/mesh/internal/controllers/routes/generate.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/mesh/internal/controllers/routes/generate.go b/internal/mesh/internal/controllers/routes/generate.go index 9d77a7246e3..56e6918cc60 100644 --- a/internal/mesh/internal/controllers/routes/generate.go +++ b/internal/mesh/internal/controllers/routes/generate.go @@ -605,7 +605,6 @@ func appendDefaultRouteNode( case resource.EqualType(types.TCPRouteType, routeNode.RouteType): fallthrough default: - // skip: unnecessary since appendDefaultTCPRouteRule(routeNode, defaultBackendTarget) } } From 7d047814349f723fee69ac50db1e1dacb7af17d4 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Mon, 14 Aug 2023 16:36:23 -0500 Subject: [PATCH 09/49] remove old todo --- internal/mesh/internal/controllers/routes/controller.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/mesh/internal/controllers/routes/controller.go b/internal/mesh/internal/controllers/routes/controller.go index c995e6ac705..2cdd9dc1643 100644 --- a/internal/mesh/internal/controllers/routes/controller.go +++ b/internal/mesh/internal/controllers/routes/controller.go @@ -44,7 +44,6 @@ type routesReconciler struct { mapper *xroutemapper.Mapper } -// TODO: only emit status updates on the xRoute types and when we do so, suffix them with the parent they apply to func (r *routesReconciler) Reconcile(ctx context.Context, rt controller.Runtime, req controller.Request) error { // Notably don't inject the resource-id here, since we have to do a fan-out // to multiple resources due to xRoutes having multiple parent refs. From d218235570f28efc52e0d70a683d5db48d727edc Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Mon, 14 Aug 2023 16:38:55 -0500 Subject: [PATCH 10/49] remove stray logs --- internal/mesh/internal/controllers/routes/controller.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/internal/mesh/internal/controllers/routes/controller.go b/internal/mesh/internal/controllers/routes/controller.go index 2cdd9dc1643..ff0a4d50beb 100644 --- a/internal/mesh/internal/controllers/routes/controller.go +++ b/internal/mesh/internal/controllers/routes/controller.go @@ -99,20 +99,17 @@ func ensureComputedRoutesIsSynced( prev *types.DecodedComputedRoutes, ) error { if result.Data == nil { - logger.Info("DELETE WRITE") return deleteComputedRoutes(ctx, logger, client, prev) } // Upsert the resource if changed. if prev != nil { if proto.Equal(prev.Data, result.Data) { - logger.Info("SKIPPING WRITE") return nil // no change } result.ID = prev.Resource.Id } - logger.Info("UPSERT WRITE") return upsertComputedRoutes(ctx, logger, client, result.ID, result.OwnerID, result.Data) } From 3ad8e8a7dbb35e8b36be7d4c5164dca3fbd07655 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Mon, 14 Aug 2023 16:40:36 -0500 Subject: [PATCH 11/49] update messages for new name --- internal/mesh/internal/controllers/routes/controller.go | 8 ++++---- .../mesh/internal/controllers/routes/controller_test.go | 5 ++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/internal/mesh/internal/controllers/routes/controller.go b/internal/mesh/internal/controllers/routes/controller.go index ff0a4d50beb..6e2768d06c3 100644 --- a/internal/mesh/internal/controllers/routes/controller.go +++ b/internal/mesh/internal/controllers/routes/controller.go @@ -142,11 +142,11 @@ func upsertComputedRoutes( }, }) if err != nil { - logger.Error("error writing generated mesh config", "error", err) + logger.Error("error writing computed routes", "error", err) return err } - logger.Trace("updated mesh config was successfully written") + logger.Trace("updated computed routes resource was successfully written") return nil } @@ -161,7 +161,7 @@ func deleteComputedRoutes( return nil } - // The service the mesh config controls no longer participates in the + // The service the computed routes controls no longer participates in the // mesh at all. logger.Trace("removing previous computed routes") @@ -177,7 +177,7 @@ func deleteComputedRoutes( // besides CAS version mismatches. The simplest thing to do is to just // propagate the error and retry reconciliation later. if err != nil { - logger.Error("error deleting previous mesh config", "error", err) + logger.Error("error deleting previous computed routes resource", "error", err) return err } diff --git a/internal/mesh/internal/controllers/routes/controller_test.go b/internal/mesh/internal/controllers/routes/controller_test.go index 9f3d8e43dd4..1b55d89aa56 100644 --- a/internal/mesh/internal/controllers/routes/controller_test.go +++ b/internal/mesh/internal/controllers/routes/controller_test.go @@ -67,7 +67,7 @@ func (suite *controllerSuite) TestController() { ) // Start out by creating a single port service and let it create the - // default mesh config for tcp. + // default computed routes for tcp. apiServiceData := &pbcatalog.Service{ Workloads: &pbcatalog.WorkloadSelector{ @@ -86,7 +86,7 @@ func (suite *controllerSuite) TestController() { var lastVersion string testutil.RunStep(suite.T(), "default tcp route", func(t *testing.T) { - // Check that the mesh config resource exists and it has one port that is the default. + // Check that the computed routes resource exists and it has one port that is the default. expect := &pbmesh.ComputedRoutes{ PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ "tcp": { @@ -152,7 +152,6 @@ func (suite *controllerSuite) TestController() { Write(suite.T(), suite.client) testutil.RunStep(suite.T(), "default other routes", func(t *testing.T) { - // Check that the mesh config resource exists and it has one port that is the default. expect := &pbmesh.ComputedRoutes{ PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ "tcp": { From edff76276ee221def3bd169516d293f4d50ede04 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Mon, 14 Aug 2023 16:42:08 -0500 Subject: [PATCH 12/49] use loggerFor in both places --- .../mesh/internal/controllers/routes/controller.go | 2 +- .../mesh/internal/controllers/routes/generate.go | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/internal/mesh/internal/controllers/routes/controller.go b/internal/mesh/internal/controllers/routes/controller.go index 6e2768d06c3..cfcb42f9c07 100644 --- a/internal/mesh/internal/controllers/routes/controller.go +++ b/internal/mesh/internal/controllers/routes/controller.go @@ -65,7 +65,7 @@ func (r *routesReconciler) Reconcile(ctx context.Context, rt controller.Runtime, ValidateXRouteReferences(related, pending) - generatedResults := GenerateComputedRoutes(ctx, rt.Logger, related, pending) + generatedResults := GenerateComputedRoutes(ctx, loggerFor, related, pending) if err := UpdatePendingStatuses(ctx, rt, pending); err != nil { rt.Logger.Error("error updating statuses for affected relevant resources", "error", err) diff --git a/internal/mesh/internal/controllers/routes/generate.go b/internal/mesh/internal/controllers/routes/generate.go index 56e6918cc60..22b69773aeb 100644 --- a/internal/mesh/internal/controllers/routes/generate.go +++ b/internal/mesh/internal/controllers/routes/generate.go @@ -26,13 +26,18 @@ import ( // This should not internally generate, nor return any errors. func GenerateComputedRoutes( ctx context.Context, - logger hclog.Logger, + loggerFor func(*pbresource.ID) hclog.Logger, related *loader.RelatedResources, pending PendingStatuses, ) []*ComputedRoutesResult { + if loggerFor == nil { + loggerFor = func(_ *pbresource.ID) hclog.Logger { + return hclog.NewNullLogger() + } + } out := make([]*ComputedRoutesResult, 0, len(related.ComputedRoutesList)) for _, computedRoutesID := range related.ComputedRoutesList { - out = append(out, compile(ctx, logger, related, computedRoutesID, pending)) + out = append(out, compile(ctx, loggerFor, related, computedRoutesID, pending)) } return out } @@ -46,12 +51,12 @@ type ComputedRoutesResult struct { func compile( ctx context.Context, - logger hclog.Logger, + loggerFor func(*pbresource.ID) hclog.Logger, related *loader.RelatedResources, computedRoutesID *pbresource.ID, pending PendingStatuses, ) *ComputedRoutesResult { - logger = logger.With("mesh-config-id", resource.IDToString(computedRoutesID)) + logger := loggerFor(computedRoutesID) // There is one mesh config for the entire service (perfect name alignment). // From aa58b8b914093d35db463a7800b9e46fbca86bf8 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Mon, 14 Aug 2023 16:52:48 -0500 Subject: [PATCH 13/49] cleanup naming and export similarly to failover controller --- internal/mesh/exports.go | 33 +++- .../controllers/routes/controller_test.go | 22 +-- .../internal/controllers/routes/generate.go | 16 +- .../controllers/routes/loader/loader.go | 6 +- .../controllers/routes/loader/loader_test.go | 4 +- .../controllers/routes/pending_status.go | 2 +- .../internal/controllers/routes/status.go | 162 +++++++++++++++-- .../controllers/routes/status_xroute.go | 166 ------------------ 8 files changed, 194 insertions(+), 217 deletions(-) delete mode 100644 internal/mesh/internal/controllers/routes/status_xroute.go diff --git a/internal/mesh/exports.go b/internal/mesh/exports.go index fa7cd048592..55d5a94cb5b 100644 --- a/internal/mesh/exports.go +++ b/internal/mesh/exports.go @@ -6,6 +6,7 @@ package mesh import ( "github.com/hashicorp/consul/internal/controller" "github.com/hashicorp/consul/internal/mesh/internal/controllers" + "github.com/hashicorp/consul/internal/mesh/internal/controllers/routes" "github.com/hashicorp/consul/internal/mesh/internal/controllers/sidecarproxy" "github.com/hashicorp/consul/internal/mesh/internal/controllers/sidecarproxy/status" "github.com/hashicorp/consul/internal/mesh/internal/types" @@ -47,15 +48,16 @@ var ( // Resource Types for the latest version. - ProxyConfigurationType = types.ProxyConfigurationType - UpstreamsType = types.UpstreamsType - UpstreamsConfigurationType = types.UpstreamsConfigurationType - ProxyStateTemplateType = types.ProxyStateTemplateType - HTTPRouteType = types.HTTPRouteType - GRPCRouteType = types.GRPCRouteType - TCPRouteType = types.TCPRouteType - DestinationPolicyType = types.DestinationPolicyType - ComputedRoutesType = types.ComputedRoutesType + ProxyConfigurationType = types.ProxyConfigurationType + UpstreamsType = types.UpstreamsType + UpstreamsConfigurationType = types.UpstreamsConfigurationType + ProxyStateTemplateType = types.ProxyStateTemplateType + ProxyStateTemplateConfigurationType = types.ProxyStateTemplateType + HTTPRouteType = types.HTTPRouteType + GRPCRouteType = types.GRPCRouteType + TCPRouteType = types.TCPRouteType + DestinationPolicyType = types.DestinationPolicyType + ComputedRoutesType = types.ComputedRoutesType // Controller statuses. @@ -68,6 +70,19 @@ var ( SidecarProxyStatusReasonDestinationServiceFound = status.StatusReasonDestinationServiceFound SidecarProxyStatusReasonMeshProtocolDestinationPort = status.StatusReasonMeshProtocolDestinationPort SidecarProxyStatusReasonNonMeshProtocolDestinationPort = status.StatusReasonNonMeshProtocolDestinationPort + + // Routes controller + RoutesStatusKey = routes.StatusKey + RoutesStatusConditionAccepted = routes.StatusConditionAccepted + RoutesStatusConditionAcceptedMissingParentRefReason = routes.MissingParentRefReason + RoutesStatusConditionAcceptedMissingBackendRefReason = routes.MissingBackendRefReason + RoutesStatusConditionAcceptedParentRefOutsideMeshReason = routes.ParentRefOutsideMeshReason + RoutesStatusConditionAcceptedBackendRefOutsideMeshReason = routes.BackendRefOutsideMeshReason + RoutesStatusConditionAcceptedParentRefUsingMeshPortReason = routes.ParentRefUsingMeshPortReason + RoutesStatusConditionAcceptedBackendRefUsingMeshPortReason = routes.BackendRefUsingMeshPortReason + RoutesStatusConditionAcceptedUnknownParentRefPortReason = routes.UnknownParentRefPortReason + RoutesStatusConditionAcceptedUnknownBackendRefPortReason = routes.UnknownBackendRefPortReason + RoutesStatusConditionAcceptedConflictNotBoundToParentRefReason = routes.ConflictNotBoundToParentRefReason ) const ( diff --git a/internal/mesh/internal/controllers/routes/controller_test.go b/internal/mesh/internal/controllers/routes/controller_test.go index 1b55d89aa56..889ee96cf68 100644 --- a/internal/mesh/internal/controllers/routes/controller_test.go +++ b/internal/mesh/internal/controllers/routes/controller_test.go @@ -404,9 +404,9 @@ func (suite *controllerSuite) TestController() { lastVersion = requireNewComputedRoutesVersion(t, suite.client, computedRoutesID, lastVersion, expect) - suite.client.WaitForStatusCondition(t, tcpRoute1ID, StatusKey, ConditionOK) - suite.client.WaitForStatusCondition(t, httpRoute1ID, StatusKey, ConditionOK) - suite.client.WaitForStatusCondition(t, grpcRoute1ID, StatusKey, ConditionOK) + suite.client.WaitForStatusCondition(t, tcpRoute1ID, StatusKey, ConditionXRouteOK) + suite.client.WaitForStatusCondition(t, httpRoute1ID, StatusKey, ConditionXRouteOK) + suite.client.WaitForStatusCondition(t, grpcRoute1ID, StatusKey, ConditionXRouteOK) }) // Add another route, with a bad mapping. @@ -618,9 +618,9 @@ func (suite *controllerSuite) TestController() { lastVersion = requireNewComputedRoutesVersion(t, suite.client, computedRoutesID, lastVersion, expect) - suite.client.WaitForStatusCondition(t, tcpRoute1ID, StatusKey, ConditionOK) - suite.client.WaitForStatusCondition(t, httpRoute1ID, StatusKey, ConditionOK) - suite.client.WaitForStatusCondition(t, grpcRoute1ID, StatusKey, ConditionOK) + suite.client.WaitForStatusCondition(t, tcpRoute1ID, StatusKey, ConditionXRouteOK) + suite.client.WaitForStatusCondition(t, httpRoute1ID, StatusKey, ConditionXRouteOK) + suite.client.WaitForStatusCondition(t, grpcRoute1ID, StatusKey, ConditionXRouteOK) suite.client.WaitForStatusCondition(t, tcpRoute2ID, StatusKey, ConditionMissingBackendRef(newRef(catalog.ServiceType, "bar"))) @@ -801,9 +801,9 @@ func (suite *controllerSuite) TestController() { lastVersion = requireNewComputedRoutesVersion(t, suite.client, computedRoutesID, lastVersion, expect) - suite.client.WaitForStatusCondition(t, tcpRoute1ID, StatusKey, ConditionOK) - suite.client.WaitForStatusCondition(t, httpRoute1ID, StatusKey, ConditionOK) - suite.client.WaitForStatusCondition(t, grpcRoute1ID, StatusKey, ConditionOK) + suite.client.WaitForStatusCondition(t, tcpRoute1ID, StatusKey, ConditionXRouteOK) + suite.client.WaitForStatusCondition(t, httpRoute1ID, StatusKey, ConditionXRouteOK) + suite.client.WaitForStatusCondition(t, grpcRoute1ID, StatusKey, ConditionXRouteOK) suite.client.WaitForStatusCondition(t, tcpRoute2ID, StatusKey, ConditionConflictNotBoundToParentRef(newRef(catalog.ServiceType, "api"), "http", types.HTTPRouteType)) @@ -956,8 +956,8 @@ func (suite *controllerSuite) TestController() { lastVersion = requireNewComputedRoutesVersion(t, suite.client, computedRoutesID, "" /*no change*/, expect) - suite.client.WaitForStatusCondition(t, tcpRoute1ID, StatusKey, ConditionOK) - suite.client.WaitForStatusCondition(t, httpRoute1ID, StatusKey, ConditionOK) + suite.client.WaitForStatusCondition(t, tcpRoute1ID, StatusKey, ConditionXRouteOK) + suite.client.WaitForStatusCondition(t, httpRoute1ID, StatusKey, ConditionXRouteOK) }) diff --git a/internal/mesh/internal/controllers/routes/generate.go b/internal/mesh/internal/controllers/routes/generate.go index 22b69773aeb..8b0345699c3 100644 --- a/internal/mesh/internal/controllers/routes/generate.go +++ b/internal/mesh/internal/controllers/routes/generate.go @@ -58,7 +58,7 @@ func compile( ) *ComputedRoutesResult { logger := loggerFor(computedRoutesID) - // There is one mesh config for the entire service (perfect name alignment). + // There is one computed routes resource for the entire service (perfect name alignment). // // All ports are embedded within. @@ -86,11 +86,11 @@ func compile( } } - meshConfig := &pbmesh.ComputedRoutes{ + computedRoutes := &pbmesh.ComputedRoutes{ PortedConfigs: make(map[string]*pbmesh.ComputedPortRoutes), } - // Visit all of the routes relevant to this mesh config. + // Visit all of the routes relevant to this computed routes. routeNodesByPort := make(map[string][]*inputRouteNode) related.WalkRoutesForParentRef(parentServiceRef, func( rk resource.ReferenceKey, @@ -120,7 +120,7 @@ func compile( } if len(ports) == 0 { - return // not relevant to this mesh config + return // not relevant to this computed routes } for _, port := range ports { @@ -273,7 +273,7 @@ func compile( panic("impossible") } - meshConfig.PortedConfigs[port] = mc + computedRoutes.PortedConfigs[port] = mc // TODO: prune dead targets from targets map @@ -301,17 +301,17 @@ func compile( } // TODO: Create derived composite routing instruction. - meshConfig.PortedConfigs[port] = mc + computedRoutes.PortedConfigs[port] = mc } if !inMesh { - meshConfig = nil + computedRoutes = nil } return &ComputedRoutesResult{ ID: computedRoutesID, OwnerID: parentServiceID, - Data: meshConfig, + Data: computedRoutes, } } diff --git a/internal/mesh/internal/controllers/routes/loader/loader.go b/internal/mesh/internal/controllers/routes/loader/loader.go index 109e2b3be31..5ce48495933 100644 --- a/internal/mesh/internal/controllers/routes/loader/loader.go +++ b/internal/mesh/internal/controllers/routes/loader/loader.go @@ -117,7 +117,7 @@ func (l *loader) loadOne( ) error { logger := loggerFor(computedRoutesID) - // There is one mesh config for the entire service (perfect name alignment). + // There is one computed routes for the entire service (perfect name alignment). // // All ports are embedded within. @@ -280,13 +280,13 @@ func (l *loader) gatherSingleXRouteAsInput( for _, parentRef := range route.GetParentRefs() { if types.IsServiceType(parentRef.Ref.Type) { - parentMeshConfigID := &pbresource.ID{ + parentComputedRoutesID := &pbresource.ID{ Type: types.ComputedRoutesType, Tenancy: parentRef.Ref.Tenancy, Name: parentRef.Ref.Name, } // Note: this will only schedule things to load that have not already been loaded - l.requestLoad(parentMeshConfigID) + l.requestLoad(parentComputedRoutesID) } } diff --git a/internal/mesh/internal/controllers/routes/loader/loader_test.go b/internal/mesh/internal/controllers/routes/loader/loader_test.go index f1910d1c0ef..1bb0d9aca6b 100644 --- a/internal/mesh/internal/controllers/routes/loader/loader_test.go +++ b/internal/mesh/internal/controllers/routes/loader/loader_test.go @@ -240,7 +240,7 @@ func TestLoadResourcesForComputedRoutes(t *testing.T) { }, }) - testutil.RunStep(t, "two overlapping mesh configs", func(t *testing.T) { + testutil.RunStep(t, "two overlapping computed routes resources", func(t *testing.T) { out, err := LoadResourcesForComputedRoutes(ctx, loggerFor, rt.Client, mapper, apiRoutesID) require.NoError(t, err) @@ -274,7 +274,7 @@ func TestLoadResourcesForComputedRoutes(t *testing.T) { }, }) - testutil.RunStep(t, "three overlapping mesh configs", func(t *testing.T) { + testutil.RunStep(t, "three overlapping computed routes resources", func(t *testing.T) { out, err := LoadResourcesForComputedRoutes(ctx, loggerFor, rt.Client, mapper, apiRoutesID) require.NoError(t, err) diff --git a/internal/mesh/internal/controllers/routes/pending_status.go b/internal/mesh/internal/controllers/routes/pending_status.go index 64de22b5282..1c33e2db2b8 100644 --- a/internal/mesh/internal/controllers/routes/pending_status.go +++ b/internal/mesh/internal/controllers/routes/pending_status.go @@ -57,7 +57,7 @@ func UpdatePendingStatuses( newStatus = &pbresource.Status{ ObservedGeneration: state.Generation, Conditions: []*pbresource.Condition{ - ConditionOK, + ConditionXRouteOK, }, } } diff --git a/internal/mesh/internal/controllers/routes/status.go b/internal/mesh/internal/controllers/routes/status.go index ef57592cbd8..71b6b8cd5b0 100644 --- a/internal/mesh/internal/controllers/routes/status.go +++ b/internal/mesh/internal/controllers/routes/status.go @@ -4,37 +4,165 @@ package routes import ( + "fmt" + + "github.com/hashicorp/consul/internal/resource" "github.com/hashicorp/consul/proto-public/pbresource" ) const ( - StatusKey = "consul.io/routes-controller" - StatusConditionHealthy = "healthy" + StatusKey = "consul.io/routes-controller" + StatusConditionAccepted = "accepted" + + // conditions on xRoutes + + XRouteOKReason = "Ok" + XRouteOKMessage = "xRoute was accepted" + + MissingParentRefReason = "MissingParentRef" + MissingBackendRefReason = "MissingBackendRef" + + ParentRefOutsideMeshReason = "ParentRefOutsideMesh" + BackendRefOutsideMeshReason = "BackendRefOutsideMesh" + + ParentRefUsingMeshPortReason = "ParentRefUsingMeshPort" + BackendRefUsingMeshPortReason = "BackendRefUsingMeshPort" - MeshConfigHealthyMessage = "Routing information is valid" - MeshConfigUnhealthyMessagePrefix = "Routing information is not valid: " + UnknownParentRefPortReason = "UnknownParentRefPort" + UnknownBackendRefPortReason = "UnknownBackendRefPort" + + ConflictNotBoundToParentRefReason = "ConflictNotBoundToParentRef" ) var ( - ConditionMeshPassing = &pbresource.Condition{ - Type: StatusConditionHealthy, + ConditionXRouteOK = &pbresource.Condition{ + Type: StatusConditionAccepted, State: pbresource.Condition_STATE_TRUE, - Reason: OKReason, - Message: MeshConfigHealthyMessage, + Reason: XRouteOKReason, + Message: XRouteOKMessage, } ) -func ConditionMeshError(reason, message string) *pbresource.Condition { - if reason == "" { - panic("reason is required") +func ConditionParentRefUsingMeshPort(ref *pbresource.Reference, port string) *pbresource.Condition { + return conditionRefUsingMeshPort(ref, port, false) +} + +func ConditionBackendRefUsingMeshPort(ref *pbresource.Reference, port string) *pbresource.Condition { + return conditionRefUsingMeshPort(ref, port, true) +} + +func conditionRefUsingMeshPort(ref *pbresource.Reference, port string, forBackend bool) *pbresource.Condition { + reason := ParentRefUsingMeshPortReason + short := "parent" + if forBackend { + reason = BackendRefUsingMeshPortReason + short = "backend" } - if message == "" { - panic("message is required") + return &pbresource.Condition{ + Type: StatusConditionAccepted, + State: pbresource.Condition_STATE_FALSE, + Reason: reason, + Message: fmt.Sprintf( + "service for %s ref %q uses port %q which is a special unroutable mesh port", + short, + resource.ReferenceToString(ref), + port, + ), + } +} + +func ConditionMissingParentRef(ref *pbresource.Reference) *pbresource.Condition { + return conditionMissingRef(ref, false) +} + +func ConditionMissingBackendRef(ref *pbresource.Reference) *pbresource.Condition { + return conditionMissingRef(ref, true) +} + +func conditionMissingRef(ref *pbresource.Reference, forBackend bool) *pbresource.Condition { + reason := MissingParentRefReason + short := "parent" + if forBackend { + reason = MissingBackendRefReason + short = "backend" + } + return &pbresource.Condition{ + Type: StatusConditionAccepted, + State: pbresource.Condition_STATE_FALSE, + Reason: reason, + Message: fmt.Sprintf( + "service for %s ref %q does not exist", + short, + resource.ReferenceToString(ref), + ), } +} + +func ConditionParentRefOutsideMesh(ref *pbresource.Reference) *pbresource.Condition { + return conditionRefOutsideMesh(ref, false) +} + +func ConditionBackendRefOutsideMesh(ref *pbresource.Reference) *pbresource.Condition { + return conditionRefOutsideMesh(ref, true) +} + +func conditionRefOutsideMesh(ref *pbresource.Reference, forBackend bool) *pbresource.Condition { + reason := ParentRefOutsideMeshReason + short := "parent" + if forBackend { + reason = BackendRefOutsideMeshReason + short = "backend" + } + return &pbresource.Condition{ + Type: StatusConditionAccepted, + State: pbresource.Condition_STATE_FALSE, + Reason: reason, + Message: fmt.Sprintf( + "service for %s ref %q does not expose a mesh port", + short, + resource.ReferenceToString(ref), + ), + } +} + +func ConditionUnknownParentRefPort(ref *pbresource.Reference, port string) *pbresource.Condition { + return conditionUnknownRefPort(ref, port, false) +} + +func ConditionUnknownBackendRefPort(ref *pbresource.Reference, port string) *pbresource.Condition { + return conditionUnknownRefPort(ref, port, true) +} + +func conditionUnknownRefPort(ref *pbresource.Reference, port string, forBackend bool) *pbresource.Condition { + reason := UnknownParentRefPortReason + short := "parent" + if forBackend { + reason = UnknownBackendRefPortReason + short = "backend" + } + return &pbresource.Condition{ + Type: StatusConditionAccepted, + State: pbresource.Condition_STATE_FALSE, + Reason: reason, + Message: fmt.Sprintf( + "service for %s ref %q does not expose port %q", + short, + resource.ReferenceToString(ref), + port, + ), + } +} + +func ConditionConflictNotBoundToParentRef(ref *pbresource.Reference, port string, realType *pbresource.Type) *pbresource.Condition { return &pbresource.Condition{ - Type: StatusConditionHealthy, - State: pbresource.Condition_STATE_FALSE, - Reason: reason, - Message: MeshConfigUnhealthyMessagePrefix + message, + Type: StatusConditionAccepted, + State: pbresource.Condition_STATE_FALSE, + Reason: ConflictNotBoundToParentRefReason, + Message: fmt.Sprintf( + "Existing routes of type %q are bound to parent ref %q on port %q preventing this from binding", + resource.TypeToString(realType), + resource.ReferenceToString(ref), + port, + ), } } diff --git a/internal/mesh/internal/controllers/routes/status_xroute.go b/internal/mesh/internal/controllers/routes/status_xroute.go deleted file mode 100644 index d134b4896d1..00000000000 --- a/internal/mesh/internal/controllers/routes/status_xroute.go +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package routes - -import ( - "fmt" - - "github.com/hashicorp/consul/internal/resource" - "github.com/hashicorp/consul/proto-public/pbresource" -) - -const ( - StatusConditionAccepted = "accepted" - // Deprecated: see StatusConditionAccepted - StatusConditionValid = StatusConditionAccepted - - OKReason = "Ok" - - MissingParentRefReason = "MissingParentRef" - MissingBackendRefReason = "MissingBackendRef" - - ParentRefOutsideMeshReason = "ParentRefOutsideMesh" - BackendRefOutsideMeshReason = "BackendRefOutsideMesh" - - ParentRefUsingMeshPortReason = "ParentRefUsingMeshPort" - BackendRefUsingMeshPortReason = "BackendRefUsingMeshPort" - - UnknownParentRefPortReason = "UnknownParentRefPort" - UnknownBackendRefPortReason = "UnknownBackendRefPort" - - ConflictNotBoundToParentRefReason = "ConflictNotBoundToParentRef" -) - -var ( - ConditionOK = &pbresource.Condition{ - Type: StatusConditionValid, - State: pbresource.Condition_STATE_TRUE, - Reason: OKReason, - // TODO: needs message? - } -) - -func ConditionParentRefUsingMeshPort(ref *pbresource.Reference, port string) *pbresource.Condition { - return conditionRefUsingMeshPort(ref, port, false) -} - -func ConditionBackendRefUsingMeshPort(ref *pbresource.Reference, port string) *pbresource.Condition { - return conditionRefUsingMeshPort(ref, port, true) -} - -func conditionRefUsingMeshPort(ref *pbresource.Reference, port string, forBackend bool) *pbresource.Condition { - reason := ParentRefUsingMeshPortReason - short := "parent" - if forBackend { - reason = BackendRefUsingMeshPortReason - short = "backend" - } - return &pbresource.Condition{ - Type: StatusConditionAccepted, - State: pbresource.Condition_STATE_FALSE, - Reason: reason, - Message: fmt.Sprintf( - "service for %s ref %q uses port %q which is a special unroutable mesh port", - short, - resource.ReferenceToString(ref), - port, - ), - } -} - -func ConditionMissingParentRef(ref *pbresource.Reference) *pbresource.Condition { - return conditionMissingRef(ref, false) -} - -func ConditionMissingBackendRef(ref *pbresource.Reference) *pbresource.Condition { - return conditionMissingRef(ref, true) -} - -func conditionMissingRef(ref *pbresource.Reference, forBackend bool) *pbresource.Condition { - reason := MissingParentRefReason - short := "parent" - if forBackend { - reason = MissingBackendRefReason - short = "backend" - } - return &pbresource.Condition{ - Type: StatusConditionValid, - State: pbresource.Condition_STATE_FALSE, - Reason: reason, - Message: fmt.Sprintf( - "service for %s ref %q does not exist", - short, - resource.ReferenceToString(ref), - ), - } -} - -func ConditionParentRefOutsideMesh(ref *pbresource.Reference) *pbresource.Condition { - return conditionRefOutsideMesh(ref, false) -} - -func ConditionBackendRefOutsideMesh(ref *pbresource.Reference) *pbresource.Condition { - return conditionRefOutsideMesh(ref, true) -} - -func conditionRefOutsideMesh(ref *pbresource.Reference, forBackend bool) *pbresource.Condition { - reason := ParentRefOutsideMeshReason - short := "parent" - if forBackend { - reason = BackendRefOutsideMeshReason - short = "backend" - } - return &pbresource.Condition{ - Type: StatusConditionValid, - State: pbresource.Condition_STATE_FALSE, - Reason: reason, - Message: fmt.Sprintf( - "service for %s ref %q does not expose a mesh port", - short, - resource.ReferenceToString(ref), - ), - } -} - -func ConditionUnknownParentRefPort(ref *pbresource.Reference, port string) *pbresource.Condition { - return conditionUnknownRefPort(ref, port, false) -} - -func ConditionUnknownBackendRefPort(ref *pbresource.Reference, port string) *pbresource.Condition { - return conditionUnknownRefPort(ref, port, true) -} - -func conditionUnknownRefPort(ref *pbresource.Reference, port string, forBackend bool) *pbresource.Condition { - reason := UnknownParentRefPortReason - short := "parent" - if forBackend { - reason = UnknownBackendRefPortReason - short = "backend" - } - return &pbresource.Condition{ - Type: StatusConditionValid, - State: pbresource.Condition_STATE_FALSE, - Reason: reason, - Message: fmt.Sprintf( - "service for %s ref %q does not expose port %q", - short, - resource.ReferenceToString(ref), - port, - ), - } -} - -func ConditionConflictNotBoundToParentRef(ref *pbresource.Reference, port string, realType *pbresource.Type) *pbresource.Condition { - return &pbresource.Condition{ - Type: StatusConditionValid, - State: pbresource.Condition_STATE_FALSE, - Reason: ConflictNotBoundToParentRefReason, - Message: fmt.Sprintf( - "Existing routes of type %q are bound to parent ref %q on port %q preventing this from binding", - resource.TypeToString(realType), - resource.ReferenceToString(ref), - port, - ), - } -} From 3b28e46486d53190dc69b16e46923da751719938 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Mon, 14 Aug 2023 16:54:36 -0500 Subject: [PATCH 14/49] remove debuggin --- internal/mesh/internal/controllers/routes/generate.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/internal/mesh/internal/controllers/routes/generate.go b/internal/mesh/internal/controllers/routes/generate.go index 8b0345699c3..c98c89da621 100644 --- a/internal/mesh/internal/controllers/routes/generate.go +++ b/internal/mesh/internal/controllers/routes/generate.go @@ -191,9 +191,6 @@ func compile( // First sort the input routes by the final criteria, so we can let the // stable sort take care of the ultimate tiebreakers. for port, routeNodes := range routeNodesByPort { - if port == "grpc" { - logger.Info("RBOYER - print routes", "N", len(routeNodes), "r", routeNodes) - } gammaInitialSortWrappedRoutes(routeNodes) // Now that they are sorted by age and name, we can figure out which From cd690332c0e14fc20bcd49a7ad852dd9141b2018 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Mon, 14 Aug 2023 16:56:01 -0500 Subject: [PATCH 15/49] remove todos --- .../internal/controllers/routes/generate.go | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/internal/mesh/internal/controllers/routes/generate.go b/internal/mesh/internal/controllers/routes/generate.go index c98c89da621..ac9da2c5f28 100644 --- a/internal/mesh/internal/controllers/routes/generate.go +++ b/internal/mesh/internal/controllers/routes/generate.go @@ -218,8 +218,6 @@ func compile( top.AddTargetsFrom(routeNode) } - // TODO: default route values by port by protocol? - // Now we can do the big sort. gammaSortRouteRules(top) @@ -249,7 +247,6 @@ func compile( Rules: top.HTTPRules, }, } - // TODO case resource.EqualType(top.RouteType, types.GRPCRouteType): mc.Config = &pbmesh.ComputedPortRoutes_Grpc{ Grpc: &pbmesh.InterpretedGRPCRoute{ @@ -257,7 +254,6 @@ func compile( Rules: top.GRPCRules, }, } - // TODO case resource.EqualType(top.RouteType, types.TCPRouteType): mc.Config = &pbmesh.ComputedPortRoutes_Tcp{ Tcp: &pbmesh.InterpretedTCPRoute{ @@ -265,15 +261,12 @@ func compile( Rules: top.TCPRules, }, } - // TODO default: panic("impossible") } computedRoutes.PortedConfigs[port] = mc - // TODO: prune dead targets from targets map - for _, details := range mc.Targets { svcRef := details.BackendRef.Ref @@ -297,7 +290,6 @@ func compile( } } - // TODO: Create derived composite routing instruction. computedRoutes.PortedConfigs[port] = mc } @@ -334,7 +326,7 @@ func compileHTTPRouteNode( irule := &pbmesh.InterpretedHTTPRouteRule{ Matches: protoSliceClone(rule.Matches), Filters: protoSliceClone(rule.Filters), - BackendRefs: make([]*pbmesh.InterpretedHTTPBackendRef, 0, len(rule.BackendRefs)), // TODO: populate + BackendRefs: make([]*pbmesh.InterpretedHTTPBackendRef, 0, len(rule.BackendRefs)), Timeouts: rule.Timeouts, Retries: rule.Retries, } @@ -384,7 +376,6 @@ func compileHTTPRouteNode( if inMesh && found { details := &pbmesh.BackendTargetDetails{ BackendRef: backendRef.BackendRef, - // TODO } backendTarget = node.AddTarget(backendRef.BackendRef, details) } else { @@ -427,7 +418,7 @@ func compileGRPCRouteNode( irule := &pbmesh.InterpretedGRPCRouteRule{ Matches: protoSliceClone(rule.Matches), Filters: protoSliceClone(rule.Filters), - BackendRefs: make([]*pbmesh.InterpretedGRPCBackendRef, 0, len(rule.BackendRefs)), // TODO: populate + BackendRefs: make([]*pbmesh.InterpretedGRPCBackendRef, 0, len(rule.BackendRefs)), Timeouts: rule.Timeouts, Retries: rule.Retries, } @@ -467,7 +458,6 @@ func compileGRPCRouteNode( if inMesh && found { details := &pbmesh.BackendTargetDetails{ BackendRef: backendRef.BackendRef, - // TODO } backendTarget = node.AddTarget(backendRef.BackendRef, details) } else { @@ -509,7 +499,7 @@ func compileTCPRouteNode( node.TCPRules = make([]*pbmesh.InterpretedTCPRouteRule, 0, len(route.Rules)) for _, rule := range route.Rules { irule := &pbmesh.InterpretedTCPRouteRule{ - BackendRefs: make([]*pbmesh.InterpretedTCPBackendRef, 0, len(rule.BackendRefs)), // TODO: populate + BackendRefs: make([]*pbmesh.InterpretedTCPBackendRef, 0, len(rule.BackendRefs)), } // https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.TCPRoute @@ -542,7 +532,6 @@ func compileTCPRouteNode( if inMesh && found { details := &pbmesh.BackendTargetDetails{ BackendRef: backendRef.BackendRef, - // TODO } backendTarget = node.AddTarget(backendRef.BackendRef, details) } else { @@ -578,7 +567,6 @@ func createDefaultRouteNode( defaultBackendTarget := routeNode.AddTarget(defaultBackendRef, &pbmesh.BackendTargetDetails{ BackendRef: defaultBackendRef, - // TODO }) switch { case resource.EqualType(types.HTTPRouteType, typ): From 091cc12ac0152b909b59835218c7c32464bc9122 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Mon, 14 Aug 2023 17:03:37 -0500 Subject: [PATCH 16/49] rename protobuf message types --- .../controllers/routes/controller_test.go | 158 ++-- .../internal/controllers/routes/generate.go | 44 +- .../controllers/routes/intermediate.go | 6 +- .../internal/controllers/routes/sort_rules.go | 4 +- .../internal/types/computed_routes_test.go | 2 +- .../v1alpha1/computed_routes.pb.binary.go | 36 +- .../pbmesh/v1alpha1/computed_routes.pb.go | 721 +++++++++--------- .../pbmesh/v1alpha1/computed_routes.proto | 36 +- 8 files changed, 501 insertions(+), 506 deletions(-) diff --git a/internal/mesh/internal/controllers/routes/controller_test.go b/internal/mesh/internal/controllers/routes/controller_test.go index 889ee96cf68..8ab1341e9e2 100644 --- a/internal/mesh/internal/controllers/routes/controller_test.go +++ b/internal/mesh/internal/controllers/routes/controller_test.go @@ -92,10 +92,10 @@ func (suite *controllerSuite) TestController() { "tcp": { UsingDefaultConfig: true, Config: &pbmesh.ComputedPortRoutes_Tcp{ - Tcp: &pbmesh.InterpretedTCPRoute{ + Tcp: &pbmesh.ComputedTCPRoute{ ParentRef: newParentRef(apiServiceRef, "tcp"), - Rules: []*pbmesh.InterpretedTCPRouteRule{{ - BackendRefs: []*pbmesh.InterpretedTCPBackendRef{{ + Rules: []*pbmesh.ComputedTCPRouteRule{{ + BackendRefs: []*pbmesh.ComputedTCPBackendRef{{ BackendTarget: backendName("api", "tcp"), }}, }}, @@ -157,10 +157,10 @@ func (suite *controllerSuite) TestController() { "tcp": { UsingDefaultConfig: true, Config: &pbmesh.ComputedPortRoutes_Tcp{ - Tcp: &pbmesh.InterpretedTCPRoute{ + Tcp: &pbmesh.ComputedTCPRoute{ ParentRef: newParentRef(apiServiceRef, "tcp"), - Rules: []*pbmesh.InterpretedTCPRouteRule{{ - BackendRefs: []*pbmesh.InterpretedTCPBackendRef{{ + Rules: []*pbmesh.ComputedTCPRouteRule{{ + BackendRefs: []*pbmesh.ComputedTCPBackendRef{{ BackendTarget: backendName("api", "tcp"), }}, }}, @@ -176,16 +176,16 @@ func (suite *controllerSuite) TestController() { "http": { UsingDefaultConfig: true, Config: &pbmesh.ComputedPortRoutes_Http{ - Http: &pbmesh.InterpretedHTTPRoute{ + Http: &pbmesh.ComputedHTTPRoute{ ParentRef: newParentRef(apiServiceRef, "http"), - Rules: []*pbmesh.InterpretedHTTPRouteRule{{ + Rules: []*pbmesh.ComputedHTTPRouteRule{{ Matches: []*pbmesh.HTTPRouteMatch{{ Path: &pbmesh.HTTPPathMatch{ Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, Value: "/", }, }}, - BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ BackendTarget: backendName("api", "http"), }}, }}, @@ -201,16 +201,16 @@ func (suite *controllerSuite) TestController() { "http2": { UsingDefaultConfig: true, Config: &pbmesh.ComputedPortRoutes_Http{ - Http: &pbmesh.InterpretedHTTPRoute{ + Http: &pbmesh.ComputedHTTPRoute{ ParentRef: newParentRef(apiServiceRef, "http2"), - Rules: []*pbmesh.InterpretedHTTPRouteRule{{ + Rules: []*pbmesh.ComputedHTTPRouteRule{{ Matches: []*pbmesh.HTTPRouteMatch{{ Path: &pbmesh.HTTPPathMatch{ Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, Value: "/", }, }}, - BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ BackendTarget: backendName("api", "http2"), }}, }}, @@ -226,11 +226,11 @@ func (suite *controllerSuite) TestController() { "grpc": { UsingDefaultConfig: true, Config: &pbmesh.ComputedPortRoutes_Grpc{ - Grpc: &pbmesh.InterpretedGRPCRoute{ + Grpc: &pbmesh.ComputedGRPCRoute{ ParentRef: newParentRef(apiServiceRef, "grpc"), - Rules: []*pbmesh.InterpretedGRPCRouteRule{{ + Rules: []*pbmesh.ComputedGRPCRouteRule{{ Matches: []*pbmesh.GRPCRouteMatch{{}}, - BackendRefs: []*pbmesh.InterpretedGRPCBackendRef{{ + BackendRefs: []*pbmesh.ComputedGRPCBackendRef{{ BackendTarget: backendName("api", "grpc"), }}, }}, @@ -302,10 +302,10 @@ func (suite *controllerSuite) TestController() { PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ "tcp": { Config: &pbmesh.ComputedPortRoutes_Tcp{ - Tcp: &pbmesh.InterpretedTCPRoute{ + Tcp: &pbmesh.ComputedTCPRoute{ ParentRef: newParentRef(apiServiceRef, "tcp"), - Rules: []*pbmesh.InterpretedTCPRouteRule{{ - BackendRefs: []*pbmesh.InterpretedTCPBackendRef{{ + Rules: []*pbmesh.ComputedTCPRouteRule{{ + BackendRefs: []*pbmesh.ComputedTCPBackendRef{{ BackendTarget: backendName("foo", "tcp"), }}, }}, @@ -320,18 +320,18 @@ func (suite *controllerSuite) TestController() { }, "http": { Config: &pbmesh.ComputedPortRoutes_Http{ - Http: &pbmesh.InterpretedHTTPRoute{ + Http: &pbmesh.ComputedHTTPRoute{ ParentRef: newParentRef(apiServiceRef, "http"), - Rules: []*pbmesh.InterpretedHTTPRouteRule{ + Rules: []*pbmesh.ComputedHTTPRouteRule{ { Matches: defaultHTTPRouteMatches(), - BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ BackendTarget: backendName("foo", "http"), }}, }, { Matches: defaultHTTPRouteMatches(), - BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ BackendTarget: types.NullRouteBackend, }}, }, @@ -347,18 +347,18 @@ func (suite *controllerSuite) TestController() { }, "grpc": { Config: &pbmesh.ComputedPortRoutes_Grpc{ - Grpc: &pbmesh.InterpretedGRPCRoute{ + Grpc: &pbmesh.ComputedGRPCRoute{ ParentRef: newParentRef(apiServiceRef, "grpc"), - Rules: []*pbmesh.InterpretedGRPCRouteRule{ + Rules: []*pbmesh.ComputedGRPCRouteRule{ { Matches: []*pbmesh.GRPCRouteMatch{{}}, - BackendRefs: []*pbmesh.InterpretedGRPCBackendRef{{ + BackendRefs: []*pbmesh.ComputedGRPCBackendRef{{ BackendTarget: backendName("foo", "grpc"), }}, }, { Matches: []*pbmesh.GRPCRouteMatch{{}}, - BackendRefs: []*pbmesh.InterpretedGRPCBackendRef{{ + BackendRefs: []*pbmesh.ComputedGRPCBackendRef{{ BackendTarget: types.NullRouteBackend, }}, }, @@ -374,18 +374,18 @@ func (suite *controllerSuite) TestController() { }, "http2": { Config: &pbmesh.ComputedPortRoutes_Http{ - Http: &pbmesh.InterpretedHTTPRoute{ + Http: &pbmesh.ComputedHTTPRoute{ ParentRef: newParentRef(apiServiceRef, "http2"), - Rules: []*pbmesh.InterpretedHTTPRouteRule{ + Rules: []*pbmesh.ComputedHTTPRouteRule{ { Matches: defaultHTTPRouteMatches(), - BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ BackendTarget: backendName("foo", "http2"), }}, }, { Matches: defaultHTTPRouteMatches(), - BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ BackendTarget: types.NullRouteBackend, }}, }, @@ -475,16 +475,16 @@ func (suite *controllerSuite) TestController() { PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ "tcp": { Config: &pbmesh.ComputedPortRoutes_Tcp{ - Tcp: &pbmesh.InterpretedTCPRoute{ + Tcp: &pbmesh.ComputedTCPRoute{ ParentRef: newParentRef(apiServiceRef, "tcp"), - Rules: []*pbmesh.InterpretedTCPRouteRule{ + Rules: []*pbmesh.ComputedTCPRouteRule{ { - BackendRefs: []*pbmesh.InterpretedTCPBackendRef{{ + BackendRefs: []*pbmesh.ComputedTCPBackendRef{{ BackendTarget: backendName("foo", "tcp"), }}, }, { - BackendRefs: []*pbmesh.InterpretedTCPBackendRef{{ + BackendRefs: []*pbmesh.ComputedTCPBackendRef{{ BackendTarget: types.NullRouteBackend, }}, }, @@ -500,9 +500,9 @@ func (suite *controllerSuite) TestController() { }, "http": { Config: &pbmesh.ComputedPortRoutes_Http{ - Http: &pbmesh.InterpretedHTTPRoute{ + Http: &pbmesh.ComputedHTTPRoute{ ParentRef: newParentRef(apiServiceRef, "http"), - Rules: []*pbmesh.InterpretedHTTPRouteRule{ + Rules: []*pbmesh.ComputedHTTPRouteRule{ { Matches: []*pbmesh.HTTPRouteMatch{{ Path: &pbmesh.HTTPPathMatch{ @@ -510,19 +510,19 @@ func (suite *controllerSuite) TestController() { Value: "/healthz", }, }}, - BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ BackendTarget: types.NullRouteBackend, }}, }, { Matches: defaultHTTPRouteMatches(), - BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ BackendTarget: backendName("foo", "http"), }}, }, { Matches: defaultHTTPRouteMatches(), - BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ BackendTarget: types.NullRouteBackend, }}, }, @@ -538,12 +538,12 @@ func (suite *controllerSuite) TestController() { }, "grpc": { Config: &pbmesh.ComputedPortRoutes_Grpc{ - Grpc: &pbmesh.InterpretedGRPCRoute{ + Grpc: &pbmesh.ComputedGRPCRoute{ ParentRef: newParentRef(apiServiceRef, "grpc"), - Rules: []*pbmesh.InterpretedGRPCRouteRule{ + Rules: []*pbmesh.ComputedGRPCRouteRule{ { Matches: []*pbmesh.GRPCRouteMatch{{}}, - BackendRefs: []*pbmesh.InterpretedGRPCBackendRef{{ + BackendRefs: []*pbmesh.ComputedGRPCBackendRef{{ BackendTarget: backendName("foo", "grpc"), }}, }, @@ -555,13 +555,13 @@ func (suite *controllerSuite) TestController() { Method: "charge", }, }}, - BackendRefs: []*pbmesh.InterpretedGRPCBackendRef{{ + BackendRefs: []*pbmesh.ComputedGRPCBackendRef{{ BackendTarget: types.NullRouteBackend, }}, }, { Matches: []*pbmesh.GRPCRouteMatch{{}}, - BackendRefs: []*pbmesh.InterpretedGRPCBackendRef{{ + BackendRefs: []*pbmesh.ComputedGRPCBackendRef{{ BackendTarget: types.NullRouteBackend, }}, }, @@ -577,9 +577,9 @@ func (suite *controllerSuite) TestController() { }, "http2": { Config: &pbmesh.ComputedPortRoutes_Http{ - Http: &pbmesh.InterpretedHTTPRoute{ + Http: &pbmesh.ComputedHTTPRoute{ ParentRef: newParentRef(apiServiceRef, "http2"), - Rules: []*pbmesh.InterpretedHTTPRouteRule{ + Rules: []*pbmesh.ComputedHTTPRouteRule{ { Matches: []*pbmesh.HTTPRouteMatch{{ Path: &pbmesh.HTTPPathMatch{ @@ -587,19 +587,19 @@ func (suite *controllerSuite) TestController() { Value: "/healthz", }, }}, - BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ BackendTarget: types.NullRouteBackend, }}, }, { Matches: defaultHTTPRouteMatches(), - BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ BackendTarget: backendName("foo", "http2"), }}, }, { Matches: defaultHTTPRouteMatches(), - BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ BackendTarget: types.NullRouteBackend, }}, }, @@ -699,10 +699,10 @@ func (suite *controllerSuite) TestController() { PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ "tcp": { Config: &pbmesh.ComputedPortRoutes_Tcp{ - Tcp: &pbmesh.InterpretedTCPRoute{ + Tcp: &pbmesh.ComputedTCPRoute{ ParentRef: newParentRef(apiServiceRef, "tcp"), - Rules: []*pbmesh.InterpretedTCPRouteRule{{ - BackendRefs: []*pbmesh.InterpretedTCPBackendRef{{ + Rules: []*pbmesh.ComputedTCPRouteRule{{ + BackendRefs: []*pbmesh.ComputedTCPBackendRef{{ BackendTarget: backendName("foo", "tcp"), }}, }}, @@ -717,18 +717,18 @@ func (suite *controllerSuite) TestController() { }, "http": { Config: &pbmesh.ComputedPortRoutes_Http{ - Http: &pbmesh.InterpretedHTTPRoute{ + Http: &pbmesh.ComputedHTTPRoute{ ParentRef: newParentRef(apiServiceRef, "http"), - Rules: []*pbmesh.InterpretedHTTPRouteRule{ + Rules: []*pbmesh.ComputedHTTPRouteRule{ { Matches: defaultHTTPRouteMatches(), - BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ BackendTarget: backendName("foo", "http"), }}, }, { Matches: defaultHTTPRouteMatches(), - BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ BackendTarget: types.NullRouteBackend, }}, }, @@ -744,18 +744,18 @@ func (suite *controllerSuite) TestController() { }, "grpc": { Config: &pbmesh.ComputedPortRoutes_Grpc{ - Grpc: &pbmesh.InterpretedGRPCRoute{ + Grpc: &pbmesh.ComputedGRPCRoute{ ParentRef: newParentRef(apiServiceRef, "grpc"), - Rules: []*pbmesh.InterpretedGRPCRouteRule{ + Rules: []*pbmesh.ComputedGRPCRouteRule{ { Matches: []*pbmesh.GRPCRouteMatch{{}}, - BackendRefs: []*pbmesh.InterpretedGRPCBackendRef{{ + BackendRefs: []*pbmesh.ComputedGRPCBackendRef{{ BackendTarget: backendName("foo", "grpc"), }}, }, { Matches: []*pbmesh.GRPCRouteMatch{{}}, - BackendRefs: []*pbmesh.InterpretedGRPCBackendRef{{ + BackendRefs: []*pbmesh.ComputedGRPCBackendRef{{ BackendTarget: types.NullRouteBackend, }}, }, @@ -771,18 +771,18 @@ func (suite *controllerSuite) TestController() { }, "http2": { Config: &pbmesh.ComputedPortRoutes_Http{ - Http: &pbmesh.InterpretedHTTPRoute{ + Http: &pbmesh.ComputedHTTPRoute{ ParentRef: newParentRef(apiServiceRef, "http2"), - Rules: []*pbmesh.InterpretedHTTPRouteRule{ + Rules: []*pbmesh.ComputedHTTPRouteRule{ { Matches: defaultHTTPRouteMatches(), - BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ BackendTarget: backendName("foo", "http2"), }}, }, { Matches: defaultHTTPRouteMatches(), - BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ BackendTarget: types.NullRouteBackend, }}, }, @@ -849,10 +849,10 @@ func (suite *controllerSuite) TestController() { PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ "tcp": { Config: &pbmesh.ComputedPortRoutes_Tcp{ - Tcp: &pbmesh.InterpretedTCPRoute{ + Tcp: &pbmesh.ComputedTCPRoute{ ParentRef: newParentRef(apiServiceRef, "tcp"), - Rules: []*pbmesh.InterpretedTCPRouteRule{{ - BackendRefs: []*pbmesh.InterpretedTCPBackendRef{{ + Rules: []*pbmesh.ComputedTCPRouteRule{{ + BackendRefs: []*pbmesh.ComputedTCPBackendRef{{ BackendTarget: backendName("foo", "tcp"), }}, }}, @@ -867,18 +867,18 @@ func (suite *controllerSuite) TestController() { }, "http": { Config: &pbmesh.ComputedPortRoutes_Http{ - Http: &pbmesh.InterpretedHTTPRoute{ + Http: &pbmesh.ComputedHTTPRoute{ ParentRef: newParentRef(apiServiceRef, "http"), - Rules: []*pbmesh.InterpretedHTTPRouteRule{ + Rules: []*pbmesh.ComputedHTTPRouteRule{ { Matches: defaultHTTPRouteMatches(), - BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ BackendTarget: backendName("foo", "http"), }}, }, { Matches: defaultHTTPRouteMatches(), - BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ BackendTarget: types.NullRouteBackend, }}, }, @@ -894,18 +894,18 @@ func (suite *controllerSuite) TestController() { }, "grpc": { Config: &pbmesh.ComputedPortRoutes_Grpc{ - Grpc: &pbmesh.InterpretedGRPCRoute{ + Grpc: &pbmesh.ComputedGRPCRoute{ ParentRef: newParentRef(apiServiceRef, "grpc"), - Rules: []*pbmesh.InterpretedGRPCRouteRule{ + Rules: []*pbmesh.ComputedGRPCRouteRule{ { Matches: []*pbmesh.GRPCRouteMatch{{}}, - BackendRefs: []*pbmesh.InterpretedGRPCBackendRef{{ + BackendRefs: []*pbmesh.ComputedGRPCBackendRef{{ BackendTarget: backendName("foo", "grpc"), }}, }, { Matches: []*pbmesh.GRPCRouteMatch{{}}, - BackendRefs: []*pbmesh.InterpretedGRPCBackendRef{{ + BackendRefs: []*pbmesh.ComputedGRPCBackendRef{{ BackendTarget: types.NullRouteBackend, }}, }, @@ -921,18 +921,18 @@ func (suite *controllerSuite) TestController() { }, "http2": { Config: &pbmesh.ComputedPortRoutes_Http{ - Http: &pbmesh.InterpretedHTTPRoute{ + Http: &pbmesh.ComputedHTTPRoute{ ParentRef: newParentRef(apiServiceRef, "http2"), - Rules: []*pbmesh.InterpretedHTTPRouteRule{ + Rules: []*pbmesh.ComputedHTTPRouteRule{ { Matches: defaultHTTPRouteMatches(), - BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ BackendTarget: backendName("foo", "http2"), }}, }, { Matches: defaultHTTPRouteMatches(), - BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ BackendTarget: types.NullRouteBackend, }}, }, diff --git a/internal/mesh/internal/controllers/routes/generate.go b/internal/mesh/internal/controllers/routes/generate.go index ac9da2c5f28..581478b41a4 100644 --- a/internal/mesh/internal/controllers/routes/generate.go +++ b/internal/mesh/internal/controllers/routes/generate.go @@ -56,8 +56,6 @@ func compile( computedRoutesID *pbresource.ID, pending PendingStatuses, ) *ComputedRoutesResult { - logger := loggerFor(computedRoutesID) - // There is one computed routes resource for the entire service (perfect name alignment). // // All ports are embedded within. @@ -242,21 +240,21 @@ func compile( switch { case resource.EqualType(top.RouteType, types.HTTPRouteType): mc.Config = &pbmesh.ComputedPortRoutes_Http{ - Http: &pbmesh.InterpretedHTTPRoute{ + Http: &pbmesh.ComputedHTTPRoute{ ParentRef: parentRef, Rules: top.HTTPRules, }, } case resource.EqualType(top.RouteType, types.GRPCRouteType): mc.Config = &pbmesh.ComputedPortRoutes_Grpc{ - Grpc: &pbmesh.InterpretedGRPCRoute{ + Grpc: &pbmesh.ComputedGRPCRoute{ ParentRef: parentRef, Rules: top.GRPCRules, }, } case resource.EqualType(top.RouteType, types.TCPRouteType): mc.Config = &pbmesh.ComputedPortRoutes_Tcp{ - Tcp: &pbmesh.InterpretedTCPRoute{ + Tcp: &pbmesh.ComputedTCPRoute{ ParentRef: parentRef, Rules: top.TCPRules, }, @@ -321,12 +319,12 @@ func compileHTTPRouteNode( node.RouteType = types.HTTPRouteType node.HTTP = dec - node.HTTPRules = make([]*pbmesh.InterpretedHTTPRouteRule, 0, len(route.Rules)) + node.HTTPRules = make([]*pbmesh.ComputedHTTPRouteRule, 0, len(route.Rules)) for _, rule := range route.Rules { - irule := &pbmesh.InterpretedHTTPRouteRule{ + irule := &pbmesh.ComputedHTTPRouteRule{ Matches: protoSliceClone(rule.Matches), Filters: protoSliceClone(rule.Filters), - BackendRefs: make([]*pbmesh.InterpretedHTTPBackendRef, 0, len(rule.BackendRefs)), + BackendRefs: make([]*pbmesh.ComputedHTTPBackendRef, 0, len(rule.BackendRefs)), Timeouts: rule.Timeouts, Retries: rule.Retries, } @@ -382,7 +380,7 @@ func compileHTTPRouteNode( backendTarget = types.NullRouteBackend } } - ibr := &pbmesh.InterpretedHTTPBackendRef{ + ibr := &pbmesh.ComputedHTTPBackendRef{ BackendTarget: backendTarget, Weight: backendRef.Weight, Filters: backendRef.Filters, @@ -413,12 +411,12 @@ func compileGRPCRouteNode( node.RouteType = types.GRPCRouteType node.GRPC = dec - node.GRPCRules = make([]*pbmesh.InterpretedGRPCRouteRule, 0, len(route.Rules)) + node.GRPCRules = make([]*pbmesh.ComputedGRPCRouteRule, 0, len(route.Rules)) for _, rule := range route.Rules { - irule := &pbmesh.InterpretedGRPCRouteRule{ + irule := &pbmesh.ComputedGRPCRouteRule{ Matches: protoSliceClone(rule.Matches), Filters: protoSliceClone(rule.Filters), - BackendRefs: make([]*pbmesh.InterpretedGRPCBackendRef, 0, len(rule.BackendRefs)), + BackendRefs: make([]*pbmesh.ComputedGRPCBackendRef, 0, len(rule.BackendRefs)), Timeouts: rule.Timeouts, Retries: rule.Retries, } @@ -465,7 +463,7 @@ func compileGRPCRouteNode( } } - ibr := &pbmesh.InterpretedGRPCBackendRef{ + ibr := &pbmesh.ComputedGRPCBackendRef{ BackendTarget: backendTarget, Weight: backendRef.Weight, Filters: backendRef.Filters, @@ -496,10 +494,10 @@ func compileTCPRouteNode( node.RouteType = types.TCPRouteType node.TCP = dec - node.TCPRules = make([]*pbmesh.InterpretedTCPRouteRule, 0, len(route.Rules)) + node.TCPRules = make([]*pbmesh.ComputedTCPRouteRule, 0, len(route.Rules)) for _, rule := range route.Rules { - irule := &pbmesh.InterpretedTCPRouteRule{ - BackendRefs: make([]*pbmesh.InterpretedTCPBackendRef, 0, len(rule.BackendRefs)), + irule := &pbmesh.ComputedTCPRouteRule{ + BackendRefs: make([]*pbmesh.ComputedTCPBackendRef, 0, len(rule.BackendRefs)), } // https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.TCPRoute @@ -539,7 +537,7 @@ func compileTCPRouteNode( } } - ibr := &pbmesh.InterpretedTCPBackendRef{ + ibr := &pbmesh.ComputedTCPBackendRef{ BackendTarget: backendTarget, Weight: backendRef.Weight, } @@ -611,9 +609,9 @@ func appendDefaultHTTPRouteRule( routeNode *inputRouteNode, backendTarget string, ) { - routeNode.HTTPRules = append(routeNode.HTTPRules, &pbmesh.InterpretedHTTPRouteRule{ + routeNode.HTTPRules = append(routeNode.HTTPRules, &pbmesh.ComputedHTTPRouteRule{ Matches: defaultHTTPRouteMatches(), - BackendRefs: []*pbmesh.InterpretedHTTPBackendRef{{ + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ BackendTarget: backendTarget, }}, }) @@ -631,9 +629,9 @@ func appendDefaultGRPCRouteRule( routeNode *inputRouteNode, backendTarget string, ) { - routeNode.GRPCRules = append(routeNode.GRPCRules, &pbmesh.InterpretedGRPCRouteRule{ + routeNode.GRPCRules = append(routeNode.GRPCRules, &pbmesh.ComputedGRPCRouteRule{ Matches: defaultGRPCRouteMatches(), - BackendRefs: []*pbmesh.InterpretedGRPCBackendRef{{ + BackendRefs: []*pbmesh.ComputedGRPCBackendRef{{ BackendTarget: backendTarget, }}, }) @@ -651,8 +649,8 @@ func appendDefaultTCPRouteRule( routeNode *inputRouteNode, backendTarget string, ) { - routeNode.TCPRules = append(routeNode.TCPRules, &pbmesh.InterpretedTCPRouteRule{ - BackendRefs: []*pbmesh.InterpretedTCPBackendRef{{ + routeNode.TCPRules = append(routeNode.TCPRules, &pbmesh.ComputedTCPRouteRule{ + BackendRefs: []*pbmesh.ComputedTCPBackendRef{{ BackendTarget: backendTarget, }}, }) diff --git a/internal/mesh/internal/controllers/routes/intermediate.go b/internal/mesh/internal/controllers/routes/intermediate.go index 63b9beeca54..4c95b432ff3 100644 --- a/internal/mesh/internal/controllers/routes/intermediate.go +++ b/internal/mesh/internal/controllers/routes/intermediate.go @@ -18,9 +18,9 @@ type inputRouteNode struct { ParentPort string // always set // only one of these can be set to non-empty - HTTPRules []*pbmesh.InterpretedHTTPRouteRule - GRPCRules []*pbmesh.InterpretedGRPCRouteRule - TCPRules []*pbmesh.InterpretedTCPRouteRule + HTTPRules []*pbmesh.ComputedHTTPRouteRule + GRPCRules []*pbmesh.ComputedGRPCRouteRule + TCPRules []*pbmesh.ComputedTCPRouteRule RouteType *pbresource.Type Default bool diff --git a/internal/mesh/internal/controllers/routes/sort_rules.go b/internal/mesh/internal/controllers/routes/sort_rules.go index f199e2a7213..0a12225140e 100644 --- a/internal/mesh/internal/controllers/routes/sort_rules.go +++ b/internal/mesh/internal/controllers/routes/sort_rules.go @@ -26,7 +26,7 @@ func gammaSortRouteRules(node *inputRouteNode) { } } -func gammaSortHTTPRouteRules(rules []*pbmesh.InterpretedHTTPRouteRule) { +func gammaSortHTTPRouteRules(rules []*pbmesh.ComputedHTTPRouteRule) { // First generate a parallel slice. sortable := &sortableHTTPRouteRules{ rules: rules, @@ -83,7 +83,7 @@ type derivedHTTPRouteRuleInfo struct { } type sortableHTTPRouteRules struct { - rules []*pbmesh.InterpretedHTTPRouteRule + rules []*pbmesh.ComputedHTTPRouteRule derivedInfo []*derivedHTTPRouteRuleInfo } diff --git a/internal/mesh/internal/types/computed_routes_test.go b/internal/mesh/internal/types/computed_routes_test.go index 8040e2afbd2..9967acda752 100644 --- a/internal/mesh/internal/types/computed_routes_test.go +++ b/internal/mesh/internal/types/computed_routes_test.go @@ -58,7 +58,7 @@ func TestValidateComputedRoutes(t *testing.T) { PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ "http": { Config: &pbmesh.ComputedPortRoutes_Tcp{ - Tcp: &pbmesh.InterpretedTCPRoute{}, + Tcp: &pbmesh.ComputedTCPRoute{}, }, Targets: map[string]*pbmesh.BackendTargetDetails{ "foo": {}, diff --git a/proto-public/pbmesh/v1alpha1/computed_routes.pb.binary.go b/proto-public/pbmesh/v1alpha1/computed_routes.pb.binary.go index cd30ceac270..57112113a21 100644 --- a/proto-public/pbmesh/v1alpha1/computed_routes.pb.binary.go +++ b/proto-public/pbmesh/v1alpha1/computed_routes.pb.binary.go @@ -28,92 +28,92 @@ func (msg *ComputedPortRoutes) UnmarshalBinary(b []byte) error { } // MarshalBinary implements encoding.BinaryMarshaler -func (msg *InterpretedHTTPRoute) MarshalBinary() ([]byte, error) { +func (msg *ComputedHTTPRoute) MarshalBinary() ([]byte, error) { return proto.Marshal(msg) } // UnmarshalBinary implements encoding.BinaryUnmarshaler -func (msg *InterpretedHTTPRoute) UnmarshalBinary(b []byte) error { +func (msg *ComputedHTTPRoute) UnmarshalBinary(b []byte) error { return proto.Unmarshal(b, msg) } // MarshalBinary implements encoding.BinaryMarshaler -func (msg *InterpretedHTTPRouteRule) MarshalBinary() ([]byte, error) { +func (msg *ComputedHTTPRouteRule) MarshalBinary() ([]byte, error) { return proto.Marshal(msg) } // UnmarshalBinary implements encoding.BinaryUnmarshaler -func (msg *InterpretedHTTPRouteRule) UnmarshalBinary(b []byte) error { +func (msg *ComputedHTTPRouteRule) UnmarshalBinary(b []byte) error { return proto.Unmarshal(b, msg) } // MarshalBinary implements encoding.BinaryMarshaler -func (msg *InterpretedHTTPBackendRef) MarshalBinary() ([]byte, error) { +func (msg *ComputedHTTPBackendRef) MarshalBinary() ([]byte, error) { return proto.Marshal(msg) } // UnmarshalBinary implements encoding.BinaryUnmarshaler -func (msg *InterpretedHTTPBackendRef) UnmarshalBinary(b []byte) error { +func (msg *ComputedHTTPBackendRef) UnmarshalBinary(b []byte) error { return proto.Unmarshal(b, msg) } // MarshalBinary implements encoding.BinaryMarshaler -func (msg *InterpretedGRPCRoute) MarshalBinary() ([]byte, error) { +func (msg *ComputedGRPCRoute) MarshalBinary() ([]byte, error) { return proto.Marshal(msg) } // UnmarshalBinary implements encoding.BinaryUnmarshaler -func (msg *InterpretedGRPCRoute) UnmarshalBinary(b []byte) error { +func (msg *ComputedGRPCRoute) UnmarshalBinary(b []byte) error { return proto.Unmarshal(b, msg) } // MarshalBinary implements encoding.BinaryMarshaler -func (msg *InterpretedGRPCRouteRule) MarshalBinary() ([]byte, error) { +func (msg *ComputedGRPCRouteRule) MarshalBinary() ([]byte, error) { return proto.Marshal(msg) } // UnmarshalBinary implements encoding.BinaryUnmarshaler -func (msg *InterpretedGRPCRouteRule) UnmarshalBinary(b []byte) error { +func (msg *ComputedGRPCRouteRule) UnmarshalBinary(b []byte) error { return proto.Unmarshal(b, msg) } // MarshalBinary implements encoding.BinaryMarshaler -func (msg *InterpretedGRPCBackendRef) MarshalBinary() ([]byte, error) { +func (msg *ComputedGRPCBackendRef) MarshalBinary() ([]byte, error) { return proto.Marshal(msg) } // UnmarshalBinary implements encoding.BinaryUnmarshaler -func (msg *InterpretedGRPCBackendRef) UnmarshalBinary(b []byte) error { +func (msg *ComputedGRPCBackendRef) UnmarshalBinary(b []byte) error { return proto.Unmarshal(b, msg) } // MarshalBinary implements encoding.BinaryMarshaler -func (msg *InterpretedTCPRoute) MarshalBinary() ([]byte, error) { +func (msg *ComputedTCPRoute) MarshalBinary() ([]byte, error) { return proto.Marshal(msg) } // UnmarshalBinary implements encoding.BinaryUnmarshaler -func (msg *InterpretedTCPRoute) UnmarshalBinary(b []byte) error { +func (msg *ComputedTCPRoute) UnmarshalBinary(b []byte) error { return proto.Unmarshal(b, msg) } // MarshalBinary implements encoding.BinaryMarshaler -func (msg *InterpretedTCPRouteRule) MarshalBinary() ([]byte, error) { +func (msg *ComputedTCPRouteRule) MarshalBinary() ([]byte, error) { return proto.Marshal(msg) } // UnmarshalBinary implements encoding.BinaryUnmarshaler -func (msg *InterpretedTCPRouteRule) UnmarshalBinary(b []byte) error { +func (msg *ComputedTCPRouteRule) UnmarshalBinary(b []byte) error { return proto.Unmarshal(b, msg) } // MarshalBinary implements encoding.BinaryMarshaler -func (msg *InterpretedTCPBackendRef) MarshalBinary() ([]byte, error) { +func (msg *ComputedTCPBackendRef) MarshalBinary() ([]byte, error) { return proto.Marshal(msg) } // UnmarshalBinary implements encoding.BinaryUnmarshaler -func (msg *InterpretedTCPBackendRef) UnmarshalBinary(b []byte) error { +func (msg *ComputedTCPBackendRef) UnmarshalBinary(b []byte) error { return proto.Unmarshal(b, msg) } diff --git a/proto-public/pbmesh/v1alpha1/computed_routes.pb.go b/proto-public/pbmesh/v1alpha1/computed_routes.pb.go index 0a8dd22fb3e..8b614d20e5a 100644 --- a/proto-public/pbmesh/v1alpha1/computed_routes.pb.go +++ b/proto-public/pbmesh/v1alpha1/computed_routes.pb.go @@ -127,21 +127,21 @@ func (m *ComputedPortRoutes) GetConfig() isComputedPortRoutes_Config { return nil } -func (x *ComputedPortRoutes) GetHttp() *InterpretedHTTPRoute { +func (x *ComputedPortRoutes) GetHttp() *ComputedHTTPRoute { if x, ok := x.GetConfig().(*ComputedPortRoutes_Http); ok { return x.Http } return nil } -func (x *ComputedPortRoutes) GetGrpc() *InterpretedGRPCRoute { +func (x *ComputedPortRoutes) GetGrpc() *ComputedGRPCRoute { if x, ok := x.GetConfig().(*ComputedPortRoutes_Grpc); ok { return x.Grpc } return nil } -func (x *ComputedPortRoutes) GetTcp() *InterpretedTCPRoute { +func (x *ComputedPortRoutes) GetTcp() *ComputedTCPRoute { if x, ok := x.GetConfig().(*ComputedPortRoutes_Tcp); ok { return x.Tcp } @@ -167,15 +167,15 @@ type isComputedPortRoutes_Config interface { } type ComputedPortRoutes_Http struct { - Http *InterpretedHTTPRoute `protobuf:"bytes,1,opt,name=http,proto3,oneof"` + Http *ComputedHTTPRoute `protobuf:"bytes,1,opt,name=http,proto3,oneof"` } type ComputedPortRoutes_Grpc struct { - Grpc *InterpretedGRPCRoute `protobuf:"bytes,2,opt,name=grpc,proto3,oneof"` + Grpc *ComputedGRPCRoute `protobuf:"bytes,2,opt,name=grpc,proto3,oneof"` } type ComputedPortRoutes_Tcp struct { - Tcp *InterpretedTCPRoute `protobuf:"bytes,3,opt,name=tcp,proto3,oneof"` + Tcp *ComputedTCPRoute `protobuf:"bytes,3,opt,name=tcp,proto3,oneof"` } func (*ComputedPortRoutes_Http) isComputedPortRoutes_Config() {} @@ -184,17 +184,17 @@ func (*ComputedPortRoutes_Grpc) isComputedPortRoutes_Config() {} func (*ComputedPortRoutes_Tcp) isComputedPortRoutes_Config() {} -type InterpretedHTTPRoute struct { +type ComputedHTTPRoute struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ParentRef *ParentReference `protobuf:"bytes,1,opt,name=parent_ref,json=parentRef,proto3" json:"parent_ref,omitempty"` - Rules []*InterpretedHTTPRouteRule `protobuf:"bytes,3,rep,name=rules,proto3" json:"rules,omitempty"` + ParentRef *ParentReference `protobuf:"bytes,1,opt,name=parent_ref,json=parentRef,proto3" json:"parent_ref,omitempty"` + Rules []*ComputedHTTPRouteRule `protobuf:"bytes,3,rep,name=rules,proto3" json:"rules,omitempty"` } -func (x *InterpretedHTTPRoute) Reset() { - *x = InterpretedHTTPRoute{} +func (x *ComputedHTTPRoute) Reset() { + *x = ComputedHTTPRoute{} if protoimpl.UnsafeEnabled { mi := &file_pbmesh_v1alpha1_computed_routes_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -202,13 +202,13 @@ func (x *InterpretedHTTPRoute) Reset() { } } -func (x *InterpretedHTTPRoute) String() string { +func (x *ComputedHTTPRoute) String() string { return protoimpl.X.MessageStringOf(x) } -func (*InterpretedHTTPRoute) ProtoMessage() {} +func (*ComputedHTTPRoute) ProtoMessage() {} -func (x *InterpretedHTTPRoute) ProtoReflect() protoreflect.Message { +func (x *ComputedHTTPRoute) ProtoReflect() protoreflect.Message { mi := &file_pbmesh_v1alpha1_computed_routes_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -220,39 +220,39 @@ func (x *InterpretedHTTPRoute) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use InterpretedHTTPRoute.ProtoReflect.Descriptor instead. -func (*InterpretedHTTPRoute) Descriptor() ([]byte, []int) { +// Deprecated: Use ComputedHTTPRoute.ProtoReflect.Descriptor instead. +func (*ComputedHTTPRoute) Descriptor() ([]byte, []int) { return file_pbmesh_v1alpha1_computed_routes_proto_rawDescGZIP(), []int{2} } -func (x *InterpretedHTTPRoute) GetParentRef() *ParentReference { +func (x *ComputedHTTPRoute) GetParentRef() *ParentReference { if x != nil { return x.ParentRef } return nil } -func (x *InterpretedHTTPRoute) GetRules() []*InterpretedHTTPRouteRule { +func (x *ComputedHTTPRoute) GetRules() []*ComputedHTTPRouteRule { if x != nil { return x.Rules } return nil } -type InterpretedHTTPRouteRule struct { +type ComputedHTTPRouteRule struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Matches []*HTTPRouteMatch `protobuf:"bytes,1,rep,name=matches,proto3" json:"matches,omitempty"` - Filters []*HTTPRouteFilter `protobuf:"bytes,2,rep,name=filters,proto3" json:"filters,omitempty"` - BackendRefs []*InterpretedHTTPBackendRef `protobuf:"bytes,3,rep,name=backend_refs,json=backendRefs,proto3" json:"backend_refs,omitempty"` - Timeouts *HTTPRouteTimeouts `protobuf:"bytes,4,opt,name=timeouts,proto3" json:"timeouts,omitempty"` - Retries *HTTPRouteRetries `protobuf:"bytes,5,opt,name=retries,proto3" json:"retries,omitempty"` + Matches []*HTTPRouteMatch `protobuf:"bytes,1,rep,name=matches,proto3" json:"matches,omitempty"` + Filters []*HTTPRouteFilter `protobuf:"bytes,2,rep,name=filters,proto3" json:"filters,omitempty"` + BackendRefs []*ComputedHTTPBackendRef `protobuf:"bytes,3,rep,name=backend_refs,json=backendRefs,proto3" json:"backend_refs,omitempty"` + Timeouts *HTTPRouteTimeouts `protobuf:"bytes,4,opt,name=timeouts,proto3" json:"timeouts,omitempty"` + Retries *HTTPRouteRetries `protobuf:"bytes,5,opt,name=retries,proto3" json:"retries,omitempty"` } -func (x *InterpretedHTTPRouteRule) Reset() { - *x = InterpretedHTTPRouteRule{} +func (x *ComputedHTTPRouteRule) Reset() { + *x = ComputedHTTPRouteRule{} if protoimpl.UnsafeEnabled { mi := &file_pbmesh_v1alpha1_computed_routes_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -260,13 +260,13 @@ func (x *InterpretedHTTPRouteRule) Reset() { } } -func (x *InterpretedHTTPRouteRule) String() string { +func (x *ComputedHTTPRouteRule) String() string { return protoimpl.X.MessageStringOf(x) } -func (*InterpretedHTTPRouteRule) ProtoMessage() {} +func (*ComputedHTTPRouteRule) ProtoMessage() {} -func (x *InterpretedHTTPRouteRule) ProtoReflect() protoreflect.Message { +func (x *ComputedHTTPRouteRule) ProtoReflect() protoreflect.Message { mi := &file_pbmesh_v1alpha1_computed_routes_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -278,47 +278,47 @@ func (x *InterpretedHTTPRouteRule) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use InterpretedHTTPRouteRule.ProtoReflect.Descriptor instead. -func (*InterpretedHTTPRouteRule) Descriptor() ([]byte, []int) { +// Deprecated: Use ComputedHTTPRouteRule.ProtoReflect.Descriptor instead. +func (*ComputedHTTPRouteRule) Descriptor() ([]byte, []int) { return file_pbmesh_v1alpha1_computed_routes_proto_rawDescGZIP(), []int{3} } -func (x *InterpretedHTTPRouteRule) GetMatches() []*HTTPRouteMatch { +func (x *ComputedHTTPRouteRule) GetMatches() []*HTTPRouteMatch { if x != nil { return x.Matches } return nil } -func (x *InterpretedHTTPRouteRule) GetFilters() []*HTTPRouteFilter { +func (x *ComputedHTTPRouteRule) GetFilters() []*HTTPRouteFilter { if x != nil { return x.Filters } return nil } -func (x *InterpretedHTTPRouteRule) GetBackendRefs() []*InterpretedHTTPBackendRef { +func (x *ComputedHTTPRouteRule) GetBackendRefs() []*ComputedHTTPBackendRef { if x != nil { return x.BackendRefs } return nil } -func (x *InterpretedHTTPRouteRule) GetTimeouts() *HTTPRouteTimeouts { +func (x *ComputedHTTPRouteRule) GetTimeouts() *HTTPRouteTimeouts { if x != nil { return x.Timeouts } return nil } -func (x *InterpretedHTTPRouteRule) GetRetries() *HTTPRouteRetries { +func (x *ComputedHTTPRouteRule) GetRetries() *HTTPRouteRetries { if x != nil { return x.Retries } return nil } -type InterpretedHTTPBackendRef struct { +type ComputedHTTPBackendRef struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -335,8 +335,8 @@ type InterpretedHTTPBackendRef struct { Filters []*HTTPRouteFilter `protobuf:"bytes,3,rep,name=filters,proto3" json:"filters,omitempty"` } -func (x *InterpretedHTTPBackendRef) Reset() { - *x = InterpretedHTTPBackendRef{} +func (x *ComputedHTTPBackendRef) Reset() { + *x = ComputedHTTPBackendRef{} if protoimpl.UnsafeEnabled { mi := &file_pbmesh_v1alpha1_computed_routes_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -344,13 +344,13 @@ func (x *InterpretedHTTPBackendRef) Reset() { } } -func (x *InterpretedHTTPBackendRef) String() string { +func (x *ComputedHTTPBackendRef) String() string { return protoimpl.X.MessageStringOf(x) } -func (*InterpretedHTTPBackendRef) ProtoMessage() {} +func (*ComputedHTTPBackendRef) ProtoMessage() {} -func (x *InterpretedHTTPBackendRef) ProtoReflect() protoreflect.Message { +func (x *ComputedHTTPBackendRef) ProtoReflect() protoreflect.Message { mi := &file_pbmesh_v1alpha1_computed_routes_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -362,43 +362,43 @@ func (x *InterpretedHTTPBackendRef) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use InterpretedHTTPBackendRef.ProtoReflect.Descriptor instead. -func (*InterpretedHTTPBackendRef) Descriptor() ([]byte, []int) { +// Deprecated: Use ComputedHTTPBackendRef.ProtoReflect.Descriptor instead. +func (*ComputedHTTPBackendRef) Descriptor() ([]byte, []int) { return file_pbmesh_v1alpha1_computed_routes_proto_rawDescGZIP(), []int{4} } -func (x *InterpretedHTTPBackendRef) GetBackendTarget() string { +func (x *ComputedHTTPBackendRef) GetBackendTarget() string { if x != nil { return x.BackendTarget } return "" } -func (x *InterpretedHTTPBackendRef) GetWeight() uint32 { +func (x *ComputedHTTPBackendRef) GetWeight() uint32 { if x != nil { return x.Weight } return 0 } -func (x *InterpretedHTTPBackendRef) GetFilters() []*HTTPRouteFilter { +func (x *ComputedHTTPBackendRef) GetFilters() []*HTTPRouteFilter { if x != nil { return x.Filters } return nil } -type InterpretedGRPCRoute struct { +type ComputedGRPCRoute struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ParentRef *ParentReference `protobuf:"bytes,1,opt,name=parent_ref,json=parentRef,proto3" json:"parent_ref,omitempty"` - Rules []*InterpretedGRPCRouteRule `protobuf:"bytes,3,rep,name=rules,proto3" json:"rules,omitempty"` + ParentRef *ParentReference `protobuf:"bytes,1,opt,name=parent_ref,json=parentRef,proto3" json:"parent_ref,omitempty"` + Rules []*ComputedGRPCRouteRule `protobuf:"bytes,3,rep,name=rules,proto3" json:"rules,omitempty"` } -func (x *InterpretedGRPCRoute) Reset() { - *x = InterpretedGRPCRoute{} +func (x *ComputedGRPCRoute) Reset() { + *x = ComputedGRPCRoute{} if protoimpl.UnsafeEnabled { mi := &file_pbmesh_v1alpha1_computed_routes_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -406,13 +406,13 @@ func (x *InterpretedGRPCRoute) Reset() { } } -func (x *InterpretedGRPCRoute) String() string { +func (x *ComputedGRPCRoute) String() string { return protoimpl.X.MessageStringOf(x) } -func (*InterpretedGRPCRoute) ProtoMessage() {} +func (*ComputedGRPCRoute) ProtoMessage() {} -func (x *InterpretedGRPCRoute) ProtoReflect() protoreflect.Message { +func (x *ComputedGRPCRoute) ProtoReflect() protoreflect.Message { mi := &file_pbmesh_v1alpha1_computed_routes_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -424,39 +424,39 @@ func (x *InterpretedGRPCRoute) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use InterpretedGRPCRoute.ProtoReflect.Descriptor instead. -func (*InterpretedGRPCRoute) Descriptor() ([]byte, []int) { +// Deprecated: Use ComputedGRPCRoute.ProtoReflect.Descriptor instead. +func (*ComputedGRPCRoute) Descriptor() ([]byte, []int) { return file_pbmesh_v1alpha1_computed_routes_proto_rawDescGZIP(), []int{5} } -func (x *InterpretedGRPCRoute) GetParentRef() *ParentReference { +func (x *ComputedGRPCRoute) GetParentRef() *ParentReference { if x != nil { return x.ParentRef } return nil } -func (x *InterpretedGRPCRoute) GetRules() []*InterpretedGRPCRouteRule { +func (x *ComputedGRPCRoute) GetRules() []*ComputedGRPCRouteRule { if x != nil { return x.Rules } return nil } -type InterpretedGRPCRouteRule struct { +type ComputedGRPCRouteRule struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Matches []*GRPCRouteMatch `protobuf:"bytes,1,rep,name=matches,proto3" json:"matches,omitempty"` - Filters []*GRPCRouteFilter `protobuf:"bytes,2,rep,name=filters,proto3" json:"filters,omitempty"` - BackendRefs []*InterpretedGRPCBackendRef `protobuf:"bytes,3,rep,name=backend_refs,json=backendRefs,proto3" json:"backend_refs,omitempty"` - Timeouts *HTTPRouteTimeouts `protobuf:"bytes,4,opt,name=timeouts,proto3" json:"timeouts,omitempty"` - Retries *HTTPRouteRetries `protobuf:"bytes,5,opt,name=retries,proto3" json:"retries,omitempty"` + Matches []*GRPCRouteMatch `protobuf:"bytes,1,rep,name=matches,proto3" json:"matches,omitempty"` + Filters []*GRPCRouteFilter `protobuf:"bytes,2,rep,name=filters,proto3" json:"filters,omitempty"` + BackendRefs []*ComputedGRPCBackendRef `protobuf:"bytes,3,rep,name=backend_refs,json=backendRefs,proto3" json:"backend_refs,omitempty"` + Timeouts *HTTPRouteTimeouts `protobuf:"bytes,4,opt,name=timeouts,proto3" json:"timeouts,omitempty"` + Retries *HTTPRouteRetries `protobuf:"bytes,5,opt,name=retries,proto3" json:"retries,omitempty"` } -func (x *InterpretedGRPCRouteRule) Reset() { - *x = InterpretedGRPCRouteRule{} +func (x *ComputedGRPCRouteRule) Reset() { + *x = ComputedGRPCRouteRule{} if protoimpl.UnsafeEnabled { mi := &file_pbmesh_v1alpha1_computed_routes_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -464,13 +464,13 @@ func (x *InterpretedGRPCRouteRule) Reset() { } } -func (x *InterpretedGRPCRouteRule) String() string { +func (x *ComputedGRPCRouteRule) String() string { return protoimpl.X.MessageStringOf(x) } -func (*InterpretedGRPCRouteRule) ProtoMessage() {} +func (*ComputedGRPCRouteRule) ProtoMessage() {} -func (x *InterpretedGRPCRouteRule) ProtoReflect() protoreflect.Message { +func (x *ComputedGRPCRouteRule) ProtoReflect() protoreflect.Message { mi := &file_pbmesh_v1alpha1_computed_routes_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -482,47 +482,47 @@ func (x *InterpretedGRPCRouteRule) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use InterpretedGRPCRouteRule.ProtoReflect.Descriptor instead. -func (*InterpretedGRPCRouteRule) Descriptor() ([]byte, []int) { +// Deprecated: Use ComputedGRPCRouteRule.ProtoReflect.Descriptor instead. +func (*ComputedGRPCRouteRule) Descriptor() ([]byte, []int) { return file_pbmesh_v1alpha1_computed_routes_proto_rawDescGZIP(), []int{6} } -func (x *InterpretedGRPCRouteRule) GetMatches() []*GRPCRouteMatch { +func (x *ComputedGRPCRouteRule) GetMatches() []*GRPCRouteMatch { if x != nil { return x.Matches } return nil } -func (x *InterpretedGRPCRouteRule) GetFilters() []*GRPCRouteFilter { +func (x *ComputedGRPCRouteRule) GetFilters() []*GRPCRouteFilter { if x != nil { return x.Filters } return nil } -func (x *InterpretedGRPCRouteRule) GetBackendRefs() []*InterpretedGRPCBackendRef { +func (x *ComputedGRPCRouteRule) GetBackendRefs() []*ComputedGRPCBackendRef { if x != nil { return x.BackendRefs } return nil } -func (x *InterpretedGRPCRouteRule) GetTimeouts() *HTTPRouteTimeouts { +func (x *ComputedGRPCRouteRule) GetTimeouts() *HTTPRouteTimeouts { if x != nil { return x.Timeouts } return nil } -func (x *InterpretedGRPCRouteRule) GetRetries() *HTTPRouteRetries { +func (x *ComputedGRPCRouteRule) GetRetries() *HTTPRouteRetries { if x != nil { return x.Retries } return nil } -type InterpretedGRPCBackendRef struct { +type ComputedGRPCBackendRef struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -539,8 +539,8 @@ type InterpretedGRPCBackendRef struct { Filters []*GRPCRouteFilter `protobuf:"bytes,3,rep,name=filters,proto3" json:"filters,omitempty"` } -func (x *InterpretedGRPCBackendRef) Reset() { - *x = InterpretedGRPCBackendRef{} +func (x *ComputedGRPCBackendRef) Reset() { + *x = ComputedGRPCBackendRef{} if protoimpl.UnsafeEnabled { mi := &file_pbmesh_v1alpha1_computed_routes_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -548,13 +548,13 @@ func (x *InterpretedGRPCBackendRef) Reset() { } } -func (x *InterpretedGRPCBackendRef) String() string { +func (x *ComputedGRPCBackendRef) String() string { return protoimpl.X.MessageStringOf(x) } -func (*InterpretedGRPCBackendRef) ProtoMessage() {} +func (*ComputedGRPCBackendRef) ProtoMessage() {} -func (x *InterpretedGRPCBackendRef) ProtoReflect() protoreflect.Message { +func (x *ComputedGRPCBackendRef) ProtoReflect() protoreflect.Message { mi := &file_pbmesh_v1alpha1_computed_routes_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -566,43 +566,43 @@ func (x *InterpretedGRPCBackendRef) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use InterpretedGRPCBackendRef.ProtoReflect.Descriptor instead. -func (*InterpretedGRPCBackendRef) Descriptor() ([]byte, []int) { +// Deprecated: Use ComputedGRPCBackendRef.ProtoReflect.Descriptor instead. +func (*ComputedGRPCBackendRef) Descriptor() ([]byte, []int) { return file_pbmesh_v1alpha1_computed_routes_proto_rawDescGZIP(), []int{7} } -func (x *InterpretedGRPCBackendRef) GetBackendTarget() string { +func (x *ComputedGRPCBackendRef) GetBackendTarget() string { if x != nil { return x.BackendTarget } return "" } -func (x *InterpretedGRPCBackendRef) GetWeight() uint32 { +func (x *ComputedGRPCBackendRef) GetWeight() uint32 { if x != nil { return x.Weight } return 0 } -func (x *InterpretedGRPCBackendRef) GetFilters() []*GRPCRouteFilter { +func (x *ComputedGRPCBackendRef) GetFilters() []*GRPCRouteFilter { if x != nil { return x.Filters } return nil } -type InterpretedTCPRoute struct { +type ComputedTCPRoute struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ParentRef *ParentReference `protobuf:"bytes,1,opt,name=parent_ref,json=parentRef,proto3" json:"parent_ref,omitempty"` - Rules []*InterpretedTCPRouteRule `protobuf:"bytes,2,rep,name=rules,proto3" json:"rules,omitempty"` + ParentRef *ParentReference `protobuf:"bytes,1,opt,name=parent_ref,json=parentRef,proto3" json:"parent_ref,omitempty"` + Rules []*ComputedTCPRouteRule `protobuf:"bytes,2,rep,name=rules,proto3" json:"rules,omitempty"` } -func (x *InterpretedTCPRoute) Reset() { - *x = InterpretedTCPRoute{} +func (x *ComputedTCPRoute) Reset() { + *x = ComputedTCPRoute{} if protoimpl.UnsafeEnabled { mi := &file_pbmesh_v1alpha1_computed_routes_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -610,13 +610,13 @@ func (x *InterpretedTCPRoute) Reset() { } } -func (x *InterpretedTCPRoute) String() string { +func (x *ComputedTCPRoute) String() string { return protoimpl.X.MessageStringOf(x) } -func (*InterpretedTCPRoute) ProtoMessage() {} +func (*ComputedTCPRoute) ProtoMessage() {} -func (x *InterpretedTCPRoute) ProtoReflect() protoreflect.Message { +func (x *ComputedTCPRoute) ProtoReflect() protoreflect.Message { mi := &file_pbmesh_v1alpha1_computed_routes_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -628,35 +628,35 @@ func (x *InterpretedTCPRoute) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use InterpretedTCPRoute.ProtoReflect.Descriptor instead. -func (*InterpretedTCPRoute) Descriptor() ([]byte, []int) { +// Deprecated: Use ComputedTCPRoute.ProtoReflect.Descriptor instead. +func (*ComputedTCPRoute) Descriptor() ([]byte, []int) { return file_pbmesh_v1alpha1_computed_routes_proto_rawDescGZIP(), []int{8} } -func (x *InterpretedTCPRoute) GetParentRef() *ParentReference { +func (x *ComputedTCPRoute) GetParentRef() *ParentReference { if x != nil { return x.ParentRef } return nil } -func (x *InterpretedTCPRoute) GetRules() []*InterpretedTCPRouteRule { +func (x *ComputedTCPRoute) GetRules() []*ComputedTCPRouteRule { if x != nil { return x.Rules } return nil } -type InterpretedTCPRouteRule struct { +type ComputedTCPRouteRule struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - BackendRefs []*InterpretedTCPBackendRef `protobuf:"bytes,1,rep,name=backend_refs,json=backendRefs,proto3" json:"backend_refs,omitempty"` + BackendRefs []*ComputedTCPBackendRef `protobuf:"bytes,1,rep,name=backend_refs,json=backendRefs,proto3" json:"backend_refs,omitempty"` } -func (x *InterpretedTCPRouteRule) Reset() { - *x = InterpretedTCPRouteRule{} +func (x *ComputedTCPRouteRule) Reset() { + *x = ComputedTCPRouteRule{} if protoimpl.UnsafeEnabled { mi := &file_pbmesh_v1alpha1_computed_routes_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -664,13 +664,13 @@ func (x *InterpretedTCPRouteRule) Reset() { } } -func (x *InterpretedTCPRouteRule) String() string { +func (x *ComputedTCPRouteRule) String() string { return protoimpl.X.MessageStringOf(x) } -func (*InterpretedTCPRouteRule) ProtoMessage() {} +func (*ComputedTCPRouteRule) ProtoMessage() {} -func (x *InterpretedTCPRouteRule) ProtoReflect() protoreflect.Message { +func (x *ComputedTCPRouteRule) ProtoReflect() protoreflect.Message { mi := &file_pbmesh_v1alpha1_computed_routes_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -682,12 +682,12 @@ func (x *InterpretedTCPRouteRule) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use InterpretedTCPRouteRule.ProtoReflect.Descriptor instead. -func (*InterpretedTCPRouteRule) Descriptor() ([]byte, []int) { +// Deprecated: Use ComputedTCPRouteRule.ProtoReflect.Descriptor instead. +func (*ComputedTCPRouteRule) Descriptor() ([]byte, []int) { return file_pbmesh_v1alpha1_computed_routes_proto_rawDescGZIP(), []int{9} } -func (x *InterpretedTCPRouteRule) GetBackendRefs() []*InterpretedTCPBackendRef { +func (x *ComputedTCPRouteRule) GetBackendRefs() []*ComputedTCPBackendRef { if x != nil { return x.BackendRefs } @@ -696,7 +696,7 @@ func (x *InterpretedTCPRouteRule) GetBackendRefs() []*InterpretedTCPBackendRef { // TODO: look into smuggling the target through a different typeURL, or just // skip in favor of letting the caller do their own lookups? -type InterpretedTCPBackendRef struct { +type ComputedTCPBackendRef struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -712,8 +712,8 @@ type InterpretedTCPBackendRef struct { Weight uint32 `protobuf:"varint,2,opt,name=weight,proto3" json:"weight,omitempty"` } -func (x *InterpretedTCPBackendRef) Reset() { - *x = InterpretedTCPBackendRef{} +func (x *ComputedTCPBackendRef) Reset() { + *x = ComputedTCPBackendRef{} if protoimpl.UnsafeEnabled { mi := &file_pbmesh_v1alpha1_computed_routes_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -721,13 +721,13 @@ func (x *InterpretedTCPBackendRef) Reset() { } } -func (x *InterpretedTCPBackendRef) String() string { +func (x *ComputedTCPBackendRef) String() string { return protoimpl.X.MessageStringOf(x) } -func (*InterpretedTCPBackendRef) ProtoMessage() {} +func (*ComputedTCPBackendRef) ProtoMessage() {} -func (x *InterpretedTCPBackendRef) ProtoReflect() protoreflect.Message { +func (x *ComputedTCPBackendRef) ProtoReflect() protoreflect.Message { mi := &file_pbmesh_v1alpha1_computed_routes_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -739,19 +739,19 @@ func (x *InterpretedTCPBackendRef) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use InterpretedTCPBackendRef.ProtoReflect.Descriptor instead. -func (*InterpretedTCPBackendRef) Descriptor() ([]byte, []int) { +// Deprecated: Use ComputedTCPBackendRef.ProtoReflect.Descriptor instead. +func (*ComputedTCPBackendRef) Descriptor() ([]byte, []int) { return file_pbmesh_v1alpha1_computed_routes_proto_rawDescGZIP(), []int{10} } -func (x *InterpretedTCPBackendRef) GetBackendTarget() string { +func (x *ComputedTCPBackendRef) GetBackendTarget() string { if x != nil { return x.BackendTarget } return "" } -func (x *InterpretedTCPBackendRef) GetWeight() uint32 { +func (x *ComputedTCPBackendRef) GetWeight() uint32 { if x != nil { return x.Weight } @@ -871,203 +871,200 @@ var file_pbmesh_v1alpha1_computed_routes_proto_rawDesc = []byte{ 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xfe, 0x03, 0x0a, 0x12, 0x43, 0x6f, 0x6d, 0x70, - 0x75, 0x74, 0x65, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x4a, - 0x0a, 0x04, 0x68, 0x74, 0x74, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x68, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xf5, 0x03, 0x0a, 0x12, 0x43, 0x6f, 0x6d, 0x70, + 0x75, 0x74, 0x65, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x47, + 0x0a, 0x04, 0x68, 0x74, 0x74, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, - 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x48, 0x54, 0x54, 0x50, 0x52, 0x6f, 0x75, - 0x74, 0x65, 0x48, 0x00, 0x52, 0x04, 0x68, 0x74, 0x74, 0x70, 0x12, 0x4a, 0x0a, 0x04, 0x67, 0x72, - 0x70, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, - 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, - 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x70, - 0x72, 0x65, 0x74, 0x65, 0x64, 0x47, 0x52, 0x50, 0x43, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x00, - 0x52, 0x04, 0x67, 0x72, 0x70, 0x63, 0x12, 0x47, 0x0a, 0x03, 0x74, 0x63, 0x70, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, - 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, - 0x54, 0x43, 0x50, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x00, 0x52, 0x03, 0x74, 0x63, 0x70, 0x12, - 0x30, 0x0a, 0x14, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, - 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x75, - 0x73, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x12, 0x59, 0x0a, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, - 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x50, 0x6f, 0x72, 0x74, - 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x1a, 0x70, 0x0a, 0x0c, - 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, - 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x4a, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, - 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, - 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, - 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x08, - 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xbc, 0x01, 0x0a, 0x14, 0x49, 0x6e, 0x74, - 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x48, 0x54, 0x54, 0x50, 0x52, 0x6f, 0x75, 0x74, - 0x65, 0x12, 0x4e, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x66, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, + 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x6f, + 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x48, 0x54, 0x54, 0x50, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x48, + 0x00, 0x52, 0x04, 0x68, 0x74, 0x74, 0x70, 0x12, 0x47, 0x0a, 0x04, 0x67, 0x72, 0x70, 0x63, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x66, - 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x65, - 0x66, 0x12, 0x4e, 0x0a, 0x05, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x38, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, - 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, - 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x48, 0x54, 0x54, - 0x50, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x05, 0x72, 0x75, 0x6c, 0x65, - 0x73, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x22, 0xa8, 0x03, 0x0a, 0x18, 0x49, 0x6e, 0x74, 0x65, - 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x48, 0x54, 0x54, 0x50, 0x52, 0x6f, 0x75, 0x74, 0x65, - 0x52, 0x75, 0x6c, 0x65, 0x12, 0x48, 0x0a, 0x07, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, - 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x52, 0x6f, 0x75, 0x74, 0x65, - 0x4d, 0x61, 0x74, 0x63, 0x68, 0x52, 0x07, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x49, - 0x0a, 0x07, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x2f, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, - 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, - 0x2e, 0x48, 0x54, 0x54, 0x50, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, - 0x52, 0x07, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x12, 0x5c, 0x0a, 0x0c, 0x62, 0x61, 0x63, - 0x6b, 0x65, 0x6e, 0x64, 0x5f, 0x72, 0x65, 0x66, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x39, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, - 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, - 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x48, 0x54, 0x54, 0x50, - 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x66, 0x52, 0x0b, 0x62, 0x61, 0x63, 0x6b, - 0x65, 0x6e, 0x64, 0x52, 0x65, 0x66, 0x73, 0x12, 0x4d, 0x0a, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x6f, - 0x75, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x68, 0x61, 0x73, 0x68, + 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x47, + 0x52, 0x50, 0x43, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x00, 0x52, 0x04, 0x67, 0x72, 0x70, 0x63, + 0x12, 0x44, 0x0a, 0x03, 0x74, 0x63, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, + 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, + 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, + 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x54, 0x43, 0x50, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x48, + 0x00, 0x52, 0x03, 0x74, 0x63, 0x70, 0x12, 0x30, 0x0a, 0x14, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x5f, + 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x66, 0x61, 0x75, + 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x59, 0x0a, 0x07, 0x74, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x68, 0x61, 0x73, 0x68, + 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, + 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x75, + 0x74, 0x65, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x54, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x74, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x73, 0x1a, 0x70, 0x0a, 0x0c, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x4a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, + 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x54, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, + 0xb6, 0x01, 0x0a, 0x11, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x48, 0x54, 0x54, 0x50, + 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x4e, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, + 0x72, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, - 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x52, - 0x6f, 0x75, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x73, 0x52, 0x08, 0x74, 0x69, - 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x73, 0x12, 0x4a, 0x0a, 0x07, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, - 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, + 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x61, 0x72, 0x65, 0x6e, + 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x09, 0x70, 0x61, 0x72, 0x65, + 0x6e, 0x74, 0x52, 0x65, 0x66, 0x12, 0x4b, 0x0a, 0x05, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, + 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x48, 0x54, + 0x54, 0x50, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x05, 0x72, 0x75, 0x6c, + 0x65, 0x73, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x22, 0xa2, 0x03, 0x0a, 0x15, 0x43, 0x6f, 0x6d, + 0x70, 0x75, 0x74, 0x65, 0x64, 0x48, 0x54, 0x54, 0x50, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x75, + 0x6c, 0x65, 0x12, 0x48, 0x0a, 0x07, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, + 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x4d, 0x61, + 0x74, 0x63, 0x68, 0x52, 0x07, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x49, 0x0a, 0x07, + 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, + 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, + 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x48, + 0x54, 0x54, 0x50, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x07, + 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x12, 0x59, 0x0a, 0x0c, 0x62, 0x61, 0x63, 0x6b, 0x65, + 0x6e, 0x64, 0x5f, 0x72, 0x65, 0x66, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, + 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, + 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, + 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x48, 0x54, 0x54, 0x50, 0x42, 0x61, 0x63, 0x6b, 0x65, + 0x6e, 0x64, 0x52, 0x65, 0x66, 0x52, 0x0b, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x65, + 0x66, 0x73, 0x12, 0x4d, 0x0a, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x73, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, + 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, + 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x73, 0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x73, 0x12, 0x4a, 0x0a, 0x07, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, + 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, + 0x68, 0x61, 0x31, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x74, + 0x72, 0x69, 0x65, 0x73, 0x52, 0x07, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0xa2, 0x01, + 0x0a, 0x16, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x48, 0x54, 0x54, 0x50, 0x42, 0x61, + 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x66, 0x12, 0x25, 0x0a, 0x0e, 0x62, 0x61, 0x63, 0x6b, + 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0d, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, + 0x16, 0x0a, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x49, 0x0a, 0x07, 0x66, 0x69, 0x6c, 0x74, 0x65, + 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, + 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x52, 0x6f, + 0x75, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x07, 0x66, 0x69, 0x6c, 0x74, 0x65, + 0x72, 0x73, 0x22, 0xb6, 0x01, 0x0a, 0x11, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x47, + 0x52, 0x50, 0x43, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x4e, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x65, + 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x68, + 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, + 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x61, + 0x72, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x09, 0x70, + 0x61, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x66, 0x12, 0x4b, 0x0a, 0x05, 0x72, 0x75, 0x6c, 0x65, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x52, 0x6f, 0x75, - 0x74, 0x65, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x52, 0x07, 0x72, 0x65, 0x74, 0x72, 0x69, - 0x65, 0x73, 0x22, 0xa5, 0x01, 0x0a, 0x19, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, - 0x65, 0x64, 0x48, 0x54, 0x54, 0x50, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x66, - 0x12, 0x25, 0x0a, 0x0e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, - 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, - 0x49, 0x0a, 0x07, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, + 0x64, 0x47, 0x52, 0x50, 0x43, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x05, + 0x72, 0x75, 0x6c, 0x65, 0x73, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x22, 0xa2, 0x03, 0x0a, 0x15, + 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x47, 0x52, 0x50, 0x43, 0x52, 0x6f, 0x75, 0x74, + 0x65, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x48, 0x0a, 0x07, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, + 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x52, 0x50, 0x43, 0x52, 0x6f, 0x75, 0x74, + 0x65, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x52, 0x07, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, + 0x49, 0x0a, 0x07, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, - 0x31, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, - 0x72, 0x52, 0x07, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x22, 0xbc, 0x01, 0x0a, 0x14, 0x49, - 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x47, 0x52, 0x50, 0x43, 0x52, 0x6f, - 0x75, 0x74, 0x65, 0x12, 0x4e, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x65, - 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, - 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x52, - 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, - 0x52, 0x65, 0x66, 0x12, 0x4e, 0x0a, 0x05, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, - 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x47, - 0x52, 0x50, 0x43, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x05, 0x72, 0x75, - 0x6c, 0x65, 0x73, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x22, 0xa8, 0x03, 0x0a, 0x18, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x47, 0x52, 0x50, 0x43, 0x52, 0x6f, 0x75, - 0x74, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x48, 0x0a, 0x07, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, + 0x31, 0x2e, 0x47, 0x52, 0x50, 0x43, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, + 0x72, 0x52, 0x07, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x12, 0x59, 0x0a, 0x0c, 0x62, 0x61, + 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x5f, 0x72, 0x65, 0x66, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x36, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, + 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x47, 0x52, 0x50, 0x43, 0x42, 0x61, + 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x66, 0x52, 0x0b, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, + 0x64, 0x52, 0x65, 0x66, 0x73, 0x12, 0x4d, 0x0a, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x52, 0x50, 0x43, 0x52, 0x6f, 0x75, - 0x74, 0x65, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x52, 0x07, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, - 0x12, 0x49, 0x0a, 0x07, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x2f, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, - 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x2e, 0x47, 0x52, 0x50, 0x43, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x74, - 0x65, 0x72, 0x52, 0x07, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x12, 0x5c, 0x0a, 0x0c, 0x62, - 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x5f, 0x72, 0x65, 0x66, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x39, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, - 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x47, 0x52, - 0x50, 0x43, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x66, 0x52, 0x0b, 0x62, 0x61, - 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x66, 0x73, 0x12, 0x4d, 0x0a, 0x08, 0x74, 0x69, 0x6d, - 0x65, 0x6f, 0x75, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x68, 0x61, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x52, 0x6f, 0x75, + 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x73, 0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, + 0x6f, 0x75, 0x74, 0x73, 0x12, 0x4a, 0x0a, 0x07, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, + 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, + 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x52, 0x6f, 0x75, 0x74, 0x65, + 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x52, 0x07, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, + 0x22, 0xa2, 0x01, 0x0a, 0x16, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x47, 0x52, 0x50, + 0x43, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x66, 0x12, 0x25, 0x0a, 0x0e, 0x62, + 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0d, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x54, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x49, 0x0a, 0x07, 0x66, 0x69, + 0x6c, 0x74, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, - 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x48, 0x54, 0x54, - 0x50, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x73, 0x52, 0x08, - 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x73, 0x12, 0x4a, 0x0a, 0x07, 0x72, 0x65, 0x74, 0x72, - 0x69, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x68, 0x61, 0x73, 0x68, + 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x52, 0x50, + 0x43, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x07, 0x66, 0x69, + 0x6c, 0x74, 0x65, 0x72, 0x73, 0x22, 0xae, 0x01, 0x0a, 0x10, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, + 0x65, 0x64, 0x54, 0x43, 0x50, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x4e, 0x0a, 0x0a, 0x70, 0x61, + 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, + 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, + 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, + 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, + 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x66, 0x12, 0x4a, 0x0a, 0x05, 0x72, 0x75, + 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, - 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x52, - 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x52, 0x07, 0x72, 0x65, 0x74, - 0x72, 0x69, 0x65, 0x73, 0x22, 0xa5, 0x01, 0x0a, 0x19, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, - 0x65, 0x74, 0x65, 0x64, 0x47, 0x52, 0x50, 0x43, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, - 0x65, 0x66, 0x12, 0x25, 0x0a, 0x0e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x61, - 0x72, 0x67, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x62, 0x61, 0x63, 0x6b, - 0x65, 0x6e, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x77, 0x65, 0x69, - 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, - 0x74, 0x12, 0x49, 0x0a, 0x07, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, - 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x31, 0x2e, 0x47, 0x52, 0x50, 0x43, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x69, 0x6c, - 0x74, 0x65, 0x72, 0x52, 0x07, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x22, 0xb4, 0x01, 0x0a, - 0x13, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x54, 0x43, 0x50, 0x52, - 0x6f, 0x75, 0x74, 0x65, 0x12, 0x4e, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x72, - 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, - 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, - 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, - 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, - 0x74, 0x52, 0x65, 0x66, 0x12, 0x4d, 0x0a, 0x05, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, - 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, - 0x54, 0x43, 0x50, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x05, 0x72, 0x75, - 0x6c, 0x65, 0x73, 0x22, 0x76, 0x0a, 0x17, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, - 0x65, 0x64, 0x54, 0x43, 0x50, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x5b, + 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x75, + 0x74, 0x65, 0x64, 0x54, 0x43, 0x50, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x52, + 0x05, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x22, 0x70, 0x0a, 0x14, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, + 0x65, 0x64, 0x54, 0x43, 0x50, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x58, 0x0a, 0x0c, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x5f, 0x72, 0x65, 0x66, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, - 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, - 0x64, 0x54, 0x43, 0x50, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x66, 0x52, 0x0b, - 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x66, 0x73, 0x22, 0x59, 0x0a, 0x18, 0x49, - 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x54, 0x43, 0x50, 0x42, 0x61, 0x63, - 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x66, 0x12, 0x25, 0x0a, 0x0e, 0x62, 0x61, 0x63, 0x6b, 0x65, - 0x6e, 0x64, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0d, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x16, - 0x0a, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, - 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0xed, 0x02, 0x0a, 0x14, 0x42, 0x61, 0x63, 0x6b, 0x65, - 0x6e, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, - 0x51, 0x0a, 0x0b, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x66, - 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x0a, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, - 0x65, 0x66, 0x12, 0x44, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, - 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, - 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5a, 0x0a, 0x0f, 0x66, 0x61, 0x69, 0x6c, - 0x6f, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x31, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, - 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x6f, 0x76, 0x65, 0x72, 0x50, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0e, 0x66, 0x61, 0x69, 0x6c, 0x6f, 0x76, 0x65, 0x72, 0x50, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x12, 0x60, 0x0a, 0x12, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x31, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, - 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, - 0x31, 0x2e, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x52, 0x11, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x42, 0x9b, 0x02, 0x0a, 0x22, 0x63, 0x6f, 0x6d, 0x2e, 0x68, - 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, - 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x42, 0x13, 0x43, - 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x50, 0x72, 0x6f, - 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x45, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x75, - 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2d, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2f, 0x70, - 0x62, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x3b, 0x6d, - 0x65, 0x73, 0x68, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0xa2, 0x02, 0x03, 0x48, 0x43, - 0x4d, 0xaa, 0x02, 0x1e, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x43, 0x6f, - 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x4d, 0x65, 0x73, 0x68, 0x2e, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0xca, 0x02, 0x1e, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, - 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x4d, 0x65, 0x73, 0x68, 0x5c, 0x56, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x31, 0xe2, 0x02, 0x2a, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, - 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x4d, 0x65, 0x73, 0x68, 0x5c, 0x56, 0x31, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0xea, 0x02, 0x21, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x3a, 0x3a, 0x43, 0x6f, - 0x6e, 0x73, 0x75, 0x6c, 0x3a, 0x3a, 0x4d, 0x65, 0x73, 0x68, 0x3a, 0x3a, 0x56, 0x31, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x54, 0x43, + 0x50, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x66, 0x52, 0x0b, 0x62, 0x61, 0x63, + 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x66, 0x73, 0x22, 0x56, 0x0a, 0x15, 0x43, 0x6f, 0x6d, 0x70, + 0x75, 0x74, 0x65, 0x64, 0x54, 0x43, 0x50, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x65, + 0x66, 0x12, 0x25, 0x0a, 0x0e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x62, 0x61, 0x63, 0x6b, 0x65, + 0x6e, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x77, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x22, 0xed, 0x02, 0x0a, 0x14, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x54, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x51, 0x0a, 0x0b, 0x62, 0x61, 0x63, + 0x6b, 0x65, 0x6e, 0x64, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, + 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, + 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, + 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, + 0x52, 0x0a, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x66, 0x12, 0x44, 0x0a, 0x07, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, + 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, + 0x2e, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x12, 0x5a, 0x0a, 0x0f, 0x66, 0x61, 0x69, 0x6c, 0x6f, 0x76, 0x65, 0x72, 0x5f, 0x70, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x68, 0x61, + 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x63, + 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, + 0x46, 0x61, 0x69, 0x6c, 0x6f, 0x76, 0x65, 0x72, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0e, + 0x66, 0x61, 0x69, 0x6c, 0x6f, 0x76, 0x65, 0x72, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x60, + 0x0a, 0x12, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x68, 0x61, 0x73, + 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, + 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x73, 0x74, + 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x11, 0x64, + 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x42, 0x9b, 0x02, 0x0a, 0x22, 0x63, 0x6f, 0x6d, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, + 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x42, 0x13, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, + 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x45, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, + 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2d, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2f, 0x70, 0x62, 0x6d, 0x65, 0x73, 0x68, 0x2f, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x3b, 0x6d, 0x65, 0x73, 0x68, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0xa2, 0x02, 0x03, 0x48, 0x43, 0x4d, 0xaa, 0x02, 0x1e, 0x48, 0x61, + 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x4d, + 0x65, 0x73, 0x68, 0x2e, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0xca, 0x02, 0x1e, 0x48, + 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, + 0x4d, 0x65, 0x73, 0x68, 0x5c, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0xe2, 0x02, 0x2a, + 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, + 0x5c, 0x4d, 0x65, 0x73, 0x68, 0x5c, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x5c, 0x47, + 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x21, 0x48, 0x61, 0x73, + 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x3a, 0x3a, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x3a, 0x3a, + 0x4d, 0x65, 0x73, 0x68, 0x3a, 0x3a, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1084,57 +1081,57 @@ func file_pbmesh_v1alpha1_computed_routes_proto_rawDescGZIP() []byte { var file_pbmesh_v1alpha1_computed_routes_proto_msgTypes = make([]protoimpl.MessageInfo, 14) var file_pbmesh_v1alpha1_computed_routes_proto_goTypes = []interface{}{ - (*ComputedRoutes)(nil), // 0: hashicorp.consul.mesh.v1alpha1.ComputedRoutes - (*ComputedPortRoutes)(nil), // 1: hashicorp.consul.mesh.v1alpha1.ComputedPortRoutes - (*InterpretedHTTPRoute)(nil), // 2: hashicorp.consul.mesh.v1alpha1.InterpretedHTTPRoute - (*InterpretedHTTPRouteRule)(nil), // 3: hashicorp.consul.mesh.v1alpha1.InterpretedHTTPRouteRule - (*InterpretedHTTPBackendRef)(nil), // 4: hashicorp.consul.mesh.v1alpha1.InterpretedHTTPBackendRef - (*InterpretedGRPCRoute)(nil), // 5: hashicorp.consul.mesh.v1alpha1.InterpretedGRPCRoute - (*InterpretedGRPCRouteRule)(nil), // 6: hashicorp.consul.mesh.v1alpha1.InterpretedGRPCRouteRule - (*InterpretedGRPCBackendRef)(nil), // 7: hashicorp.consul.mesh.v1alpha1.InterpretedGRPCBackendRef - (*InterpretedTCPRoute)(nil), // 8: hashicorp.consul.mesh.v1alpha1.InterpretedTCPRoute - (*InterpretedTCPRouteRule)(nil), // 9: hashicorp.consul.mesh.v1alpha1.InterpretedTCPRouteRule - (*InterpretedTCPBackendRef)(nil), // 10: hashicorp.consul.mesh.v1alpha1.InterpretedTCPBackendRef - (*BackendTargetDetails)(nil), // 11: hashicorp.consul.mesh.v1alpha1.BackendTargetDetails - nil, // 12: hashicorp.consul.mesh.v1alpha1.ComputedRoutes.PortedConfigsEntry - nil, // 13: hashicorp.consul.mesh.v1alpha1.ComputedPortRoutes.TargetsEntry - (*ParentReference)(nil), // 14: hashicorp.consul.mesh.v1alpha1.ParentReference - (*HTTPRouteMatch)(nil), // 15: hashicorp.consul.mesh.v1alpha1.HTTPRouteMatch - (*HTTPRouteFilter)(nil), // 16: hashicorp.consul.mesh.v1alpha1.HTTPRouteFilter - (*HTTPRouteTimeouts)(nil), // 17: hashicorp.consul.mesh.v1alpha1.HTTPRouteTimeouts - (*HTTPRouteRetries)(nil), // 18: hashicorp.consul.mesh.v1alpha1.HTTPRouteRetries - (*GRPCRouteMatch)(nil), // 19: hashicorp.consul.mesh.v1alpha1.GRPCRouteMatch - (*GRPCRouteFilter)(nil), // 20: hashicorp.consul.mesh.v1alpha1.GRPCRouteFilter - (*BackendReference)(nil), // 21: hashicorp.consul.mesh.v1alpha1.BackendReference - (*v1alpha1.Service)(nil), // 22: hashicorp.consul.catalog.v1alpha1.Service - (*v1alpha1.FailoverPolicy)(nil), // 23: hashicorp.consul.catalog.v1alpha1.FailoverPolicy - (*DestinationPolicy)(nil), // 24: hashicorp.consul.mesh.v1alpha1.DestinationPolicy + (*ComputedRoutes)(nil), // 0: hashicorp.consul.mesh.v1alpha1.ComputedRoutes + (*ComputedPortRoutes)(nil), // 1: hashicorp.consul.mesh.v1alpha1.ComputedPortRoutes + (*ComputedHTTPRoute)(nil), // 2: hashicorp.consul.mesh.v1alpha1.ComputedHTTPRoute + (*ComputedHTTPRouteRule)(nil), // 3: hashicorp.consul.mesh.v1alpha1.ComputedHTTPRouteRule + (*ComputedHTTPBackendRef)(nil), // 4: hashicorp.consul.mesh.v1alpha1.ComputedHTTPBackendRef + (*ComputedGRPCRoute)(nil), // 5: hashicorp.consul.mesh.v1alpha1.ComputedGRPCRoute + (*ComputedGRPCRouteRule)(nil), // 6: hashicorp.consul.mesh.v1alpha1.ComputedGRPCRouteRule + (*ComputedGRPCBackendRef)(nil), // 7: hashicorp.consul.mesh.v1alpha1.ComputedGRPCBackendRef + (*ComputedTCPRoute)(nil), // 8: hashicorp.consul.mesh.v1alpha1.ComputedTCPRoute + (*ComputedTCPRouteRule)(nil), // 9: hashicorp.consul.mesh.v1alpha1.ComputedTCPRouteRule + (*ComputedTCPBackendRef)(nil), // 10: hashicorp.consul.mesh.v1alpha1.ComputedTCPBackendRef + (*BackendTargetDetails)(nil), // 11: hashicorp.consul.mesh.v1alpha1.BackendTargetDetails + nil, // 12: hashicorp.consul.mesh.v1alpha1.ComputedRoutes.PortedConfigsEntry + nil, // 13: hashicorp.consul.mesh.v1alpha1.ComputedPortRoutes.TargetsEntry + (*ParentReference)(nil), // 14: hashicorp.consul.mesh.v1alpha1.ParentReference + (*HTTPRouteMatch)(nil), // 15: hashicorp.consul.mesh.v1alpha1.HTTPRouteMatch + (*HTTPRouteFilter)(nil), // 16: hashicorp.consul.mesh.v1alpha1.HTTPRouteFilter + (*HTTPRouteTimeouts)(nil), // 17: hashicorp.consul.mesh.v1alpha1.HTTPRouteTimeouts + (*HTTPRouteRetries)(nil), // 18: hashicorp.consul.mesh.v1alpha1.HTTPRouteRetries + (*GRPCRouteMatch)(nil), // 19: hashicorp.consul.mesh.v1alpha1.GRPCRouteMatch + (*GRPCRouteFilter)(nil), // 20: hashicorp.consul.mesh.v1alpha1.GRPCRouteFilter + (*BackendReference)(nil), // 21: hashicorp.consul.mesh.v1alpha1.BackendReference + (*v1alpha1.Service)(nil), // 22: hashicorp.consul.catalog.v1alpha1.Service + (*v1alpha1.FailoverPolicy)(nil), // 23: hashicorp.consul.catalog.v1alpha1.FailoverPolicy + (*DestinationPolicy)(nil), // 24: hashicorp.consul.mesh.v1alpha1.DestinationPolicy } var file_pbmesh_v1alpha1_computed_routes_proto_depIdxs = []int32{ 12, // 0: hashicorp.consul.mesh.v1alpha1.ComputedRoutes.ported_configs:type_name -> hashicorp.consul.mesh.v1alpha1.ComputedRoutes.PortedConfigsEntry - 2, // 1: hashicorp.consul.mesh.v1alpha1.ComputedPortRoutes.http:type_name -> hashicorp.consul.mesh.v1alpha1.InterpretedHTTPRoute - 5, // 2: hashicorp.consul.mesh.v1alpha1.ComputedPortRoutes.grpc:type_name -> hashicorp.consul.mesh.v1alpha1.InterpretedGRPCRoute - 8, // 3: hashicorp.consul.mesh.v1alpha1.ComputedPortRoutes.tcp:type_name -> hashicorp.consul.mesh.v1alpha1.InterpretedTCPRoute + 2, // 1: hashicorp.consul.mesh.v1alpha1.ComputedPortRoutes.http:type_name -> hashicorp.consul.mesh.v1alpha1.ComputedHTTPRoute + 5, // 2: hashicorp.consul.mesh.v1alpha1.ComputedPortRoutes.grpc:type_name -> hashicorp.consul.mesh.v1alpha1.ComputedGRPCRoute + 8, // 3: hashicorp.consul.mesh.v1alpha1.ComputedPortRoutes.tcp:type_name -> hashicorp.consul.mesh.v1alpha1.ComputedTCPRoute 13, // 4: hashicorp.consul.mesh.v1alpha1.ComputedPortRoutes.targets:type_name -> hashicorp.consul.mesh.v1alpha1.ComputedPortRoutes.TargetsEntry - 14, // 5: hashicorp.consul.mesh.v1alpha1.InterpretedHTTPRoute.parent_ref:type_name -> hashicorp.consul.mesh.v1alpha1.ParentReference - 3, // 6: hashicorp.consul.mesh.v1alpha1.InterpretedHTTPRoute.rules:type_name -> hashicorp.consul.mesh.v1alpha1.InterpretedHTTPRouteRule - 15, // 7: hashicorp.consul.mesh.v1alpha1.InterpretedHTTPRouteRule.matches:type_name -> hashicorp.consul.mesh.v1alpha1.HTTPRouteMatch - 16, // 8: hashicorp.consul.mesh.v1alpha1.InterpretedHTTPRouteRule.filters:type_name -> hashicorp.consul.mesh.v1alpha1.HTTPRouteFilter - 4, // 9: hashicorp.consul.mesh.v1alpha1.InterpretedHTTPRouteRule.backend_refs:type_name -> hashicorp.consul.mesh.v1alpha1.InterpretedHTTPBackendRef - 17, // 10: hashicorp.consul.mesh.v1alpha1.InterpretedHTTPRouteRule.timeouts:type_name -> hashicorp.consul.mesh.v1alpha1.HTTPRouteTimeouts - 18, // 11: hashicorp.consul.mesh.v1alpha1.InterpretedHTTPRouteRule.retries:type_name -> hashicorp.consul.mesh.v1alpha1.HTTPRouteRetries - 16, // 12: hashicorp.consul.mesh.v1alpha1.InterpretedHTTPBackendRef.filters:type_name -> hashicorp.consul.mesh.v1alpha1.HTTPRouteFilter - 14, // 13: hashicorp.consul.mesh.v1alpha1.InterpretedGRPCRoute.parent_ref:type_name -> hashicorp.consul.mesh.v1alpha1.ParentReference - 6, // 14: hashicorp.consul.mesh.v1alpha1.InterpretedGRPCRoute.rules:type_name -> hashicorp.consul.mesh.v1alpha1.InterpretedGRPCRouteRule - 19, // 15: hashicorp.consul.mesh.v1alpha1.InterpretedGRPCRouteRule.matches:type_name -> hashicorp.consul.mesh.v1alpha1.GRPCRouteMatch - 20, // 16: hashicorp.consul.mesh.v1alpha1.InterpretedGRPCRouteRule.filters:type_name -> hashicorp.consul.mesh.v1alpha1.GRPCRouteFilter - 7, // 17: hashicorp.consul.mesh.v1alpha1.InterpretedGRPCRouteRule.backend_refs:type_name -> hashicorp.consul.mesh.v1alpha1.InterpretedGRPCBackendRef - 17, // 18: hashicorp.consul.mesh.v1alpha1.InterpretedGRPCRouteRule.timeouts:type_name -> hashicorp.consul.mesh.v1alpha1.HTTPRouteTimeouts - 18, // 19: hashicorp.consul.mesh.v1alpha1.InterpretedGRPCRouteRule.retries:type_name -> hashicorp.consul.mesh.v1alpha1.HTTPRouteRetries - 20, // 20: hashicorp.consul.mesh.v1alpha1.InterpretedGRPCBackendRef.filters:type_name -> hashicorp.consul.mesh.v1alpha1.GRPCRouteFilter - 14, // 21: hashicorp.consul.mesh.v1alpha1.InterpretedTCPRoute.parent_ref:type_name -> hashicorp.consul.mesh.v1alpha1.ParentReference - 9, // 22: hashicorp.consul.mesh.v1alpha1.InterpretedTCPRoute.rules:type_name -> hashicorp.consul.mesh.v1alpha1.InterpretedTCPRouteRule - 10, // 23: hashicorp.consul.mesh.v1alpha1.InterpretedTCPRouteRule.backend_refs:type_name -> hashicorp.consul.mesh.v1alpha1.InterpretedTCPBackendRef + 14, // 5: hashicorp.consul.mesh.v1alpha1.ComputedHTTPRoute.parent_ref:type_name -> hashicorp.consul.mesh.v1alpha1.ParentReference + 3, // 6: hashicorp.consul.mesh.v1alpha1.ComputedHTTPRoute.rules:type_name -> hashicorp.consul.mesh.v1alpha1.ComputedHTTPRouteRule + 15, // 7: hashicorp.consul.mesh.v1alpha1.ComputedHTTPRouteRule.matches:type_name -> hashicorp.consul.mesh.v1alpha1.HTTPRouteMatch + 16, // 8: hashicorp.consul.mesh.v1alpha1.ComputedHTTPRouteRule.filters:type_name -> hashicorp.consul.mesh.v1alpha1.HTTPRouteFilter + 4, // 9: hashicorp.consul.mesh.v1alpha1.ComputedHTTPRouteRule.backend_refs:type_name -> hashicorp.consul.mesh.v1alpha1.ComputedHTTPBackendRef + 17, // 10: hashicorp.consul.mesh.v1alpha1.ComputedHTTPRouteRule.timeouts:type_name -> hashicorp.consul.mesh.v1alpha1.HTTPRouteTimeouts + 18, // 11: hashicorp.consul.mesh.v1alpha1.ComputedHTTPRouteRule.retries:type_name -> hashicorp.consul.mesh.v1alpha1.HTTPRouteRetries + 16, // 12: hashicorp.consul.mesh.v1alpha1.ComputedHTTPBackendRef.filters:type_name -> hashicorp.consul.mesh.v1alpha1.HTTPRouteFilter + 14, // 13: hashicorp.consul.mesh.v1alpha1.ComputedGRPCRoute.parent_ref:type_name -> hashicorp.consul.mesh.v1alpha1.ParentReference + 6, // 14: hashicorp.consul.mesh.v1alpha1.ComputedGRPCRoute.rules:type_name -> hashicorp.consul.mesh.v1alpha1.ComputedGRPCRouteRule + 19, // 15: hashicorp.consul.mesh.v1alpha1.ComputedGRPCRouteRule.matches:type_name -> hashicorp.consul.mesh.v1alpha1.GRPCRouteMatch + 20, // 16: hashicorp.consul.mesh.v1alpha1.ComputedGRPCRouteRule.filters:type_name -> hashicorp.consul.mesh.v1alpha1.GRPCRouteFilter + 7, // 17: hashicorp.consul.mesh.v1alpha1.ComputedGRPCRouteRule.backend_refs:type_name -> hashicorp.consul.mesh.v1alpha1.ComputedGRPCBackendRef + 17, // 18: hashicorp.consul.mesh.v1alpha1.ComputedGRPCRouteRule.timeouts:type_name -> hashicorp.consul.mesh.v1alpha1.HTTPRouteTimeouts + 18, // 19: hashicorp.consul.mesh.v1alpha1.ComputedGRPCRouteRule.retries:type_name -> hashicorp.consul.mesh.v1alpha1.HTTPRouteRetries + 20, // 20: hashicorp.consul.mesh.v1alpha1.ComputedGRPCBackendRef.filters:type_name -> hashicorp.consul.mesh.v1alpha1.GRPCRouteFilter + 14, // 21: hashicorp.consul.mesh.v1alpha1.ComputedTCPRoute.parent_ref:type_name -> hashicorp.consul.mesh.v1alpha1.ParentReference + 9, // 22: hashicorp.consul.mesh.v1alpha1.ComputedTCPRoute.rules:type_name -> hashicorp.consul.mesh.v1alpha1.ComputedTCPRouteRule + 10, // 23: hashicorp.consul.mesh.v1alpha1.ComputedTCPRouteRule.backend_refs:type_name -> hashicorp.consul.mesh.v1alpha1.ComputedTCPBackendRef 21, // 24: hashicorp.consul.mesh.v1alpha1.BackendTargetDetails.backend_ref:type_name -> hashicorp.consul.mesh.v1alpha1.BackendReference 22, // 25: hashicorp.consul.mesh.v1alpha1.BackendTargetDetails.service:type_name -> hashicorp.consul.catalog.v1alpha1.Service 23, // 26: hashicorp.consul.mesh.v1alpha1.BackendTargetDetails.failover_policy:type_name -> hashicorp.consul.catalog.v1alpha1.FailoverPolicy @@ -1185,7 +1182,7 @@ func file_pbmesh_v1alpha1_computed_routes_proto_init() { } } file_pbmesh_v1alpha1_computed_routes_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*InterpretedHTTPRoute); i { + switch v := v.(*ComputedHTTPRoute); i { case 0: return &v.state case 1: @@ -1197,7 +1194,7 @@ func file_pbmesh_v1alpha1_computed_routes_proto_init() { } } file_pbmesh_v1alpha1_computed_routes_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*InterpretedHTTPRouteRule); i { + switch v := v.(*ComputedHTTPRouteRule); i { case 0: return &v.state case 1: @@ -1209,7 +1206,7 @@ func file_pbmesh_v1alpha1_computed_routes_proto_init() { } } file_pbmesh_v1alpha1_computed_routes_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*InterpretedHTTPBackendRef); i { + switch v := v.(*ComputedHTTPBackendRef); i { case 0: return &v.state case 1: @@ -1221,7 +1218,7 @@ func file_pbmesh_v1alpha1_computed_routes_proto_init() { } } file_pbmesh_v1alpha1_computed_routes_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*InterpretedGRPCRoute); i { + switch v := v.(*ComputedGRPCRoute); i { case 0: return &v.state case 1: @@ -1233,7 +1230,7 @@ func file_pbmesh_v1alpha1_computed_routes_proto_init() { } } file_pbmesh_v1alpha1_computed_routes_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*InterpretedGRPCRouteRule); i { + switch v := v.(*ComputedGRPCRouteRule); i { case 0: return &v.state case 1: @@ -1245,7 +1242,7 @@ func file_pbmesh_v1alpha1_computed_routes_proto_init() { } } file_pbmesh_v1alpha1_computed_routes_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*InterpretedGRPCBackendRef); i { + switch v := v.(*ComputedGRPCBackendRef); i { case 0: return &v.state case 1: @@ -1257,7 +1254,7 @@ func file_pbmesh_v1alpha1_computed_routes_proto_init() { } } file_pbmesh_v1alpha1_computed_routes_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*InterpretedTCPRoute); i { + switch v := v.(*ComputedTCPRoute); i { case 0: return &v.state case 1: @@ -1269,7 +1266,7 @@ func file_pbmesh_v1alpha1_computed_routes_proto_init() { } } file_pbmesh_v1alpha1_computed_routes_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*InterpretedTCPRouteRule); i { + switch v := v.(*ComputedTCPRouteRule); i { case 0: return &v.state case 1: @@ -1281,7 +1278,7 @@ func file_pbmesh_v1alpha1_computed_routes_proto_init() { } } file_pbmesh_v1alpha1_computed_routes_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*InterpretedTCPBackendRef); i { + switch v := v.(*ComputedTCPBackendRef); i { case 0: return &v.state case 1: diff --git a/proto-public/pbmesh/v1alpha1/computed_routes.proto b/proto-public/pbmesh/v1alpha1/computed_routes.proto index f177152083f..4407a48c77c 100644 --- a/proto-public/pbmesh/v1alpha1/computed_routes.proto +++ b/proto-public/pbmesh/v1alpha1/computed_routes.proto @@ -21,9 +21,9 @@ message ComputedRoutes { message ComputedPortRoutes { oneof config { - InterpretedHTTPRoute http = 1; - InterpretedGRPCRoute grpc = 2; - InterpretedTCPRoute tcp = 3; + ComputedHTTPRoute http = 1; + ComputedGRPCRoute grpc = 2; + ComputedTCPRoute tcp = 3; } bool using_default_config = 4; // TODO @@ -31,21 +31,21 @@ message ComputedPortRoutes { map targets = 5; } -message InterpretedHTTPRoute { +message ComputedHTTPRoute { ParentReference parent_ref = 1; reserved 2; // hostnames - repeated InterpretedHTTPRouteRule rules = 3; + repeated ComputedHTTPRouteRule rules = 3; } -message InterpretedHTTPRouteRule { +message ComputedHTTPRouteRule { repeated HTTPRouteMatch matches = 1; repeated HTTPRouteFilter filters = 2; - repeated InterpretedHTTPBackendRef backend_refs = 3; + repeated ComputedHTTPBackendRef backend_refs = 3; HTTPRouteTimeouts timeouts = 4; HTTPRouteRetries retries = 5; } -message InterpretedHTTPBackendRef { +message ComputedHTTPBackendRef { // BackendTarget indicates which key in the targets map provides // the rest of the configuration. // @@ -58,21 +58,21 @@ message InterpretedHTTPBackendRef { repeated HTTPRouteFilter filters = 3; } -message InterpretedGRPCRoute { +message ComputedGRPCRoute { ParentReference parent_ref = 1; reserved 2; // hostnames - repeated InterpretedGRPCRouteRule rules = 3; + repeated ComputedGRPCRouteRule rules = 3; } -message InterpretedGRPCRouteRule { +message ComputedGRPCRouteRule { repeated GRPCRouteMatch matches = 1; repeated GRPCRouteFilter filters = 2; - repeated InterpretedGRPCBackendRef backend_refs = 3; + repeated ComputedGRPCBackendRef backend_refs = 3; HTTPRouteTimeouts timeouts = 4; HTTPRouteRetries retries = 5; } -message InterpretedGRPCBackendRef { +message ComputedGRPCBackendRef { // BackendTarget indicates which key in the targets map provides // the rest of the configuration. // @@ -85,18 +85,18 @@ message InterpretedGRPCBackendRef { repeated GRPCRouteFilter filters = 3; } -message InterpretedTCPRoute { +message ComputedTCPRoute { ParentReference parent_ref = 1; - repeated InterpretedTCPRouteRule rules = 2; + repeated ComputedTCPRouteRule rules = 2; } -message InterpretedTCPRouteRule { - repeated InterpretedTCPBackendRef backend_refs = 1; +message ComputedTCPRouteRule { + repeated ComputedTCPBackendRef backend_refs = 1; } // TODO: look into smuggling the target through a different typeURL, or just // skip in favor of letting the caller do their own lookups? -message InterpretedTCPBackendRef { +message ComputedTCPBackendRef { // BackendTarget indicates which key in the targets map provides // the rest of the configuration. // From cec01740dc8dc59784eb2ce9437679a8bfb51602 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Mon, 14 Aug 2023 17:09:05 -0500 Subject: [PATCH 17/49] extract shared blob --- .../internal/controllers/routes/generate.go | 107 +++++++----------- 1 file changed, 40 insertions(+), 67 deletions(-) diff --git a/internal/mesh/internal/controllers/routes/generate.go b/internal/mesh/internal/controllers/routes/generate.go index 581478b41a4..fce264b67ee 100644 --- a/internal/mesh/internal/controllers/routes/generate.go +++ b/internal/mesh/internal/controllers/routes/generate.go @@ -355,30 +355,13 @@ func compileHTTPRouteNode( backendTarget string backendSvc = serviceGetter.GetService(backendRef.BackendRef.Ref) ) - - if backendSvc == nil { - backendTarget = types.NullRouteBackend - } else { - found := false - inMesh := false - for _, port := range backendSvc.Data.Ports { - if port.Protocol == pbcatalog.Protocol_PROTOCOL_MESH { - inMesh = true - continue // skip - } - if port.TargetPort == backendRef.BackendRef.Port { - found = true - } - } - - if inMesh && found { - details := &pbmesh.BackendTargetDetails{ - BackendRef: backendRef.BackendRef, - } - backendTarget = node.AddTarget(backendRef.BackendRef, details) - } else { - backendTarget = types.NullRouteBackend + if shouldRouteTrafficToBackend(backendSvc, backendRef.BackendRef) { + details := &pbmesh.BackendTargetDetails{ + BackendRef: backendRef.BackendRef, } + backendTarget = node.AddTarget(backendRef.BackendRef, details) + } else { + backendTarget = types.NullRouteBackend } ibr := &pbmesh.ComputedHTTPBackendRef{ BackendTarget: backendTarget, @@ -438,29 +421,13 @@ func compileGRPCRouteNode( backendTarget string backendSvc = serviceGetter.GetService(backendRef.BackendRef.Ref) ) - if backendSvc == nil { - backendTarget = types.NullRouteBackend - } else { - found := false - inMesh := false - for _, port := range backendSvc.Data.Ports { - if port.Protocol == pbcatalog.Protocol_PROTOCOL_MESH { - inMesh = true - continue // skip - } - if port.TargetPort == backendRef.BackendRef.Port { - found = true - } - } - - if inMesh && found { - details := &pbmesh.BackendTargetDetails{ - BackendRef: backendRef.BackendRef, - } - backendTarget = node.AddTarget(backendRef.BackendRef, details) - } else { - backendTarget = types.NullRouteBackend + if shouldRouteTrafficToBackend(backendSvc, backendRef.BackendRef) { + details := &pbmesh.BackendTargetDetails{ + BackendRef: backendRef.BackendRef, } + backendTarget = node.AddTarget(backendRef.BackendRef, details) + } else { + backendTarget = types.NullRouteBackend } ibr := &pbmesh.ComputedGRPCBackendRef{ @@ -512,29 +479,13 @@ func compileTCPRouteNode( backendTarget string backendSvc = serviceGetter.GetService(backendRef.BackendRef.Ref) ) - if backendSvc == nil { - backendTarget = types.NullRouteBackend - } else { - found := false - inMesh := false - for _, port := range backendSvc.Data.Ports { - if port.Protocol == pbcatalog.Protocol_PROTOCOL_MESH { - inMesh = true - continue // skip - } - if port.TargetPort == backendRef.BackendRef.Port { - found = true - } - } - - if inMesh && found { - details := &pbmesh.BackendTargetDetails{ - BackendRef: backendRef.BackendRef, - } - backendTarget = node.AddTarget(backendRef.BackendRef, details) - } else { - backendTarget = types.NullRouteBackend + if shouldRouteTrafficToBackend(backendSvc, backendRef.BackendRef) { + details := &pbmesh.BackendTargetDetails{ + BackendRef: backendRef.BackendRef, } + backendTarget = node.AddTarget(backendRef.BackendRef, details) + } else { + backendTarget = types.NullRouteBackend } ibr := &pbmesh.ComputedTCPBackendRef{ @@ -550,6 +501,28 @@ func compileTCPRouteNode( return node } +func shouldRouteTrafficToBackend(backendSvc *types.DecodedService, backendRef *pbmesh.BackendReference) bool { + if backendSvc == nil { + return false + } + + var ( + found = false + inMesh = false + ) + for _, port := range backendSvc.Data.Ports { + if port.Protocol == pbcatalog.Protocol_PROTOCOL_MESH { + inMesh = true + continue + } + if port.TargetPort == backendRef.Port { + found = true + } + } + + return inMesh && found +} + func createDefaultRouteNode( parentServiceRef *pbresource.Reference, port string, From 97722518b9344d15c4848883e51d4b9a58562779 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Mon, 14 Aug 2023 17:17:48 -0500 Subject: [PATCH 18/49] appease the linter --- .../internal/controllers/routes/controller.go | 2 +- .../internal/controllers/routes/generate.go | 24 ++---------- .../controllers/routes/loader/loader.go | 16 +++----- .../routes/xroutemapper/xroutemapper.go | 38 +++++++++---------- 4 files changed, 28 insertions(+), 52 deletions(-) diff --git a/internal/mesh/internal/controllers/routes/controller.go b/internal/mesh/internal/controllers/routes/controller.go index cfcb42f9c07..22842bc6a51 100644 --- a/internal/mesh/internal/controllers/routes/controller.go +++ b/internal/mesh/internal/controllers/routes/controller.go @@ -65,7 +65,7 @@ func (r *routesReconciler) Reconcile(ctx context.Context, rt controller.Runtime, ValidateXRouteReferences(related, pending) - generatedResults := GenerateComputedRoutes(ctx, loggerFor, related, pending) + generatedResults := GenerateComputedRoutes(related, pending) if err := UpdatePendingStatuses(ctx, rt, pending); err != nil { rt.Logger.Error("error updating statuses for affected relevant resources", "error", err) diff --git a/internal/mesh/internal/controllers/routes/generate.go b/internal/mesh/internal/controllers/routes/generate.go index fce264b67ee..1f859eab4ee 100644 --- a/internal/mesh/internal/controllers/routes/generate.go +++ b/internal/mesh/internal/controllers/routes/generate.go @@ -4,10 +4,6 @@ package routes import ( - "context" - - "github.com/hashicorp/go-hclog" - "github.com/hashicorp/consul/internal/catalog" "github.com/hashicorp/consul/internal/mesh/internal/controllers/routes/loader" "github.com/hashicorp/consul/internal/mesh/internal/types" @@ -25,19 +21,12 @@ import ( // // This should not internally generate, nor return any errors. func GenerateComputedRoutes( - ctx context.Context, - loggerFor func(*pbresource.ID) hclog.Logger, related *loader.RelatedResources, pending PendingStatuses, ) []*ComputedRoutesResult { - if loggerFor == nil { - loggerFor = func(_ *pbresource.ID) hclog.Logger { - return hclog.NewNullLogger() - } - } out := make([]*ComputedRoutesResult, 0, len(related.ComputedRoutesList)) for _, computedRoutesID := range related.ComputedRoutesList { - out = append(out, compile(ctx, loggerFor, related, computedRoutesID, pending)) + out = append(out, compile(related, computedRoutesID, pending)) } return out } @@ -50,8 +39,6 @@ type ComputedRoutesResult struct { } func compile( - ctx context.Context, - loggerFor func(*pbresource.ID) hclog.Logger, related *loader.RelatedResources, computedRoutesID *pbresource.ID, pending PendingStatuses, @@ -139,21 +126,21 @@ func compile( node = newInputRouteNode(port) setupDefaultHTTPRouteNode(node, types.NullRouteBackend) } else { - node = compileHTTPRouteNode(parentServiceRef, port, res, route, related) + node = compileHTTPRouteNode(port, res, route, related) } case *pbmesh.GRPCRoute: if nullRouteTraffic { node = newInputRouteNode(port) setupDefaultGRPCRouteNode(node, types.NullRouteBackend) } else { - node = compileGRPCRouteNode(parentServiceRef, port, res, route, related) + node = compileGRPCRouteNode(port, res, route, related) } case *pbmesh.TCPRoute: if nullRouteTraffic { node = newInputRouteNode(port) setupDefaultTCPRouteNode(node, types.NullRouteBackend) } else { - node = compileTCPRouteNode(parentServiceRef, port, res, route, related) + node = compileTCPRouteNode(port, res, route, related) } default: return // unknown xroute type (impossible) @@ -303,7 +290,6 @@ func compile( } func compileHTTPRouteNode( - parentServiceRef *pbresource.Reference, port string, res *pbresource.Resource, route *pbmesh.HTTPRoute, @@ -378,7 +364,6 @@ func compileHTTPRouteNode( } func compileGRPCRouteNode( - parentServiceRef *pbresource.Reference, port string, res *pbresource.Resource, route *pbmesh.GRPCRoute, @@ -445,7 +430,6 @@ func compileGRPCRouteNode( } func compileTCPRouteNode( - parentServiceRef *pbresource.Reference, port string, res *pbresource.Resource, route *pbmesh.TCPRoute, diff --git a/internal/mesh/internal/controllers/routes/loader/loader.go b/internal/mesh/internal/controllers/routes/loader/loader.go index 5ce48495933..0c8ae49b8f6 100644 --- a/internal/mesh/internal/controllers/routes/loader/loader.go +++ b/internal/mesh/internal/controllers/routes/loader/loader.go @@ -48,7 +48,7 @@ func LoadResourcesForComputedRoutes( mcDone: make(map[resource.ReferenceKey]struct{}), } - if err := loader.load(ctx, loggerFor, client, computedRoutesID); err != nil { + if err := loader.load(ctx, loggerFor, computedRoutesID); err != nil { return nil, err } @@ -87,7 +87,6 @@ func (l *loader) nextRequested() *pbresource.ID { func (l *loader) load( ctx context.Context, loggerFor func(*pbresource.ID) hclog.Logger, - client pbresource.ResourceServiceClient, computedRoutesID *pbresource.ID, ) error { l.out = NewRelatedResources() @@ -101,7 +100,7 @@ func (l *loader) load( break } - if err := l.loadOne(ctx, loggerFor, client, mcID); err != nil { + if err := l.loadOne(ctx, loggerFor, mcID); err != nil { return err } } @@ -112,7 +111,6 @@ func (l *loader) load( func (l *loader) loadOne( ctx context.Context, loggerFor func(*pbresource.ID) hclog.Logger, - client pbresource.ResourceServiceClient, computedRoutesID *pbresource.ID, ) error { logger := loggerFor(computedRoutesID) @@ -128,7 +126,7 @@ func (l *loader) loadOne( return err } - if err := l.gatherXRoutesAsInput(ctx, logger, computedRoutesID, parentServiceRef); err != nil { + if err := l.gatherXRoutesAsInput(ctx, logger, parentServiceRef); err != nil { return err } @@ -142,7 +140,6 @@ func (l *loader) loadOne( func (l *loader) gatherXRoutesAsInput( ctx context.Context, logger hclog.Logger, - computedRoutesID *pbresource.ID, parentServiceRef *pbresource.Reference, ) error { routeIDs := l.mapper.RouteIDsByParentServiceRef(parentServiceRef) @@ -159,7 +156,7 @@ func (l *loader) gatherXRoutesAsInput( if route != nil { routeData = route.Data } - err = l.gatherSingleXRouteAsInput(ctx, logger, computedRoutesID, routeID, routeData, func() { + err = l.gatherSingleXRouteAsInput(ctx, logger, routeID, routeData, func() { l.out.AddResource(route) }) if err != nil { @@ -174,7 +171,7 @@ func (l *loader) gatherXRoutesAsInput( if route != nil { routeData = route.Data } - err = l.gatherSingleXRouteAsInput(ctx, logger, computedRoutesID, routeID, routeData, func() { + err = l.gatherSingleXRouteAsInput(ctx, logger, routeID, routeData, func() { l.out.AddResource(route) }) if err != nil { @@ -189,7 +186,7 @@ func (l *loader) gatherXRoutesAsInput( if route != nil { routeData = route.Data } - err = l.gatherSingleXRouteAsInput(ctx, logger, computedRoutesID, routeID, routeData, func() { + err = l.gatherSingleXRouteAsInput(ctx, logger, routeID, routeData, func() { l.out.AddResource(route) }) if err != nil { @@ -264,7 +261,6 @@ func (l *loader) loadUpstreamService( func (l *loader) gatherSingleXRouteAsInput( ctx context.Context, logger hclog.Logger, - computedRoutesID *pbresource.ID, routeID *pbresource.ID, route types.XRouteData, relatedRouteCaptureFn func(), diff --git a/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper.go b/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper.go index 3250257b709..be31cc956ec 100644 --- a/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper.go +++ b/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper.go @@ -157,18 +157,18 @@ func (m *Mapper) BackendServiceRefsByRouteID(item *pbresource.ID) []*pbresource. } // MapHTTPRoute will map HTTPRoute changes to ComputedRoutes changes. -func (m *Mapper) MapHTTPRoute(ctx context.Context, rt controller.Runtime, res *pbresource.Resource) ([]controller.Request, error) { - return mapXRouteToComputedRoutes[pbmesh.HTTPRoute, *pbmesh.HTTPRoute](ctx, rt, res, m) +func (m *Mapper) MapHTTPRoute(_ context.Context, _ controller.Runtime, res *pbresource.Resource) ([]controller.Request, error) { + return mapXRouteToComputedRoutes[pbmesh.HTTPRoute, *pbmesh.HTTPRoute](res, m) } // MapGRPCRoute will map GRPCRoute changes to ComputedRoutes changes. -func (m *Mapper) MapGRPCRoute(ctx context.Context, rt controller.Runtime, res *pbresource.Resource) ([]controller.Request, error) { - return mapXRouteToComputedRoutes[pbmesh.GRPCRoute, *pbmesh.GRPCRoute](ctx, rt, res, m) +func (m *Mapper) MapGRPCRoute(_ context.Context, _ controller.Runtime, res *pbresource.Resource) ([]controller.Request, error) { + return mapXRouteToComputedRoutes[pbmesh.GRPCRoute, *pbmesh.GRPCRoute](res, m) } // MapTCPRoute will map TCPRoute changes to ComputedRoutes changes. -func (m *Mapper) MapTCPRoute(ctx context.Context, rt controller.Runtime, res *pbresource.Resource) ([]controller.Request, error) { - return mapXRouteToComputedRoutes[pbmesh.TCPRoute, *pbmesh.TCPRoute](ctx, rt, res, m) +func (m *Mapper) MapTCPRoute(_ context.Context, _ controller.Runtime, res *pbresource.Resource) ([]controller.Request, error) { + return mapXRouteToComputedRoutes[pbmesh.TCPRoute, *pbmesh.TCPRoute](res, m) } // mapXRouteToComputedRoutes will map xRoute changes to ComputedRoutes changes. @@ -176,7 +176,7 @@ func mapXRouteToComputedRoutes[V any, PV interface { proto.Message *V types.XRouteWithRefs -}](ctx context.Context, rt controller.Runtime, res *pbresource.Resource, m *Mapper) ([]controller.Request, error) { +}](res *pbresource.Resource, m *Mapper) ([]controller.Request, error) { dec, err := resource.Decode[V, PV](res) if err != nil { return nil, fmt.Errorf("error unmarshalling xRoute: %w", err) @@ -193,8 +193,8 @@ func mapXRouteToComputedRoutes[V any, PV interface { } func (m *Mapper) MapFailoverPolicy( - ctx context.Context, - rt controller.Runtime, + _ context.Context, + _ controller.Runtime, res *pbresource.Resource, ) ([]controller.Request, error) { if !types.IsFailoverPolicyType(res.Id.Type) { @@ -212,7 +212,7 @@ func (m *Mapper) MapFailoverPolicy( // will route any traffic to this destination service. svcID := changeType(res.Id, catalog.ServiceType) - return m.mapXRouteDirectServiceRefToComputedRoutesByID(ctx, rt, svcID) + return m.mapXRouteDirectServiceRefToComputedRoutesByID(svcID) } func (m *Mapper) TrackFailoverPolicy(failover *types.DecodedFailoverPolicy) { @@ -226,8 +226,8 @@ func (m *Mapper) UntrackFailoverPolicy(failoverPolicyID *pbresource.ID) { } func (m *Mapper) MapDestinationPolicy( - ctx context.Context, - rt controller.Runtime, + _ context.Context, + _ controller.Runtime, res *pbresource.Resource, ) ([]controller.Request, error) { if !types.IsDestinationPolicyType(res.Id.Type) { @@ -238,12 +238,12 @@ func (m *Mapper) MapDestinationPolicy( // will route any traffic to this destination service. svcID := changeType(res.Id, catalog.ServiceType) - return m.mapXRouteDirectServiceRefToComputedRoutesByID(ctx, rt, svcID) + return m.mapXRouteDirectServiceRefToComputedRoutesByID(svcID) } func (m *Mapper) MapService( - ctx context.Context, - rt controller.Runtime, + _ context.Context, + _ controller.Runtime, res *pbresource.Resource, ) ([]controller.Request, error) { // Ultimately we want to wake up a ComputedRoutes if either of the @@ -261,7 +261,7 @@ func (m *Mapper) MapService( var reqs []controller.Request for _, svcID := range effectiveServiceIDs { - got, err := m.mapXRouteDirectServiceRefToComputedRoutesByID(ctx, rt, svcID) + got, err := m.mapXRouteDirectServiceRefToComputedRoutesByID(svcID) if err != nil { return nil, err } @@ -272,11 +272,7 @@ func (m *Mapper) MapService( } // NOTE: this function does not interrogate down into failover policies -func (m *Mapper) mapXRouteDirectServiceRefToComputedRoutesByID( - ctx context.Context, - rt controller.Runtime, - svcID *pbresource.ID, -) ([]controller.Request, error) { +func (m *Mapper) mapXRouteDirectServiceRefToComputedRoutesByID(svcID *pbresource.ID) ([]controller.Request, error) { if !types.IsServiceType(svcID.Type) { return nil, fmt.Errorf("type is not a service type: %s", svcID.Type) } From e240ec7fa2c195824af3e314580b46aeedfc7f57 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Mon, 14 Aug 2023 17:20:27 -0500 Subject: [PATCH 19/49] remove todo --- internal/mesh/internal/controllers/routes/intermediate.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/mesh/internal/controllers/routes/intermediate.go b/internal/mesh/internal/controllers/routes/intermediate.go index 4c95b432ff3..b5ac1c02095 100644 --- a/internal/mesh/internal/controllers/routes/intermediate.go +++ b/internal/mesh/internal/controllers/routes/intermediate.go @@ -10,8 +10,6 @@ import ( "github.com/hashicorp/consul/proto-public/pbresource" ) -// TODO: move to a new package? compiler? - // inputRouteNode is a dressed up version of an XRoute meant as working state // for one pass of the compilation procedure. type inputRouteNode struct { From 4e0da78b75181d1615446dc68df3c110507f85fc Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Mon, 14 Aug 2023 17:31:54 -0500 Subject: [PATCH 20/49] code gen helpers --- .../routes/loader/dest_policy.gen.go | 46 +++++ .../routes/loader/failover_policy.gen.go | 46 +++++ .../routes/loader/gen_memoizer_funcs.sh | 92 ++++++++++ .../routes/loader/grpc_route.gen.go | 46 +++++ .../routes/loader/http_route.gen.go | 46 +++++ .../controllers/routes/loader/memoized.go | 161 +++--------------- .../controllers/routes/loader/service.gen.go | 46 +++++ .../routes/loader/tcp_route.gen.go | 46 +++++ 8 files changed, 388 insertions(+), 141 deletions(-) create mode 100644 internal/mesh/internal/controllers/routes/loader/dest_policy.gen.go create mode 100644 internal/mesh/internal/controllers/routes/loader/failover_policy.gen.go create mode 100755 internal/mesh/internal/controllers/routes/loader/gen_memoizer_funcs.sh create mode 100644 internal/mesh/internal/controllers/routes/loader/grpc_route.gen.go create mode 100644 internal/mesh/internal/controllers/routes/loader/http_route.gen.go create mode 100644 internal/mesh/internal/controllers/routes/loader/service.gen.go create mode 100644 internal/mesh/internal/controllers/routes/loader/tcp_route.gen.go diff --git a/internal/mesh/internal/controllers/routes/loader/dest_policy.gen.go b/internal/mesh/internal/controllers/routes/loader/dest_policy.gen.go new file mode 100644 index 00000000000..8ee7b5d613b --- /dev/null +++ b/internal/mesh/internal/controllers/routes/loader/dest_policy.gen.go @@ -0,0 +1,46 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 +package loader + +// Code generated by gen_memoizer_funcs.sh. DO NOT EDIT. + +import ( + "context" + "fmt" + + "github.com/hashicorp/consul/internal/catalog" + "github.com/hashicorp/consul/internal/mesh/internal/types" + "github.com/hashicorp/consul/internal/resource" + pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v1alpha1" + pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" + "github.com/hashicorp/consul/proto-public/pbresource" +) + +// Avoid unused imports in generated code. +var _ *pbmesh.ParentReference + +var _ *pbcatalog.Service + +var _ = types.HTTPRouteType + +var _ = catalog.ServiceType + +func (m *memoizingLoader) GetDestinationPolicy(ctx context.Context, id *pbresource.ID) (*types.DecodedDestinationPolicy, error) { + if !resource.EqualType(id.Type, types.DestinationPolicyType) { + return nil, fmt.Errorf("expected *pbmesh.DestinationPolicy, not %s", resource.TypeToString(id.Type)) + } + + rk := resource.NewReferenceKey(id) + + if cached, ok := m.mapDestinationPolicy[rk]; ok { + return cached, nil // cached value may be nil + } + + dec, err := resource.GetDecodedResource[pbmesh.DestinationPolicy, *pbmesh.DestinationPolicy](ctx, m.client, id) + if err != nil { + return nil, err + } + + m.mapDestinationPolicy[rk] = dec + return dec, nil +} diff --git a/internal/mesh/internal/controllers/routes/loader/failover_policy.gen.go b/internal/mesh/internal/controllers/routes/loader/failover_policy.gen.go new file mode 100644 index 00000000000..4b1d4fc6aec --- /dev/null +++ b/internal/mesh/internal/controllers/routes/loader/failover_policy.gen.go @@ -0,0 +1,46 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 +package loader + +// Code generated by gen_memoizer_funcs.sh. DO NOT EDIT. + +import ( + "context" + "fmt" + + "github.com/hashicorp/consul/internal/catalog" + "github.com/hashicorp/consul/internal/mesh/internal/types" + "github.com/hashicorp/consul/internal/resource" + pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v1alpha1" + pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" + "github.com/hashicorp/consul/proto-public/pbresource" +) + +// Avoid unused imports in generated code. +var _ *pbmesh.ParentReference + +var _ *pbcatalog.Service + +var _ = types.HTTPRouteType + +var _ = catalog.ServiceType + +func (m *memoizingLoader) GetFailoverPolicy(ctx context.Context, id *pbresource.ID) (*types.DecodedFailoverPolicy, error) { + if !resource.EqualType(id.Type, catalog.FailoverPolicyType) { + return nil, fmt.Errorf("expected *pbcatalog.FailoverPolicy, not %s", resource.TypeToString(id.Type)) + } + + rk := resource.NewReferenceKey(id) + + if cached, ok := m.mapFailoverPolicy[rk]; ok { + return cached, nil // cached value may be nil + } + + dec, err := resource.GetDecodedResource[pbcatalog.FailoverPolicy, *pbcatalog.FailoverPolicy](ctx, m.client, id) + if err != nil { + return nil, err + } + + m.mapFailoverPolicy[rk] = dec + return dec, nil +} diff --git a/internal/mesh/internal/controllers/routes/loader/gen_memoizer_funcs.sh b/internal/mesh/internal/controllers/routes/loader/gen_memoizer_funcs.sh new file mode 100755 index 00000000000..2b0e0396714 --- /dev/null +++ b/internal/mesh/internal/controllers/routes/loader/gen_memoizer_funcs.sh @@ -0,0 +1,92 @@ +#!/usr/bin/env bash +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +unset CDPATH + +set -euo pipefail + +# NOTE: this script could be made more generic if folks find it useful. + +if [[ $# -lt 2 ]]; then + echo "expected at least 2 args" >&2 + exit 1 +fi + +# first argument is output path +output="$1" +shift + +if [[ "$output" != *.go ]]; then + echo "output file should end with a .go suffix" >&2 + exit 2 +fi + +cat > "${output}.tmp" <&2 + exit 1 + fi + gopkg="types" + if [[ "$pkg" = "pbcatalog" ]]; then + gopkg="catalog" + fi + ###################### + cat >> "${output}.tmp" < Date: Tue, 15 Aug 2023 15:35:17 -0500 Subject: [PATCH 21/49] fix go.mod --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 215b4d55e65..6f66be3244a 100644 --- a/go.mod +++ b/go.mod @@ -88,6 +88,7 @@ require ( github.com/mitchellh/pointerstructure v1.2.1 github.com/mitchellh/reflectwalk v1.0.2 github.com/natefinch/npipe v0.0.0-20160621034901-c1b8fa8bdcce + github.com/oklog/ulid v1.3.1 github.com/oklog/ulid/v2 v2.1.0 github.com/olekukonko/tablewriter v0.0.4 github.com/patrickmn/go-cache v2.1.0+incompatible @@ -219,7 +220,6 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 // indirect github.com/oklog/run v1.0.0 // indirect - github.com/oklog/ulid v1.3.1 // indirect github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c // indirect github.com/pierrec/lz4 v2.5.2+incompatible // indirect From fba64fdeface78c478e006a3fbcc97f3356c521b Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Mon, 21 Aug 2023 16:53:35 -0500 Subject: [PATCH 22/49] fix signatures --- .../mesh/internal/controllers/routes/controller.go | 2 +- .../controllers/routes/loader/dest_policy.gen.go | 2 +- .../routes/loader/failover_policy.gen.go | 2 +- .../controllers/routes/loader/gen_memoizer_funcs.sh | 2 +- .../controllers/routes/loader/grpc_route.gen.go | 2 +- .../controllers/routes/loader/http_route.gen.go | 2 +- .../controllers/routes/loader/loader_test.go | 12 ++++++------ .../controllers/routes/loader/service.gen.go | 2 +- .../controllers/routes/loader/tcp_route.gen.go | 2 +- .../controllers/routes/ref_validation_test.go | 2 +- .../controllers/routes/xroutemapper/util.go | 8 ++++---- .../controllers/routes/xroutemapper/xroutemapper.go | 13 ++++++------- 12 files changed, 25 insertions(+), 26 deletions(-) diff --git a/internal/mesh/internal/controllers/routes/controller.go b/internal/mesh/internal/controllers/routes/controller.go index 22842bc6a51..e81e69fc632 100644 --- a/internal/mesh/internal/controllers/routes/controller.go +++ b/internal/mesh/internal/controllers/routes/controller.go @@ -77,7 +77,7 @@ func (r *routesReconciler) Reconcile(ctx context.Context, rt controller.Runtime, logger := rt.Logger.With("resource-id", resource.IDToString(computedRoutesID)) - prev, err := resource.GetDecodedResource[pbmesh.ComputedRoutes, *pbmesh.ComputedRoutes](ctx, rt.Client, computedRoutesID) + prev, err := resource.GetDecodedResource[*pbmesh.ComputedRoutes](ctx, rt.Client, computedRoutesID) if err != nil { logger.Error("error loading previous computed routes", "error", err) return err diff --git a/internal/mesh/internal/controllers/routes/loader/dest_policy.gen.go b/internal/mesh/internal/controllers/routes/loader/dest_policy.gen.go index 8ee7b5d613b..ccffb6b5328 100644 --- a/internal/mesh/internal/controllers/routes/loader/dest_policy.gen.go +++ b/internal/mesh/internal/controllers/routes/loader/dest_policy.gen.go @@ -36,7 +36,7 @@ func (m *memoizingLoader) GetDestinationPolicy(ctx context.Context, id *pbresour return cached, nil // cached value may be nil } - dec, err := resource.GetDecodedResource[pbmesh.DestinationPolicy, *pbmesh.DestinationPolicy](ctx, m.client, id) + dec, err := resource.GetDecodedResource[*pbmesh.DestinationPolicy](ctx, m.client, id) if err != nil { return nil, err } diff --git a/internal/mesh/internal/controllers/routes/loader/failover_policy.gen.go b/internal/mesh/internal/controllers/routes/loader/failover_policy.gen.go index 4b1d4fc6aec..3eb56fb6e1f 100644 --- a/internal/mesh/internal/controllers/routes/loader/failover_policy.gen.go +++ b/internal/mesh/internal/controllers/routes/loader/failover_policy.gen.go @@ -36,7 +36,7 @@ func (m *memoizingLoader) GetFailoverPolicy(ctx context.Context, id *pbresource. return cached, nil // cached value may be nil } - dec, err := resource.GetDecodedResource[pbcatalog.FailoverPolicy, *pbcatalog.FailoverPolicy](ctx, m.client, id) + dec, err := resource.GetDecodedResource[*pbcatalog.FailoverPolicy](ctx, m.client, id) if err != nil { return nil, err } diff --git a/internal/mesh/internal/controllers/routes/loader/gen_memoizer_funcs.sh b/internal/mesh/internal/controllers/routes/loader/gen_memoizer_funcs.sh index 2b0e0396714..cc546340f6a 100755 --- a/internal/mesh/internal/controllers/routes/loader/gen_memoizer_funcs.sh +++ b/internal/mesh/internal/controllers/routes/loader/gen_memoizer_funcs.sh @@ -76,7 +76,7 @@ func (m *memoizingLoader) Get${message}(ctx context.Context, id *pbresource.ID) return cached, nil // cached value may be nil } - dec, err := resource.GetDecodedResource[${pkg}.${message}, *${pkg}.${message}](ctx, m.client, id) + dec, err := resource.GetDecodedResource[*${pkg}.${message}](ctx, m.client, id) if err != nil { return nil, err } diff --git a/internal/mesh/internal/controllers/routes/loader/grpc_route.gen.go b/internal/mesh/internal/controllers/routes/loader/grpc_route.gen.go index 06b687c49ee..77a0b103946 100644 --- a/internal/mesh/internal/controllers/routes/loader/grpc_route.gen.go +++ b/internal/mesh/internal/controllers/routes/loader/grpc_route.gen.go @@ -36,7 +36,7 @@ func (m *memoizingLoader) GetGRPCRoute(ctx context.Context, id *pbresource.ID) ( return cached, nil // cached value may be nil } - dec, err := resource.GetDecodedResource[pbmesh.GRPCRoute, *pbmesh.GRPCRoute](ctx, m.client, id) + dec, err := resource.GetDecodedResource[*pbmesh.GRPCRoute](ctx, m.client, id) if err != nil { return nil, err } diff --git a/internal/mesh/internal/controllers/routes/loader/http_route.gen.go b/internal/mesh/internal/controllers/routes/loader/http_route.gen.go index 0ee196332ab..40f2edffbfc 100644 --- a/internal/mesh/internal/controllers/routes/loader/http_route.gen.go +++ b/internal/mesh/internal/controllers/routes/loader/http_route.gen.go @@ -36,7 +36,7 @@ func (m *memoizingLoader) GetHTTPRoute(ctx context.Context, id *pbresource.ID) ( return cached, nil // cached value may be nil } - dec, err := resource.GetDecodedResource[pbmesh.HTTPRoute, *pbmesh.HTTPRoute](ctx, m.client, id) + dec, err := resource.GetDecodedResource[*pbmesh.HTTPRoute](ctx, m.client, id) if err != nil { return nil, err } diff --git a/internal/mesh/internal/controllers/routes/loader/loader_test.go b/internal/mesh/internal/controllers/routes/loader/loader_test.go index 1bb0d9aca6b..7909eaba1cb 100644 --- a/internal/mesh/internal/controllers/routes/loader/loader_test.go +++ b/internal/mesh/internal/controllers/routes/loader/loader_test.go @@ -57,7 +57,7 @@ func TestLoadResourcesForComputedRoutes(t *testing.T) { WithData(t, data). Write(t, client) mapper.TrackXRoute(res.Id, data) - dec, err := resource.Decode[pbmesh.HTTPRoute, *pbmesh.HTTPRoute](res) + dec, err := resource.Decode[*pbmesh.HTTPRoute](res) require.NoError(t, err) return dec } @@ -67,7 +67,7 @@ func TestLoadResourcesForComputedRoutes(t *testing.T) { WithData(t, data). Write(t, client) mapper.TrackXRoute(res.Id, data) - dec, err := resource.Decode[pbmesh.GRPCRoute, *pbmesh.GRPCRoute](res) + dec, err := resource.Decode[*pbmesh.GRPCRoute](res) require.NoError(t, err) return dec } @@ -78,7 +78,7 @@ func TestLoadResourcesForComputedRoutes(t *testing.T) { WithData(t, data). Write(t, client) mapper.TrackXRoute(res.Id, data) - dec, err := resource.Decode[pbmesh.TCPRoute, *pbmesh.TCPRoute](res) + dec, err := resource.Decode[*pbmesh.TCPRoute](res) require.NoError(t, err) return dec } @@ -88,7 +88,7 @@ func TestLoadResourcesForComputedRoutes(t *testing.T) { res := rtest.Resource(types.DestinationPolicyType, name). WithData(t, data). Write(t, client) - dec, err := resource.Decode[pbmesh.DestinationPolicy, *pbmesh.DestinationPolicy](res) + dec, err := resource.Decode[*pbmesh.DestinationPolicy](res) require.NoError(t, err) return dec } @@ -97,7 +97,7 @@ func TestLoadResourcesForComputedRoutes(t *testing.T) { res := rtest.Resource(catalog.FailoverPolicyType, name). WithData(t, data). Write(t, client) - dec, err := resource.Decode[pbcatalog.FailoverPolicy, *pbcatalog.FailoverPolicy](res) + dec, err := resource.Decode[*pbcatalog.FailoverPolicy](res) require.NoError(t, err) return dec } @@ -106,7 +106,7 @@ func TestLoadResourcesForComputedRoutes(t *testing.T) { res := rtest.Resource(catalog.ServiceType, name). WithData(t, data). Write(t, client) - dec, err := resource.Decode[pbcatalog.Service, *pbcatalog.Service](res) + dec, err := resource.Decode[*pbcatalog.Service](res) require.NoError(t, err) return dec } diff --git a/internal/mesh/internal/controllers/routes/loader/service.gen.go b/internal/mesh/internal/controllers/routes/loader/service.gen.go index 7b6b7688024..67880881c2f 100644 --- a/internal/mesh/internal/controllers/routes/loader/service.gen.go +++ b/internal/mesh/internal/controllers/routes/loader/service.gen.go @@ -36,7 +36,7 @@ func (m *memoizingLoader) GetService(ctx context.Context, id *pbresource.ID) (*t return cached, nil // cached value may be nil } - dec, err := resource.GetDecodedResource[pbcatalog.Service, *pbcatalog.Service](ctx, m.client, id) + dec, err := resource.GetDecodedResource[*pbcatalog.Service](ctx, m.client, id) if err != nil { return nil, err } diff --git a/internal/mesh/internal/controllers/routes/loader/tcp_route.gen.go b/internal/mesh/internal/controllers/routes/loader/tcp_route.gen.go index e836c566c2d..0b2c525c537 100644 --- a/internal/mesh/internal/controllers/routes/loader/tcp_route.gen.go +++ b/internal/mesh/internal/controllers/routes/loader/tcp_route.gen.go @@ -36,7 +36,7 @@ func (m *memoizingLoader) GetTCPRoute(ctx context.Context, id *pbresource.ID) (* return cached, nil // cached value may be nil } - dec, err := resource.GetDecodedResource[pbmesh.TCPRoute, *pbmesh.TCPRoute](ctx, m.client, id) + dec, err := resource.GetDecodedResource[*pbmesh.TCPRoute](ctx, m.client, id) if err != nil { return nil, err } diff --git a/internal/mesh/internal/controllers/routes/ref_validation_test.go b/internal/mesh/internal/controllers/routes/ref_validation_test.go index b6a697ef137..30f187de8e8 100644 --- a/internal/mesh/internal/controllers/routes/ref_validation_test.go +++ b/internal/mesh/internal/controllers/routes/ref_validation_test.go @@ -36,7 +36,7 @@ func TestComputeNewRouteRefConditions(t *testing.T) { Build() rtest.ValidateAndNormalize(t, registry, svc) - dec, err := resource.Decode[pbcatalog.Service, *pbcatalog.Service](svc) + dec, err := resource.Decode[*pbcatalog.Service](svc) require.NoError(t, err) return dec } diff --git a/internal/mesh/internal/controllers/routes/xroutemapper/util.go b/internal/mesh/internal/controllers/routes/xroutemapper/util.go index 4239a34c90d..3f14419c84d 100644 --- a/internal/mesh/internal/controllers/routes/xroutemapper/util.go +++ b/internal/mesh/internal/controllers/routes/xroutemapper/util.go @@ -34,11 +34,11 @@ type resID struct { UID string } -func parentRefSliceToRefSlice(parentRefs []*pbmesh.ParentReference) []*pbresource.Reference { +func parentRefSliceToRefSlice(parentRefs []*pbmesh.ParentReference) []resource.ReferenceOrID { if parentRefs == nil { return nil } - parents := make([]*pbresource.Reference, 0, len(parentRefs)) + parents := make([]resource.ReferenceOrID, 0, len(parentRefs)) for _, parentRef := range parentRefs { if parentRef.Ref != nil && types.IsServiceType(parentRef.Ref.Type) { parents = append(parents, parentRef.Ref) @@ -47,11 +47,11 @@ func parentRefSliceToRefSlice(parentRefs []*pbmesh.ParentReference) []*pbresourc return parents } -func backendRefSliceToRefSlice(backendRefs []*pbmesh.BackendReference) []*pbresource.Reference { +func backendRefSliceToRefSlice(backendRefs []*pbmesh.BackendReference) []resource.ReferenceOrID { if backendRefs == nil { return nil } - backends := make([]*pbresource.Reference, 0, len(backendRefs)) + backends := make([]resource.ReferenceOrID, 0, len(backendRefs)) for _, backendRef := range backendRefs { if backendRef.Ref != nil && types.IsServiceType(backendRef.Ref.Type) { backends = append(backends, backendRef.Ref) diff --git a/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper.go b/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper.go index be31cc956ec..77e241e5b27 100644 --- a/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper.go +++ b/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper.go @@ -158,26 +158,25 @@ func (m *Mapper) BackendServiceRefsByRouteID(item *pbresource.ID) []*pbresource. // MapHTTPRoute will map HTTPRoute changes to ComputedRoutes changes. func (m *Mapper) MapHTTPRoute(_ context.Context, _ controller.Runtime, res *pbresource.Resource) ([]controller.Request, error) { - return mapXRouteToComputedRoutes[pbmesh.HTTPRoute, *pbmesh.HTTPRoute](res, m) + return mapXRouteToComputedRoutes[*pbmesh.HTTPRoute](res, m) } // MapGRPCRoute will map GRPCRoute changes to ComputedRoutes changes. func (m *Mapper) MapGRPCRoute(_ context.Context, _ controller.Runtime, res *pbresource.Resource) ([]controller.Request, error) { - return mapXRouteToComputedRoutes[pbmesh.GRPCRoute, *pbmesh.GRPCRoute](res, m) + return mapXRouteToComputedRoutes[*pbmesh.GRPCRoute](res, m) } // MapTCPRoute will map TCPRoute changes to ComputedRoutes changes. func (m *Mapper) MapTCPRoute(_ context.Context, _ controller.Runtime, res *pbresource.Resource) ([]controller.Request, error) { - return mapXRouteToComputedRoutes[pbmesh.TCPRoute, *pbmesh.TCPRoute](res, m) + return mapXRouteToComputedRoutes[*pbmesh.TCPRoute](res, m) } // mapXRouteToComputedRoutes will map xRoute changes to ComputedRoutes changes. -func mapXRouteToComputedRoutes[V any, PV interface { +func mapXRouteToComputedRoutes[T interface { proto.Message - *V types.XRouteWithRefs }](res *pbresource.Resource, m *Mapper) ([]controller.Request, error) { - dec, err := resource.Decode[V, PV](res) + dec, err := resource.Decode[T](res) if err != nil { return nil, fmt.Errorf("error unmarshalling xRoute: %w", err) } @@ -201,7 +200,7 @@ func (m *Mapper) MapFailoverPolicy( return nil, fmt.Errorf("type is not a failover policy type: %s", res.Id.Type) } - dec, err := resource.Decode[pbcatalog.FailoverPolicy, *pbcatalog.FailoverPolicy](res) + dec, err := resource.Decode[*pbcatalog.FailoverPolicy](res) if err != nil { return nil, fmt.Errorf("error unmarshalling failover policy: %w", err) } From 9570170b243e083e65b6fa0ec8067f61ad251fce Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Mon, 21 Aug 2023 17:08:22 -0500 Subject: [PATCH 23/49] remove unnecessary meta fields --- internal/mesh/internal/controllers/routes/controller.go | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/internal/mesh/internal/controllers/routes/controller.go b/internal/mesh/internal/controllers/routes/controller.go index e81e69fc632..7ff4f6fdd60 100644 --- a/internal/mesh/internal/controllers/routes/controller.go +++ b/internal/mesh/internal/controllers/routes/controller.go @@ -20,10 +20,6 @@ import ( "github.com/hashicorp/consul/proto-public/pbresource" ) -const ( - metaManagedBy = "managed-by-controller" -) - func Controller() controller.Controller { mapper := xroutemapper.New() @@ -135,10 +131,7 @@ func upsertComputedRoutes( Resource: &pbresource.Resource{ Id: id, Owner: ownerID, - Metadata: map[string]string{ - metaManagedBy: StatusKey, - }, - Data: mcData, + Data: mcData, }, }) if err != nil { From b189c94cd97f18782551fe21fd49f5c0e393ab49 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Mon, 21 Aug 2023 17:09:02 -0500 Subject: [PATCH 24/49] clarify comment --- internal/mesh/internal/controllers/routes/controller.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/mesh/internal/controllers/routes/controller.go b/internal/mesh/internal/controllers/routes/controller.go index 7ff4f6fdd60..86c4c24cb31 100644 --- a/internal/mesh/internal/controllers/routes/controller.go +++ b/internal/mesh/internal/controllers/routes/controller.go @@ -41,10 +41,10 @@ type routesReconciler struct { } func (r *routesReconciler) Reconcile(ctx context.Context, rt controller.Runtime, req controller.Request) error { - // Notably don't inject the resource-id here, since we have to do a fan-out - // to multiple resources due to xRoutes having multiple parent refs. + // Notably don't inject the resource-id here into the logger, since we have + // to do a fan-out to multiple resources due to xRoutes having multiple + // parent refs. rt.Logger = rt.Logger.With("controller", StatusKey) - // rt.Logger = rt.Logger.With("event-resource-id", resource.IDToString(req.ID)) rt.Logger.Trace("reconciling computed routes") From 0430838dfcbae606d33bcd44efaebbdfd8a6b6cc Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Mon, 21 Aug 2023 17:10:22 -0500 Subject: [PATCH 25/49] use default printing format --- internal/mesh/internal/controllers/routes/controller.go | 4 ++-- .../mesh/internal/controllers/routes/loader/loader_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/mesh/internal/controllers/routes/controller.go b/internal/mesh/internal/controllers/routes/controller.go index 86c4c24cb31..bb17ad17def 100644 --- a/internal/mesh/internal/controllers/routes/controller.go +++ b/internal/mesh/internal/controllers/routes/controller.go @@ -49,7 +49,7 @@ func (r *routesReconciler) Reconcile(ctx context.Context, rt controller.Runtime, rt.Logger.Trace("reconciling computed routes") loggerFor := func(id *pbresource.ID) hclog.Logger { - return rt.Logger.With("resource-id", resource.IDToString(id)) + return rt.Logger.With("resource-id", id) } related, err := loader.LoadResourcesForComputedRoutes(ctx, loggerFor, rt.Client, r.mapper, req.ID) if err != nil { @@ -71,7 +71,7 @@ func (r *routesReconciler) Reconcile(ctx context.Context, rt controller.Runtime, for _, result := range generatedResults { computedRoutesID := result.ID - logger := rt.Logger.With("resource-id", resource.IDToString(computedRoutesID)) + logger := rt.Logger.With("resource-id", computedRoutesID) prev, err := resource.GetDecodedResource[*pbmesh.ComputedRoutes](ctx, rt.Client, computedRoutesID) if err != nil { diff --git a/internal/mesh/internal/controllers/routes/loader/loader_test.go b/internal/mesh/internal/controllers/routes/loader/loader_test.go index 7909eaba1cb..8981085ec14 100644 --- a/internal/mesh/internal/controllers/routes/loader/loader_test.go +++ b/internal/mesh/internal/controllers/routes/loader/loader_test.go @@ -35,7 +35,7 @@ func TestLoadResourcesForComputedRoutes(t *testing.T) { client := rtest.NewClient(rclient) loggerFor := func(id *pbresource.ID) hclog.Logger { - return rt.Logger.With("resource-id", resource.IDToString(id)) + return rt.Logger.With("resource-id", id) } mapper := xroutemapper.New() From 82538b4ad0ccef6654eb5468f79b41f79ea6a8d3 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Mon, 21 Aug 2023 17:17:50 -0500 Subject: [PATCH 26/49] early return --- .../internal/controllers/routes/generate.go | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/internal/mesh/internal/controllers/routes/generate.go b/internal/mesh/internal/controllers/routes/generate.go index 1f859eab4ee..49815b7d3bf 100644 --- a/internal/mesh/internal/controllers/routes/generate.go +++ b/internal/mesh/internal/controllers/routes/generate.go @@ -56,18 +56,32 @@ func compile( parentServiceRef := resource.Reference(parentServiceID, "") parentServiceDec := related.GetService(parentServiceID) + if parentServiceDec == nil { + return &ComputedRoutesResult{ + ID: computedRoutesID, + OwnerID: parentServiceID, + Data: nil, // returning nil signals a delete is requested + } + } + parentServiceID = parentServiceDec.Resource.Id // get ULID out of it - allowedPortProtocols := make(map[string]pbcatalog.Protocol) - inMesh := false - if parentServiceDec != nil { - parentServiceID = parentServiceDec.Resource.Id // get ULID out of it + var ( + inMesh = false + allowedPortProtocols = make(map[string]pbcatalog.Protocol) + ) + for _, port := range parentServiceDec.Data.Ports { + if port.Protocol == pbcatalog.Protocol_PROTOCOL_MESH { + inMesh = true + continue // skip + } + allowedPortProtocols[port.TargetPort] = port.Protocol + } - for _, port := range parentServiceDec.Data.Ports { - if port.Protocol == pbcatalog.Protocol_PROTOCOL_MESH { - inMesh = true - continue // skip - } - allowedPortProtocols[port.TargetPort] = port.Protocol + if !inMesh { + return &ComputedRoutesResult{ + ID: computedRoutesID, + OwnerID: parentServiceID, + Data: nil, // returning nil signals a delete is requested } } @@ -278,10 +292,6 @@ func compile( computedRoutes.PortedConfigs[port] = mc } - if !inMesh { - computedRoutes = nil - } - return &ComputedRoutesResult{ ID: computedRoutesID, OwnerID: parentServiceID, From b1f1c911361bda66e984daa027b9881bce1487a8 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Mon, 21 Aug 2023 17:23:02 -0500 Subject: [PATCH 27/49] update commentary --- .../mesh/internal/controllers/routes/ref_validation.go | 10 +++++----- .../internal/controllers/routes/ref_validation_test.go | 1 - 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/internal/mesh/internal/controllers/routes/ref_validation.go b/internal/mesh/internal/controllers/routes/ref_validation.go index 81a52dad967..55a28da00bf 100644 --- a/internal/mesh/internal/controllers/routes/ref_validation.go +++ b/internal/mesh/internal/controllers/routes/ref_validation.go @@ -41,14 +41,14 @@ func computeNewRouteRefConditions( ) []*pbresource.Condition { var conditions []*pbresource.Condition - // TODO: handle port numbers here too? the virtual port + // TODO(rb): handle port numbers here too if we are allowing those instead of the name? for _, parentRef := range parentRefs { if parentRef.Ref == nil || !resource.EqualType(parentRef.Ref.Type, catalog.ServiceType) { - continue + continue // not possible due to xRoute validation } if parentRef.Ref.Section != "" { - continue + continue // not possible due to xRoute validation } if svc := related.GetService(parentRef.Ref); svc != nil { found := false @@ -82,10 +82,10 @@ func computeNewRouteRefConditions( for _, backendRef := range backendRefs { if backendRef.Ref == nil || !resource.EqualType(backendRef.Ref.Type, catalog.ServiceType) { - continue + continue // not possible due to xRoute validation } if backendRef.Ref.Section != "" { - continue + continue // not possible due to xRoute validation } if svc := related.GetService(backendRef.Ref); svc != nil { found := false diff --git a/internal/mesh/internal/controllers/routes/ref_validation_test.go b/internal/mesh/internal/controllers/routes/ref_validation_test.go index 30f187de8e8..57e1c1ca22b 100644 --- a/internal/mesh/internal/controllers/routes/ref_validation_test.go +++ b/internal/mesh/internal/controllers/routes/ref_validation_test.go @@ -40,7 +40,6 @@ func TestComputeNewRouteRefConditions(t *testing.T) { require.NoError(t, err) return dec } - _ = newService t.Run("no refs", func(t *testing.T) { sg := newTestServiceGetter() From 53832305d29c9d64ce03933a30686d87694a1120 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Tue, 22 Aug 2023 10:13:57 -0500 Subject: [PATCH 28/49] checkpoint --- .../controllers/routes/controller_test.go | 6 -- .../controllers/routes/generate_test.go | 79 +++++++++++++++++++ .../controllers/routes/loader/related.go | 3 +- 3 files changed, 81 insertions(+), 7 deletions(-) create mode 100644 internal/mesh/internal/controllers/routes/generate_test.go diff --git a/internal/mesh/internal/controllers/routes/controller_test.go b/internal/mesh/internal/controllers/routes/controller_test.go index 8ab1341e9e2..929fff1fe2e 100644 --- a/internal/mesh/internal/controllers/routes/controller_test.go +++ b/internal/mesh/internal/controllers/routes/controller_test.go @@ -43,12 +43,6 @@ func (suite *controllerSuite) SetupTest() { } func (suite *controllerSuite) TestController() { - // TODO: tidy comment - // - // This test's purpose is to exercise the controller in a halfway realistic - // way. - - // Run the controller manager mgr := controller.NewManager(suite.client, suite.rt.Logger) mgr.Register(Controller()) mgr.SetRaftLeader(true) diff --git a/internal/mesh/internal/controllers/routes/generate_test.go b/internal/mesh/internal/controllers/routes/generate_test.go new file mode 100644 index 00000000000..f9d7a472619 --- /dev/null +++ b/internal/mesh/internal/controllers/routes/generate_test.go @@ -0,0 +1,79 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package routes + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/hashicorp/consul/internal/catalog" + "github.com/hashicorp/consul/internal/mesh/internal/controllers/routes/loader" + "github.com/hashicorp/consul/internal/mesh/internal/types" + rtest "github.com/hashicorp/consul/internal/resource/resourcetest" + "github.com/hashicorp/consul/proto/private/prototest" +) + +func TestGenerateComputedRoutes(t *testing.T) { + type testcase struct { + related *loader.RelatedResources + expect []*ComputedRoutesResult + expectPending int + } + + run := func(t *testing.T, tc testcase) { + pending := make(PendingStatuses) + + got := GenerateComputedRoutes(tc.related, pending) + require.Len(t, pending, tc.expectPending) + + prototest.AssertElementsMatch[*ComputedRoutesResult]( + t, tc.expect, got, + ) + } + + var ( + apiServiceID = rtest.Resource(catalog.ServiceType, "api").ID() + apiComputedRoutesID = rtest.Resource(types.ComputedRoutesType, "api").ID() + ) + + cases := map[string]testcase{ + "none": { + related: loader.NewRelatedResources(), + }, + "no aligned service": { + related: loader.NewRelatedResources(). + AddComputedRoutesIDs(apiComputedRoutesID). + AddResources( + apiServiceData := &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"api-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "tcp", Protocol: pbcatalog.Protocol_PROTOCOL_TCP}, + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + // {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + }, + } + ), + expect: []*ComputedRoutesResult{ + { + ID: apiComputedRoutesID, + OwnerID: apiServiceID, + Data: nil, + }, + }, + }, + "aligned service not in mesh": { + related: loader.NewRelatedResources(), + }, + } + // loader.NewRelatedResources().AddResources(nil).AddComputedRoutesIDs() + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + run(t, tc) + }) + } +} diff --git a/internal/mesh/internal/controllers/routes/loader/related.go b/internal/mesh/internal/controllers/routes/loader/related.go index 35640a55756..d051db2aa1f 100644 --- a/internal/mesh/internal/controllers/routes/loader/related.go +++ b/internal/mesh/internal/controllers/routes/loader/related.go @@ -44,11 +44,12 @@ func (r *RelatedResources) AddComputedRoutesIDs(list ...*pbresource.ID) *Related return r } -func (r *RelatedResources) AddComputedRoutesID(id *pbresource.ID) { +func (r *RelatedResources) AddComputedRoutesID(id *pbresource.ID) *RelatedResources { if !resource.EqualType(id.Type, types.ComputedRoutesType) { panic(fmt.Sprintf("expected *mesh.ComputedRoutes, not %s", resource.TypeToString(id.Type))) } r.ComputedRoutesList = append(r.ComputedRoutesList, id) + return r } func (r *RelatedResources) AddResources(list ...decodedResource) *RelatedResources { From 2de11ed3eab366202b7fb1a35f2fac763cd52348 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Tue, 22 Aug 2023 12:30:12 -0500 Subject: [PATCH 29/49] adding some generation tests outside of controller logic --- .../internal/controllers/routes/generate.go | 18 +- .../controllers/routes/generate_test.go | 244 +++++++++++++++--- 2 files changed, 213 insertions(+), 49 deletions(-) diff --git a/internal/mesh/internal/controllers/routes/generate.go b/internal/mesh/internal/controllers/routes/generate.go index 49815b7d3bf..cbfb7912d4f 100644 --- a/internal/mesh/internal/controllers/routes/generate.go +++ b/internal/mesh/internal/controllers/routes/generate.go @@ -32,9 +32,11 @@ func GenerateComputedRoutes( } type ComputedRoutesResult struct { - ID *pbresource.ID + // ID is always required. + ID *pbresource.ID + // OwnerID is only required on upserts. OwnerID *pbresource.ID - // If Data is empty it means delete if exists. + // Data being empty means delete if exists. Data *pbmesh.ComputedRoutes } @@ -58,9 +60,8 @@ func compile( parentServiceDec := related.GetService(parentServiceID) if parentServiceDec == nil { return &ComputedRoutesResult{ - ID: computedRoutesID, - OwnerID: parentServiceID, - Data: nil, // returning nil signals a delete is requested + ID: computedRoutesID, + Data: nil, // returning nil signals a delete is requested } } parentServiceID = parentServiceDec.Resource.Id // get ULID out of it @@ -79,9 +80,8 @@ func compile( if !inMesh { return &ComputedRoutesResult{ - ID: computedRoutesID, - OwnerID: parentServiceID, - Data: nil, // returning nil signals a delete is requested + ID: computedRoutesID, + Data: nil, // returning nil signals a delete is requested } } @@ -178,7 +178,7 @@ func compile( case pbcatalog.Protocol_PROTOCOL_TCP: typ = types.TCPRouteType default: - typ = types.TCPRouteType + continue // unknown protocol (impossible through validation) } routeNode := createDefaultRouteNode(parentServiceRef, port, typ) diff --git a/internal/mesh/internal/controllers/routes/generate_test.go b/internal/mesh/internal/controllers/routes/generate_test.go index f9d7a472619..43022bbae86 100644 --- a/internal/mesh/internal/controllers/routes/generate_test.go +++ b/internal/mesh/internal/controllers/routes/generate_test.go @@ -4,6 +4,7 @@ package routes import ( + "fmt" "testing" "github.com/stretchr/testify/require" @@ -11,69 +12,232 @@ import ( "github.com/hashicorp/consul/internal/catalog" "github.com/hashicorp/consul/internal/mesh/internal/controllers/routes/loader" "github.com/hashicorp/consul/internal/mesh/internal/types" + "github.com/hashicorp/consul/internal/resource" rtest "github.com/hashicorp/consul/internal/resource/resourcetest" + pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v1alpha1" + pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" "github.com/hashicorp/consul/proto/private/prototest" ) func TestGenerateComputedRoutes(t *testing.T) { - type testcase struct { - related *loader.RelatedResources - expect []*ComputedRoutesResult - expectPending int - } + registry := resource.NewRegistry() + types.Register(registry) + catalog.RegisterTypes(registry) - run := func(t *testing.T, tc testcase) { + run := func(t *testing.T, related *loader.RelatedResources, expect []*ComputedRoutesResult, expectPending int) { pending := make(PendingStatuses) - got := GenerateComputedRoutes(tc.related, pending) - require.Len(t, pending, tc.expectPending) + got := GenerateComputedRoutes(related, pending) + require.Len(t, pending, expectPending) prototest.AssertElementsMatch[*ComputedRoutesResult]( - t, tc.expect, got, + t, expect, got, ) } + newService := func(name string, data *pbcatalog.Service) *types.DecodedService { + svc := rtest.Resource(catalog.ServiceType, name). + WithData(t, data).Build() + rtest.ValidateAndNormalize(t, registry, svc) + return rtest.MustDecode[*pbcatalog.Service](t, svc) + } + + backendName := func(name, port string) string { + return fmt.Sprintf("catalog.v1alpha1.Service/default.local.default/%s?port=%s", name, port) + } + var ( apiServiceID = rtest.Resource(catalog.ServiceType, "api").ID() + apiServiceRef = resource.Reference(apiServiceID, "") apiComputedRoutesID = rtest.Resource(types.ComputedRoutesType, "api").ID() ) - cases := map[string]testcase{ - "none": { - related: loader.NewRelatedResources(), - }, - "no aligned service": { - related: loader.NewRelatedResources(). + t.Run("none", func(t *testing.T) { + run(t, loader.NewRelatedResources(), nil, 0) + }) + + t.Run("no aligned service", func(t *testing.T) { + related := loader.NewRelatedResources(). + AddComputedRoutesIDs(apiComputedRoutesID) + expect := []*ComputedRoutesResult{ + { + ID: apiComputedRoutesID, + Data: nil, + }, + } + run(t, related, expect, 0) + }) + + t.Run("aligned service not in mesh", func(t *testing.T) { + related := loader.NewRelatedResources(). + AddComputedRoutesIDs(apiComputedRoutesID). + AddResources(newService("api", &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"api-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "tcp", Protocol: pbcatalog.Protocol_PROTOCOL_TCP}, + }, + })) + expect := []*ComputedRoutesResult{ + { + ID: apiComputedRoutesID, + Data: nil, + }, + } + run(t, related, expect, 0) + }) + + t.Run("tcp service with default route", func(t *testing.T) { + apiServiceData := &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"api-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "tcp", Protocol: pbcatalog.Protocol_PROTOCOL_TCP}, + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + }, + } + + related := loader.NewRelatedResources(). + AddComputedRoutesIDs(apiComputedRoutesID). + AddResources(newService("api", apiServiceData)) + + expect := []*ComputedRoutesResult{ + { + ID: apiComputedRoutesID, + OwnerID: apiServiceID, + Data: &pbmesh.ComputedRoutes{ + PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ + "tcp": { + Config: &pbmesh.ComputedPortRoutes_Tcp{ + Tcp: &pbmesh.ComputedTCPRoute{ + ParentRef: newParentRef(apiServiceRef, "tcp"), + Rules: []*pbmesh.ComputedTCPRouteRule{{ + BackendRefs: []*pbmesh.ComputedTCPBackendRef{{ + BackendTarget: backendName("api", "tcp"), + }}, + }}, + }, + }, + UsingDefaultConfig: true, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("api", "tcp"): { + BackendRef: newBackendRef(apiServiceRef, "tcp", ""), + Service: apiServiceData, + }, + }, + }, + }, + }, + }, + } + run(t, related, expect, 0) + }) + + for protoName, protocol := range map[string]pbcatalog.Protocol{ + "http": pbcatalog.Protocol_PROTOCOL_HTTP, + "http2": pbcatalog.Protocol_PROTOCOL_HTTP2, + } { + t.Run(protoName+" service with default route", func(t *testing.T) { + apiServiceData := &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"api-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: protoName, Protocol: protocol}, + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + }, + } + + related := loader.NewRelatedResources(). AddComputedRoutesIDs(apiComputedRoutesID). - AddResources( - apiServiceData := &pbcatalog.Service{ - Workloads: &pbcatalog.WorkloadSelector{ - Prefixes: []string{"api-"}, - }, - Ports: []*pbcatalog.ServicePort{ - {TargetPort: "tcp", Protocol: pbcatalog.Protocol_PROTOCOL_TCP}, - {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, - // {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, - }, - } - ), - expect: []*ComputedRoutesResult{ + AddResources(newService("api", apiServiceData)) + + expect := []*ComputedRoutesResult{ { ID: apiComputedRoutesID, OwnerID: apiServiceID, - Data: nil, + Data: &pbmesh.ComputedRoutes{ + PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ + protoName: { + Config: &pbmesh.ComputedPortRoutes_Http{ + Http: &pbmesh.ComputedHTTPRoute{ + ParentRef: newParentRef(apiServiceRef, protoName), + Rules: []*pbmesh.ComputedHTTPRouteRule{{ + Matches: []*pbmesh.HTTPRouteMatch{{ + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, + Value: "/", + }, + }}, + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ + BackendTarget: backendName("api", protoName), + }}, + }}, + }, + }, + UsingDefaultConfig: true, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("api", protoName): { + BackendRef: newBackendRef(apiServiceRef, protoName, ""), + Service: apiServiceData, + }, + }, + }, + }, + }, }, - }, - }, - "aligned service not in mesh": { - related: loader.NewRelatedResources(), - }, - } - // loader.NewRelatedResources().AddResources(nil).AddComputedRoutesIDs() - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - run(t, tc) + } + run(t, related, expect, 0) }) } + + t.Run("grpc service with default route", func(t *testing.T) { + apiServiceData := &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"api-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "grpc", Protocol: pbcatalog.Protocol_PROTOCOL_GRPC}, + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + }, + } + + related := loader.NewRelatedResources(). + AddComputedRoutesIDs(apiComputedRoutesID). + AddResources(newService("api", apiServiceData)) + + expect := []*ComputedRoutesResult{ + { + ID: apiComputedRoutesID, + OwnerID: apiServiceID, + Data: &pbmesh.ComputedRoutes{ + PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ + "grpc": { + Config: &pbmesh.ComputedPortRoutes_Grpc{ + Grpc: &pbmesh.ComputedGRPCRoute{ + ParentRef: newParentRef(apiServiceRef, "grpc"), + Rules: []*pbmesh.ComputedGRPCRouteRule{{ + Matches: []*pbmesh.GRPCRouteMatch{{}}, + BackendRefs: []*pbmesh.ComputedGRPCBackendRef{{ + BackendTarget: backendName("api", "grpc"), + }}, + }}, + }, + }, + UsingDefaultConfig: true, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("api", "grpc"): { + BackendRef: newBackendRef(apiServiceRef, "grpc", ""), + Service: apiServiceData, + }, + }, + }, + }, + }, + }, + } + run(t, related, expect, 0) + }) } From 57e460e191cf82c974c6eb1b17d50166e9841839 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Tue, 22 Aug 2023 12:41:29 -0500 Subject: [PATCH 30/49] move deduplication into a test and rely upon controller framework to do this normally --- .../mesh/internal/controllers/routes/util.go | 8 ------- .../controllers/routes/xroutemapper/util.go | 23 ------------------ .../routes/xroutemapper/xroutemapper.go | 8 +++---- .../routes/xroutemapper/xroutemapper_test.go | 24 +++++++++++++++++++ 4 files changed, 28 insertions(+), 35 deletions(-) diff --git a/internal/mesh/internal/controllers/routes/util.go b/internal/mesh/internal/controllers/routes/util.go index 288a462ff09..0abd5f095a6 100644 --- a/internal/mesh/internal/controllers/routes/util.go +++ b/internal/mesh/internal/controllers/routes/util.go @@ -5,9 +5,6 @@ package routes import ( "google.golang.org/protobuf/proto" - - "github.com/hashicorp/consul/internal/controller" - "github.com/hashicorp/consul/internal/mesh/internal/controllers/routes/xroutemapper" ) func protoClone[T proto.Message](v T) T { @@ -24,8 +21,3 @@ func protoSliceClone[T proto.Message](in []T) []T { } return out } - -// Deprecated: xroutemapper.DeduplicateRequests -func deduplicate(reqs []controller.Request) []controller.Request { - return xroutemapper.DeduplicateRequests(reqs) -} diff --git a/internal/mesh/internal/controllers/routes/xroutemapper/util.go b/internal/mesh/internal/controllers/routes/xroutemapper/util.go index 3f14419c84d..e02131d7130 100644 --- a/internal/mesh/internal/controllers/routes/xroutemapper/util.go +++ b/internal/mesh/internal/controllers/routes/xroutemapper/util.go @@ -11,29 +11,6 @@ import ( "github.com/hashicorp/consul/proto-public/pbresource" ) -func DeduplicateRequests(reqs []controller.Request) []controller.Request { - out := make([]controller.Request, 0, len(reqs)) - seen := make(map[resID]struct{}) - - for _, req := range reqs { - rid := resID{ - ReferenceKey: resource.NewReferenceKey(req.ID), - UID: req.ID.Uid, - } - if _, ok := seen[rid]; !ok { - out = append(out, req) - seen[rid] = struct{}{} - } - } - - return out -} - -type resID struct { - resource.ReferenceKey - UID string -} - func parentRefSliceToRefSlice(parentRefs []*pbmesh.ParentReference) []resource.ReferenceOrID { if parentRefs == nil { return nil diff --git a/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper.go b/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper.go index 77e241e5b27..ce2e8bd755d 100644 --- a/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper.go +++ b/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper.go @@ -185,10 +185,10 @@ func mapXRouteToComputedRoutes[T interface { m.TrackXRoute(res.Id, route) - return DeduplicateRequests(makeControllerRequests( + return makeControllerRequests( types.ComputedRoutesType, parentRefSliceToRefSlice(route.GetParentRefs()), - )), nil + ), nil } func (m *Mapper) MapFailoverPolicy( @@ -267,7 +267,7 @@ func (m *Mapper) MapService( reqs = append(reqs, got...) } - return DeduplicateRequests(reqs), nil + return reqs, nil } // NOTE: this function does not interrogate down into failover policies @@ -302,5 +302,5 @@ func (m *Mapper) mapXRouteDirectServiceRefToComputedRoutesByID(svcID *pbresource )...) } - return DeduplicateRequests(out), nil + return out, nil } diff --git a/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper_test.go b/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper_test.go index d3b825099e9..169f6d7c096 100644 --- a/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper_test.go +++ b/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper_test.go @@ -557,6 +557,7 @@ func requireTracking( } require.NoError(t, err) + reqs = testDeduplicateRequests(reqs) require.Len(t, reqs, len(computedRoutesIDs)) for _, computedRoutesID := range computedRoutesIDs { require.NotNil(t, computedRoutesID) @@ -585,3 +586,26 @@ func defaultTenancy() *pbresource.Tenancy { PeerName: "local", } } + +func testDeduplicateRequests(reqs []controller.Request) []controller.Request { + type resID struct { + resource.ReferenceKey + UID string + } + + out := make([]controller.Request, 0, len(reqs)) + seen := make(map[resID]struct{}) + + for _, req := range reqs { + rid := resID{ + ReferenceKey: resource.NewReferenceKey(req.ID), + UID: req.ID.Uid, + } + if _, ok := seen[rid]; !ok { + out = append(out, req) + seen[rid] = struct{}{} + } + } + + return out +} From 9dfdde768ba6ec2f18bd1a67d2a4fcb445b9236b Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Tue, 22 Aug 2023 13:52:06 -0500 Subject: [PATCH 31/49] stuff --- .../controllers/routes/generate_test.go | 205 ++++++++++++++++++ 1 file changed, 205 insertions(+) diff --git a/internal/mesh/internal/controllers/routes/generate_test.go b/internal/mesh/internal/controllers/routes/generate_test.go index 43022bbae86..254ecf78e93 100644 --- a/internal/mesh/internal/controllers/routes/generate_test.go +++ b/internal/mesh/internal/controllers/routes/generate_test.go @@ -42,6 +42,25 @@ func TestGenerateComputedRoutes(t *testing.T) { return rtest.MustDecode[*pbcatalog.Service](t, svc) } + newHTTPRoute := func(name string, data *pbmesh.HTTPRoute) *types.DecodedHTTPRoute { + svc := rtest.Resource(types.HTTPRouteType, name). + WithData(t, data).Build() + rtest.ValidateAndNormalize(t, registry, svc) + return rtest.MustDecode[*pbmesh.HTTPRoute](t, svc) + } + newGRPCRoute := func(name string, data *pbmesh.GRPCRoute) *types.DecodedGRPCRoute { + svc := rtest.Resource(types.GRPCRouteType, name). + WithData(t, data).Build() + rtest.ValidateAndNormalize(t, registry, svc) + return rtest.MustDecode[*pbmesh.GRPCRoute](t, svc) + } + newTCPRoute := func(name string, data *pbmesh.TCPRoute) *types.DecodedTCPRoute { + svc := rtest.Resource(types.TCPRouteType, name). + WithData(t, data).Build() + rtest.ValidateAndNormalize(t, registry, svc) + return rtest.MustDecode[*pbmesh.TCPRoute](t, svc) + } + backendName := func(name, port string) string { return fmt.Sprintf("catalog.v1alpha1.Service/default.local.default/%s?port=%s", name, port) } @@ -50,6 +69,9 @@ func TestGenerateComputedRoutes(t *testing.T) { apiServiceID = rtest.Resource(catalog.ServiceType, "api").ID() apiServiceRef = resource.Reference(apiServiceID, "") apiComputedRoutesID = rtest.Resource(types.ComputedRoutesType, "api").ID() + + fooServiceID = rtest.Resource(catalog.ServiceType, "foo").ID() + fooServiceRef = resource.Reference(fooServiceID, "") ) t.Run("none", func(t *testing.T) { @@ -240,4 +262,187 @@ func TestGenerateComputedRoutes(t *testing.T) { } run(t, related, expect, 0) }) + + t.Run("all ports with a mix of routes", func(t *testing.T) { + apiServiceData := &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"api-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "tcp", Protocol: pbcatalog.Protocol_PROTOCOL_TCP}, + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + {TargetPort: "http2", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP2}, + {TargetPort: "grpc", Protocol: pbcatalog.Protocol_PROTOCOL_GRPC}, + }, + } + + fooServiceData := &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"foo-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "tcp", Protocol: pbcatalog.Protocol_PROTOCOL_TCP}, + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + {TargetPort: "http2", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP2}, + {TargetPort: "grpc", Protocol: pbcatalog.Protocol_PROTOCOL_GRPC}, + }, + } + + tcpRoute1 := &pbmesh.TCPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + newParentRef(newRef(catalog.ServiceType, "api"), "tcp"), + }, + Rules: []*pbmesh.TCPRouteRule{{ + BackendRefs: []*pbmesh.TCPBackendRef{{ + BackendRef: newBackendRef(fooServiceRef, "", ""), + }}, + }}, + } + + httpRoute1 := &pbmesh.HTTPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + newParentRef(newRef(catalog.ServiceType, "api"), "http"), + newParentRef(newRef(catalog.ServiceType, "api"), "http2"), + }, + Rules: []*pbmesh.HTTPRouteRule{{ + BackendRefs: []*pbmesh.HTTPBackendRef{{ + BackendRef: newBackendRef(fooServiceRef, "", ""), + }}, + }}, + } + + grpcRoute1 := &pbmesh.GRPCRoute{ + ParentRefs: []*pbmesh.ParentReference{ + newParentRef(newRef(catalog.ServiceType, "api"), "grpc"), + }, + Rules: []*pbmesh.GRPCRouteRule{{ + BackendRefs: []*pbmesh.GRPCBackendRef{{ + BackendRef: newBackendRef(fooServiceRef, "", ""), + }}, + }}, + } + + related := loader.NewRelatedResources(). + AddComputedRoutesIDs(apiComputedRoutesID). + AddResources( + newService("api", apiServiceData), + newService("foo", fooServiceData), + newTCPRoute("api-tcp-route1", tcpRoute1), + newHTTPRoute("api-http-route1", httpRoute1), + newGRPCRoute("api-grpc-route1", grpcRoute1), + ) + + expect := []*ComputedRoutesResult{ + { + ID: apiComputedRoutesID, + OwnerID: apiServiceID, + Data: &pbmesh.ComputedRoutes{ + PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ + "tcp": { + Config: &pbmesh.ComputedPortRoutes_Tcp{ + Tcp: &pbmesh.ComputedTCPRoute{ + ParentRef: newParentRef(apiServiceRef, "tcp"), + Rules: []*pbmesh.ComputedTCPRouteRule{{ + BackendRefs: []*pbmesh.ComputedTCPBackendRef{{ + BackendTarget: backendName("foo", "tcp"), + }}, + }}, + }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("foo", "tcp"): { + BackendRef: newBackendRef(fooServiceRef, "tcp", ""), + Service: fooServiceData, + }, + }, + }, + "http": { + Config: &pbmesh.ComputedPortRoutes_Http{ + Http: &pbmesh.ComputedHTTPRoute{ + ParentRef: newParentRef(apiServiceRef, "http"), + Rules: []*pbmesh.ComputedHTTPRouteRule{ + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ + BackendTarget: backendName("foo", "http"), + }}, + }, + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, + }, + }, + }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("foo", "http"): { + BackendRef: newBackendRef(fooServiceRef, "http", ""), + Service: fooServiceData, + }, + }, + }, + "http2": { + Config: &pbmesh.ComputedPortRoutes_Http{ + Http: &pbmesh.ComputedHTTPRoute{ + ParentRef: newParentRef(apiServiceRef, "http2"), + Rules: []*pbmesh.ComputedHTTPRouteRule{ + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ + BackendTarget: backendName("foo", "http2"), + }}, + }, + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, + }, + }, + }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("foo", "http2"): { + BackendRef: newBackendRef(fooServiceRef, "http2", ""), + Service: fooServiceData, + }, + }, + }, + "grpc": { + Config: &pbmesh.ComputedPortRoutes_Grpc{ + Grpc: &pbmesh.ComputedGRPCRoute{ + ParentRef: newParentRef(apiServiceRef, "grpc"), + Rules: []*pbmesh.ComputedGRPCRouteRule{ + { + Matches: []*pbmesh.GRPCRouteMatch{{}}, + BackendRefs: []*pbmesh.ComputedGRPCBackendRef{{ + BackendTarget: backendName("foo", "grpc"), + }}, + }, + { + Matches: []*pbmesh.GRPCRouteMatch{{}}, + BackendRefs: []*pbmesh.ComputedGRPCBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, + }, + }, + }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("foo", "grpc"): { + BackendRef: newBackendRef(fooServiceRef, "grpc", ""), + Service: fooServiceData, + }, + }, + }, + }, + }, + }, + } + run(t, related, expect, 0) + }) } From bb3dca38678acacb7bc91e732da10e009f2e3cec Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Tue, 22 Aug 2023 13:58:02 -0500 Subject: [PATCH 32/49] more tests --- .../controllers/routes/generate_test.go | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/internal/mesh/internal/controllers/routes/generate_test.go b/internal/mesh/internal/controllers/routes/generate_test.go index 254ecf78e93..f311858cae9 100644 --- a/internal/mesh/internal/controllers/routes/generate_test.go +++ b/internal/mesh/internal/controllers/routes/generate_test.go @@ -445,4 +445,97 @@ func TestGenerateComputedRoutes(t *testing.T) { } run(t, related, expect, 0) }) + + t.Run("all ports with a wildcard route only bypassing the protocol", func(t *testing.T) { + apiServiceData := &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"api-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "tcp", Protocol: pbcatalog.Protocol_PROTOCOL_TCP}, + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + {TargetPort: "http2", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP2}, + {TargetPort: "grpc", Protocol: pbcatalog.Protocol_PROTOCOL_GRPC}, + }, + } + + fooServiceData := &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"foo-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "tcp", Protocol: pbcatalog.Protocol_PROTOCOL_TCP}, + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + {TargetPort: "http2", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP2}, + {TargetPort: "grpc", Protocol: pbcatalog.Protocol_PROTOCOL_GRPC}, + }, + } + + httpRoute1 := &pbmesh.HTTPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + newParentRef(newRef(catalog.ServiceType, "api"), ""), + }, + Rules: []*pbmesh.HTTPRouteRule{{ + BackendRefs: []*pbmesh.HTTPBackendRef{{ + BackendRef: newBackendRef(fooServiceRef, "", ""), + }}, + }}, + } + + related := loader.NewRelatedResources(). + AddComputedRoutesIDs(apiComputedRoutesID). + AddResources( + newService("api", apiServiceData), + newService("foo", fooServiceData), + newHTTPRoute("api-http-route1", httpRoute1), + ) + + chunk := func(portName string) *pbmesh.ComputedPortRoutes { + return &pbmesh.ComputedPortRoutes{ + Config: &pbmesh.ComputedPortRoutes_Http{ + Http: &pbmesh.ComputedHTTPRoute{ + ParentRef: newParentRef(apiServiceRef, portName), + Rules: []*pbmesh.ComputedHTTPRouteRule{ + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ + BackendTarget: backendName("foo", portName), + }}, + }, + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, + }, + }, + }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("foo", portName): { + BackendRef: newBackendRef(fooServiceRef, portName, ""), + Service: fooServiceData, + }, + }, + } + } + + expect := []*ComputedRoutesResult{ + { + ID: apiComputedRoutesID, + OwnerID: apiServiceID, + Data: &pbmesh.ComputedRoutes{ + PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ + "tcp": chunk("tcp"), + "http": chunk("http"), + "http2": chunk("http2"), + "grpc": chunk("grpc"), + }, + }, + }, + } + run(t, related, expect, 0) + }) } From 95be76d76e6e36fc559730f2bdb3a9d228a7e49a Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Tue, 22 Aug 2023 16:11:14 -0500 Subject: [PATCH 33/49] change indent --- .../controllers/routes/generate_test.go | 344 +++++++++--------- 1 file changed, 166 insertions(+), 178 deletions(-) diff --git a/internal/mesh/internal/controllers/routes/generate_test.go b/internal/mesh/internal/controllers/routes/generate_test.go index f311858cae9..6826481ff99 100644 --- a/internal/mesh/internal/controllers/routes/generate_test.go +++ b/internal/mesh/internal/controllers/routes/generate_test.go @@ -101,12 +101,10 @@ func TestGenerateComputedRoutes(t *testing.T) { {TargetPort: "tcp", Protocol: pbcatalog.Protocol_PROTOCOL_TCP}, }, })) - expect := []*ComputedRoutesResult{ - { - ID: apiComputedRoutesID, - Data: nil, - }, - } + expect := []*ComputedRoutesResult{{ + ID: apiComputedRoutesID, + Data: nil, + }} run(t, related, expect, 0) }) @@ -125,34 +123,32 @@ func TestGenerateComputedRoutes(t *testing.T) { AddComputedRoutesIDs(apiComputedRoutesID). AddResources(newService("api", apiServiceData)) - expect := []*ComputedRoutesResult{ - { - ID: apiComputedRoutesID, - OwnerID: apiServiceID, - Data: &pbmesh.ComputedRoutes{ - PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ - "tcp": { - Config: &pbmesh.ComputedPortRoutes_Tcp{ - Tcp: &pbmesh.ComputedTCPRoute{ - ParentRef: newParentRef(apiServiceRef, "tcp"), - Rules: []*pbmesh.ComputedTCPRouteRule{{ - BackendRefs: []*pbmesh.ComputedTCPBackendRef{{ - BackendTarget: backendName("api", "tcp"), - }}, + expect := []*ComputedRoutesResult{{ + ID: apiComputedRoutesID, + OwnerID: apiServiceID, + Data: &pbmesh.ComputedRoutes{ + PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ + "tcp": { + Config: &pbmesh.ComputedPortRoutes_Tcp{ + Tcp: &pbmesh.ComputedTCPRoute{ + ParentRef: newParentRef(apiServiceRef, "tcp"), + Rules: []*pbmesh.ComputedTCPRouteRule{{ + BackendRefs: []*pbmesh.ComputedTCPBackendRef{{ + BackendTarget: backendName("api", "tcp"), }}, - }, + }}, }, - UsingDefaultConfig: true, - Targets: map[string]*pbmesh.BackendTargetDetails{ - backendName("api", "tcp"): { - BackendRef: newBackendRef(apiServiceRef, "tcp", ""), - Service: apiServiceData, - }, + }, + UsingDefaultConfig: true, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("api", "tcp"): { + BackendRef: newBackendRef(apiServiceRef, "tcp", ""), + Service: apiServiceData, }, }, }, }, - }, + }}, } run(t, related, expect, 0) }) @@ -176,41 +172,39 @@ func TestGenerateComputedRoutes(t *testing.T) { AddComputedRoutesIDs(apiComputedRoutesID). AddResources(newService("api", apiServiceData)) - expect := []*ComputedRoutesResult{ - { - ID: apiComputedRoutesID, - OwnerID: apiServiceID, - Data: &pbmesh.ComputedRoutes{ - PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ - protoName: { - Config: &pbmesh.ComputedPortRoutes_Http{ - Http: &pbmesh.ComputedHTTPRoute{ - ParentRef: newParentRef(apiServiceRef, protoName), - Rules: []*pbmesh.ComputedHTTPRouteRule{{ - Matches: []*pbmesh.HTTPRouteMatch{{ - Path: &pbmesh.HTTPPathMatch{ - Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, - Value: "/", - }, - }}, - BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ - BackendTarget: backendName("api", protoName), - }}, + expect := []*ComputedRoutesResult{{ + ID: apiComputedRoutesID, + OwnerID: apiServiceID, + Data: &pbmesh.ComputedRoutes{ + PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ + protoName: { + Config: &pbmesh.ComputedPortRoutes_Http{ + Http: &pbmesh.ComputedHTTPRoute{ + ParentRef: newParentRef(apiServiceRef, protoName), + Rules: []*pbmesh.ComputedHTTPRouteRule{{ + Matches: []*pbmesh.HTTPRouteMatch{{ + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, + Value: "/", + }, }}, - }, + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ + BackendTarget: backendName("api", protoName), + }}, + }}, }, - UsingDefaultConfig: true, - Targets: map[string]*pbmesh.BackendTargetDetails{ - backendName("api", protoName): { - BackendRef: newBackendRef(apiServiceRef, protoName, ""), - Service: apiServiceData, - }, + }, + UsingDefaultConfig: true, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("api", protoName): { + BackendRef: newBackendRef(apiServiceRef, protoName, ""), + Service: apiServiceData, }, }, }, }, }, - } + }} run(t, related, expect, 0) }) } @@ -230,36 +224,34 @@ func TestGenerateComputedRoutes(t *testing.T) { AddComputedRoutesIDs(apiComputedRoutesID). AddResources(newService("api", apiServiceData)) - expect := []*ComputedRoutesResult{ - { - ID: apiComputedRoutesID, - OwnerID: apiServiceID, - Data: &pbmesh.ComputedRoutes{ - PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ - "grpc": { - Config: &pbmesh.ComputedPortRoutes_Grpc{ - Grpc: &pbmesh.ComputedGRPCRoute{ - ParentRef: newParentRef(apiServiceRef, "grpc"), - Rules: []*pbmesh.ComputedGRPCRouteRule{{ - Matches: []*pbmesh.GRPCRouteMatch{{}}, - BackendRefs: []*pbmesh.ComputedGRPCBackendRef{{ - BackendTarget: backendName("api", "grpc"), - }}, + expect := []*ComputedRoutesResult{{ + ID: apiComputedRoutesID, + OwnerID: apiServiceID, + Data: &pbmesh.ComputedRoutes{ + PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ + "grpc": { + Config: &pbmesh.ComputedPortRoutes_Grpc{ + Grpc: &pbmesh.ComputedGRPCRoute{ + ParentRef: newParentRef(apiServiceRef, "grpc"), + Rules: []*pbmesh.ComputedGRPCRouteRule{{ + Matches: []*pbmesh.GRPCRouteMatch{{}}, + BackendRefs: []*pbmesh.ComputedGRPCBackendRef{{ + BackendTarget: backendName("api", "grpc"), }}, - }, + }}, }, - UsingDefaultConfig: true, - Targets: map[string]*pbmesh.BackendTargetDetails{ - backendName("api", "grpc"): { - BackendRef: newBackendRef(apiServiceRef, "grpc", ""), - Service: apiServiceData, - }, + }, + UsingDefaultConfig: true, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("api", "grpc"): { + BackendRef: newBackendRef(apiServiceRef, "grpc", ""), + Service: apiServiceData, }, }, }, }, }, - } + }} run(t, related, expect, 0) }) @@ -334,115 +326,113 @@ func TestGenerateComputedRoutes(t *testing.T) { newGRPCRoute("api-grpc-route1", grpcRoute1), ) - expect := []*ComputedRoutesResult{ - { - ID: apiComputedRoutesID, - OwnerID: apiServiceID, - Data: &pbmesh.ComputedRoutes{ - PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ - "tcp": { - Config: &pbmesh.ComputedPortRoutes_Tcp{ - Tcp: &pbmesh.ComputedTCPRoute{ - ParentRef: newParentRef(apiServiceRef, "tcp"), - Rules: []*pbmesh.ComputedTCPRouteRule{{ - BackendRefs: []*pbmesh.ComputedTCPBackendRef{{ - BackendTarget: backendName("foo", "tcp"), - }}, + expect := []*ComputedRoutesResult{{ + ID: apiComputedRoutesID, + OwnerID: apiServiceID, + Data: &pbmesh.ComputedRoutes{ + PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ + "tcp": { + Config: &pbmesh.ComputedPortRoutes_Tcp{ + Tcp: &pbmesh.ComputedTCPRoute{ + ParentRef: newParentRef(apiServiceRef, "tcp"), + Rules: []*pbmesh.ComputedTCPRouteRule{{ + BackendRefs: []*pbmesh.ComputedTCPBackendRef{{ + BackendTarget: backendName("foo", "tcp"), }}, - }, + }}, }, - Targets: map[string]*pbmesh.BackendTargetDetails{ - backendName("foo", "tcp"): { - BackendRef: newBackendRef(fooServiceRef, "tcp", ""), - Service: fooServiceData, - }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("foo", "tcp"): { + BackendRef: newBackendRef(fooServiceRef, "tcp", ""), + Service: fooServiceData, }, }, - "http": { - Config: &pbmesh.ComputedPortRoutes_Http{ - Http: &pbmesh.ComputedHTTPRoute{ - ParentRef: newParentRef(apiServiceRef, "http"), - Rules: []*pbmesh.ComputedHTTPRouteRule{ - { - Matches: defaultHTTPRouteMatches(), - BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ - BackendTarget: backendName("foo", "http"), - }}, - }, - { - Matches: defaultHTTPRouteMatches(), - BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ - BackendTarget: types.NullRouteBackend, - }}, - }, + }, + "http": { + Config: &pbmesh.ComputedPortRoutes_Http{ + Http: &pbmesh.ComputedHTTPRoute{ + ParentRef: newParentRef(apiServiceRef, "http"), + Rules: []*pbmesh.ComputedHTTPRouteRule{ + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ + BackendTarget: backendName("foo", "http"), + }}, + }, + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, }, }, }, - Targets: map[string]*pbmesh.BackendTargetDetails{ - backendName("foo", "http"): { - BackendRef: newBackendRef(fooServiceRef, "http", ""), - Service: fooServiceData, - }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("foo", "http"): { + BackendRef: newBackendRef(fooServiceRef, "http", ""), + Service: fooServiceData, }, }, - "http2": { - Config: &pbmesh.ComputedPortRoutes_Http{ - Http: &pbmesh.ComputedHTTPRoute{ - ParentRef: newParentRef(apiServiceRef, "http2"), - Rules: []*pbmesh.ComputedHTTPRouteRule{ - { - Matches: defaultHTTPRouteMatches(), - BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ - BackendTarget: backendName("foo", "http2"), - }}, - }, - { - Matches: defaultHTTPRouteMatches(), - BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ - BackendTarget: types.NullRouteBackend, - }}, - }, + }, + "http2": { + Config: &pbmesh.ComputedPortRoutes_Http{ + Http: &pbmesh.ComputedHTTPRoute{ + ParentRef: newParentRef(apiServiceRef, "http2"), + Rules: []*pbmesh.ComputedHTTPRouteRule{ + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ + BackendTarget: backendName("foo", "http2"), + }}, + }, + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, }, }, }, - Targets: map[string]*pbmesh.BackendTargetDetails{ - backendName("foo", "http2"): { - BackendRef: newBackendRef(fooServiceRef, "http2", ""), - Service: fooServiceData, - }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("foo", "http2"): { + BackendRef: newBackendRef(fooServiceRef, "http2", ""), + Service: fooServiceData, }, }, - "grpc": { - Config: &pbmesh.ComputedPortRoutes_Grpc{ - Grpc: &pbmesh.ComputedGRPCRoute{ - ParentRef: newParentRef(apiServiceRef, "grpc"), - Rules: []*pbmesh.ComputedGRPCRouteRule{ - { - Matches: []*pbmesh.GRPCRouteMatch{{}}, - BackendRefs: []*pbmesh.ComputedGRPCBackendRef{{ - BackendTarget: backendName("foo", "grpc"), - }}, - }, - { - Matches: []*pbmesh.GRPCRouteMatch{{}}, - BackendRefs: []*pbmesh.ComputedGRPCBackendRef{{ - BackendTarget: types.NullRouteBackend, - }}, - }, + }, + "grpc": { + Config: &pbmesh.ComputedPortRoutes_Grpc{ + Grpc: &pbmesh.ComputedGRPCRoute{ + ParentRef: newParentRef(apiServiceRef, "grpc"), + Rules: []*pbmesh.ComputedGRPCRouteRule{ + { + Matches: []*pbmesh.GRPCRouteMatch{{}}, + BackendRefs: []*pbmesh.ComputedGRPCBackendRef{{ + BackendTarget: backendName("foo", "grpc"), + }}, + }, + { + Matches: []*pbmesh.GRPCRouteMatch{{}}, + BackendRefs: []*pbmesh.ComputedGRPCBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, }, }, }, - Targets: map[string]*pbmesh.BackendTargetDetails{ - backendName("foo", "grpc"): { - BackendRef: newBackendRef(fooServiceRef, "grpc", ""), - Service: fooServiceData, - }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("foo", "grpc"): { + BackendRef: newBackendRef(fooServiceRef, "grpc", ""), + Service: fooServiceData, }, }, }, }, }, - } + }} run(t, related, expect, 0) }) @@ -522,20 +512,18 @@ func TestGenerateComputedRoutes(t *testing.T) { } } - expect := []*ComputedRoutesResult{ - { - ID: apiComputedRoutesID, - OwnerID: apiServiceID, - Data: &pbmesh.ComputedRoutes{ - PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ - "tcp": chunk("tcp"), - "http": chunk("http"), - "http2": chunk("http2"), - "grpc": chunk("grpc"), - }, + expect := []*ComputedRoutesResult{{ + ID: apiComputedRoutesID, + OwnerID: apiServiceID, + Data: &pbmesh.ComputedRoutes{ + PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ + "tcp": chunk("tcp"), + "http": chunk("http"), + "http2": chunk("http2"), + "grpc": chunk("grpc"), }, }, - } + }} run(t, related, expect, 0) }) } From fc61ea7b61c47b12081f28ffd96d33ae8250d4c3 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Tue, 22 Aug 2023 16:32:53 -0500 Subject: [PATCH 34/49] add more coverage to just the generate.go file --- .../internal/controllers/routes/generate.go | 76 +- .../controllers/routes/generate_test.go | 1005 ++++++++++++++++- 2 files changed, 1016 insertions(+), 65 deletions(-) diff --git a/internal/mesh/internal/controllers/routes/generate.go b/internal/mesh/internal/controllers/routes/generate.go index cbfb7912d4f..3eaf85a2e1e 100644 --- a/internal/mesh/internal/controllers/routes/generate.go +++ b/internal/mesh/internal/controllers/routes/generate.go @@ -4,6 +4,8 @@ package routes import ( + "fmt" + "github.com/hashicorp/consul/internal/catalog" "github.com/hashicorp/consul/internal/mesh/internal/controllers/routes/loader" "github.com/hashicorp/consul/internal/mesh/internal/types" @@ -102,11 +104,13 @@ func compile( ) for _, ref := range xroute.GetParentRefs() { if resource.ReferenceOrIDMatch(ref.Ref, parentServiceRef) { - ports = append(ports, ref.Port) if ref.Port == "" { wildcardedPort = true break } + if _, ok := allowedPortProtocols[ref.Port]; ok { + ports = append(ports, ref.Port) + } } } @@ -127,37 +131,16 @@ func compile( panic("impossible to have an empty port here") } - // Check if the user provided port is actually valid. - nullRouteTraffic := (parentServiceDec == nil) - if _, ok := allowedPortProtocols[port]; !ok { - nullRouteTraffic = true - } - var node *inputRouteNode switch route := xroute.(type) { case *pbmesh.HTTPRoute: - if nullRouteTraffic { - node = newInputRouteNode(port) - setupDefaultHTTPRouteNode(node, types.NullRouteBackend) - } else { - node = compileHTTPRouteNode(port, res, route, related) - } + node = compileHTTPRouteNode(port, res, route, related) case *pbmesh.GRPCRoute: - if nullRouteTraffic { - node = newInputRouteNode(port) - setupDefaultGRPCRouteNode(node, types.NullRouteBackend) - } else { - node = compileGRPCRouteNode(port, res, route, related) - } + node = compileGRPCRouteNode(port, res, route, related) case *pbmesh.TCPRoute: - if nullRouteTraffic { - node = newInputRouteNode(port) - setupDefaultTCPRouteNode(node, types.NullRouteBackend) - } else { - node = compileTCPRouteNode(port, res, route, related) - } + node = compileTCPRouteNode(port, res, route, related) default: - return // unknown xroute type (impossible) + panic(fmt.Sprintf("unexpected xroute type: %T", xroute)) } routeNodesByPort[node.ParentPort] = append(routeNodesByPort[node.ParentPort], node) @@ -168,6 +151,8 @@ func compile( for port, protocol := range allowedPortProtocols { if _, ok := routeNodesByPort[port]; !ok { var typ *pbresource.Type + + // enumcover:pbcatalog.Protocol switch protocol { case pbcatalog.Protocol_PROTOCOL_HTTP2: typ = types.HTTPRouteType @@ -177,8 +162,10 @@ func compile( typ = types.GRPCRouteType case pbcatalog.Protocol_PROTOCOL_TCP: typ = types.TCPRouteType + case pbcatalog.Protocol_PROTOCOL_MESH: + fallthrough // to default default: - continue // unknown protocol (impossible through validation) + continue // not possible } routeNode := createDefaultRouteNode(parentServiceRef, port, typ) @@ -271,7 +258,7 @@ func compile( svc := related.GetService(svcRef) failoverPolicy := related.GetFailoverPolicyForService(svcRef) - destConfig := related.GetDestinationPolicy(svcRef) + destConfig := related.GetDestinationPolicyForService(svcRef) if svc == nil { panic("impossible at this point; should already have been handled before getting here") @@ -535,13 +522,16 @@ func createDefaultRouteNode( }) switch { case resource.EqualType(types.HTTPRouteType, typ): - setupDefaultHTTPRouteNode(routeNode, defaultBackendTarget) + routeNode.RouteType = types.HTTPRouteType + appendDefaultHTTPRouteRule(routeNode, defaultBackendTarget) case resource.EqualType(types.GRPCRouteType, typ): - setupDefaultGRPCRouteNode(routeNode, defaultBackendTarget) + routeNode.RouteType = types.GRPCRouteType + appendDefaultGRPCRouteRule(routeNode, defaultBackendTarget) case resource.EqualType(types.TCPRouteType, typ): fallthrough default: - setupDefaultTCPRouteNode(routeNode, defaultBackendTarget) + routeNode.RouteType = types.TCPRouteType + appendDefaultTCPRouteRule(routeNode, defaultBackendTarget) } routeNode.Default = true @@ -564,14 +554,6 @@ func appendDefaultRouteNode( } } -func setupDefaultHTTPRouteNode( - routeNode *inputRouteNode, - defaultBackendTarget string, -) { - routeNode.RouteType = types.HTTPRouteType - appendDefaultHTTPRouteRule(routeNode, defaultBackendTarget) -} - func appendDefaultHTTPRouteRule( routeNode *inputRouteNode, backendTarget string, @@ -584,14 +566,6 @@ func appendDefaultHTTPRouteRule( }) } -func setupDefaultGRPCRouteNode( - routeNode *inputRouteNode, - defaultBackendTarget string, -) { - routeNode.RouteType = types.GRPCRouteType - appendDefaultGRPCRouteRule(routeNode, defaultBackendTarget) -} - func appendDefaultGRPCRouteRule( routeNode *inputRouteNode, backendTarget string, @@ -604,14 +578,6 @@ func appendDefaultGRPCRouteRule( }) } -func setupDefaultTCPRouteNode( - routeNode *inputRouteNode, - defaultBackendTarget string, -) { - routeNode.RouteType = types.TCPRouteType - appendDefaultTCPRouteRule(routeNode, defaultBackendTarget) -} - func appendDefaultTCPRouteRule( routeNode *inputRouteNode, backendTarget string, diff --git a/internal/mesh/internal/controllers/routes/generate_test.go b/internal/mesh/internal/controllers/routes/generate_test.go index 6826481ff99..ecf83da7bfd 100644 --- a/internal/mesh/internal/controllers/routes/generate_test.go +++ b/internal/mesh/internal/controllers/routes/generate_test.go @@ -6,8 +6,11 @@ package routes import ( "fmt" "testing" + "time" "github.com/stretchr/testify/require" + "google.golang.org/protobuf/types/known/anypb" + "google.golang.org/protobuf/types/known/durationpb" "github.com/hashicorp/consul/internal/catalog" "github.com/hashicorp/consul/internal/mesh/internal/controllers/routes/loader" @@ -16,6 +19,7 @@ import ( rtest "github.com/hashicorp/consul/internal/resource/resourcetest" pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v1alpha1" pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" + "github.com/hashicorp/consul/proto-public/pbresource" "github.com/hashicorp/consul/proto/private/prototest" ) @@ -24,15 +28,32 @@ func TestGenerateComputedRoutes(t *testing.T) { types.Register(registry) catalog.RegisterTypes(registry) - run := func(t *testing.T, related *loader.RelatedResources, expect []*ComputedRoutesResult, expectPending int) { + run := func( + t *testing.T, + related *loader.RelatedResources, + expect []*ComputedRoutesResult, + expectPending PendingStatuses, + ) { pending := make(PendingStatuses) got := GenerateComputedRoutes(related, pending) - require.Len(t, pending, expectPending) prototest.AssertElementsMatch[*ComputedRoutesResult]( t, expect, got, ) + + require.Len(t, pending, len(expectPending)) + if len(expectPending) > 0 { + for rk, expectItem := range expectPending { + gotItem, ok := pending[rk] + require.True(t, ok, "missing expected pending status for %v", rk) + prototest.AssertDeepEqual(t, expectItem, gotItem) + } + for rk, _ := range pending { + _, ok := expectPending[rk] + require.True(t, ok, "extra pending status for %v", rk) + } + } } newService := func(name string, data *pbcatalog.Service) *types.DecodedService { @@ -61,6 +82,20 @@ func TestGenerateComputedRoutes(t *testing.T) { return rtest.MustDecode[*pbmesh.TCPRoute](t, svc) } + newDestPolicy := func(name string, data *pbmesh.DestinationPolicy) *types.DecodedDestinationPolicy { + policy := rtest.Resource(types.DestinationPolicyType, name). + WithData(t, data).Build() + rtest.ValidateAndNormalize(t, registry, policy) + return rtest.MustDecode[*pbmesh.DestinationPolicy](t, policy) + } + + newFailPolicy := func(name string, data *pbcatalog.FailoverPolicy) *types.DecodedFailoverPolicy { + policy := rtest.Resource(catalog.FailoverPolicyType, name). + WithData(t, data).Build() + rtest.ValidateAndNormalize(t, registry, policy) + return rtest.MustDecode[*pbcatalog.FailoverPolicy](t, policy) + } + backendName := func(name, port string) string { return fmt.Sprintf("catalog.v1alpha1.Service/default.local.default/%s?port=%s", name, port) } @@ -72,10 +107,13 @@ func TestGenerateComputedRoutes(t *testing.T) { fooServiceID = rtest.Resource(catalog.ServiceType, "foo").ID() fooServiceRef = resource.Reference(fooServiceID, "") + + barServiceID = rtest.Resource(catalog.ServiceType, "bar").ID() + barServiceRef = resource.Reference(barServiceID, "") ) t.Run("none", func(t *testing.T) { - run(t, loader.NewRelatedResources(), nil, 0) + run(t, loader.NewRelatedResources(), nil, nil) }) t.Run("no aligned service", func(t *testing.T) { @@ -87,7 +125,7 @@ func TestGenerateComputedRoutes(t *testing.T) { Data: nil, }, } - run(t, related, expect, 0) + run(t, related, expect, nil) }) t.Run("aligned service not in mesh", func(t *testing.T) { @@ -105,7 +143,7 @@ func TestGenerateComputedRoutes(t *testing.T) { ID: apiComputedRoutesID, Data: nil, }} - run(t, related, expect, 0) + run(t, related, expect, nil) }) t.Run("tcp service with default route", func(t *testing.T) { @@ -150,7 +188,7 @@ func TestGenerateComputedRoutes(t *testing.T) { }, }}, } - run(t, related, expect, 0) + run(t, related, expect, nil) }) for protoName, protocol := range map[string]pbcatalog.Protocol{ @@ -205,7 +243,7 @@ func TestGenerateComputedRoutes(t *testing.T) { }, }, }} - run(t, related, expect, 0) + run(t, related, expect, nil) }) } @@ -252,7 +290,7 @@ func TestGenerateComputedRoutes(t *testing.T) { }, }, }} - run(t, related, expect, 0) + run(t, related, expect, nil) }) t.Run("all ports with a mix of routes", func(t *testing.T) { @@ -433,7 +471,7 @@ func TestGenerateComputedRoutes(t *testing.T) { }, }, }} - run(t, related, expect, 0) + run(t, related, expect, nil) }) t.Run("all ports with a wildcard route only bypassing the protocol", func(t *testing.T) { @@ -524,6 +562,953 @@ func TestGenerateComputedRoutes(t *testing.T) { }, }, }} - run(t, related, expect, 0) + run(t, related, expect, nil) + }) + + t.Run("stale-weird: stale mapper causes visit of irrelevant xRoute", func(t *testing.T) { + apiServiceData := &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"api-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + }, + } + + fooServiceData := &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"foo-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + }, + } + + httpRoute1 := &pbmesh.HTTPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + newParentRef(newRef(catalog.ServiceType, "api"), ""), + }, + Rules: []*pbmesh.HTTPRouteRule{{ + BackendRefs: []*pbmesh.HTTPBackendRef{{ + BackendRef: newBackendRef(fooServiceRef, "", ""), + }}, + }}, + } + + apiSvc := newService("api", apiServiceData) + fooSvc := newService("foo", fooServiceData) + apiHTTPRoute1 := newHTTPRoute("api-http-route1", httpRoute1) + + related := loader.NewRelatedResources(). + AddComputedRoutesIDs(apiComputedRoutesID). // deliberately skip adding 'foo' here to exercise the bug + AddResources(apiSvc, fooSvc, apiHTTPRoute1) + + // Update this after the fact, but don't update the inner indexing in + // the 'related' struct. + { + httpRoute1.ParentRefs[0] = newParentRef(newRef(catalog.ServiceType, "foo"), "") + apiHTTPRoute1.Data = httpRoute1 + + anyData, err := anypb.New(httpRoute1) + require.NoError(t, err) + apiHTTPRoute1.Resource.Data = anyData + } + + expect := []*ComputedRoutesResult{{ + ID: apiComputedRoutesID, + OwnerID: apiServiceID, + Data: &pbmesh.ComputedRoutes{ + PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ + "http": { + Config: &pbmesh.ComputedPortRoutes_Http{ + Http: &pbmesh.ComputedHTTPRoute{ + ParentRef: newParentRef(apiServiceRef, "http"), + Rules: []*pbmesh.ComputedHTTPRouteRule{{ + Matches: []*pbmesh.HTTPRouteMatch{{ + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, + Value: "/", + }, + }}, + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ + BackendTarget: backendName("api", "http"), + }}, + }}, + }, + }, + UsingDefaultConfig: true, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("api", "http"): { + BackendRef: newBackendRef(apiServiceRef, "http", ""), + Service: apiServiceData, + }, + }, + }, + }, + }, + }} + run(t, related, expect, nil) + }) + + t.Run("stale-weird: parent ref uses invalid port", func(t *testing.T) { + apiServiceData := &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"api-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + }, + } + + fooServiceData := &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"foo-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + }, + } + + httpRoute1 := &pbmesh.HTTPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + // Using bad parent port (www). + newParentRef(newRef(catalog.ServiceType, "api"), "www"), + }, + Rules: []*pbmesh.HTTPRouteRule{{ + BackendRefs: []*pbmesh.HTTPBackendRef{{ + BackendRef: newBackendRef(fooServiceRef, "http", ""), + }}, + }}, + } + + related := loader.NewRelatedResources(). + AddComputedRoutesIDs(apiComputedRoutesID). + AddResources( + newService("api", apiServiceData), + newService("foo", fooServiceData), + newHTTPRoute("api-http-route1", httpRoute1), + ) + + expect := []*ComputedRoutesResult{{ + ID: apiComputedRoutesID, + OwnerID: apiServiceID, + Data: &pbmesh.ComputedRoutes{ + PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ + "http": { + Config: &pbmesh.ComputedPortRoutes_Http{ + Http: &pbmesh.ComputedHTTPRoute{ + ParentRef: newParentRef(apiServiceRef, "http"), + Rules: []*pbmesh.ComputedHTTPRouteRule{{ + Matches: []*pbmesh.HTTPRouteMatch{{ + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, + Value: "/", + }, + }}, + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ + BackendTarget: backendName("api", "http"), + }}, + }}, + }, + }, + UsingDefaultConfig: true, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("api", "http"): { + BackendRef: newBackendRef(apiServiceRef, "http", ""), + Service: apiServiceData, + }, + }, + }, + }, + }, + }} + run(t, related, expect, nil) + }) + + t.Run("overlapping xRoutes", func(t *testing.T) { + apiServiceData := &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"api-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + }, + } + + fooServiceData := &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"foo-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + }, + } + + httpRouteData := &pbmesh.HTTPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + // Using bad parent port (www). + newParentRef(newRef(catalog.ServiceType, "api"), "http"), + }, + Rules: []*pbmesh.HTTPRouteRule{{ + BackendRefs: []*pbmesh.HTTPBackendRef{{ + BackendRef: newBackendRef(fooServiceRef, "http", ""), + }}, + }}, + } + httpRoute := newHTTPRoute("api-http-route", httpRouteData) + + tcpRouteData := &pbmesh.TCPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + // Using bad parent port (www). + newParentRef(newRef(catalog.ServiceType, "api"), "http"), + }, + Rules: []*pbmesh.TCPRouteRule{{ + BackendRefs: []*pbmesh.TCPBackendRef{{ + BackendRef: newBackendRef(fooServiceRef, "http", ""), + }}, + }}, + } + tcpRoute := newTCPRoute("api-tcp-route", tcpRouteData) + // Force them to have the same generation, so that we fall back on + // lexicographic sort on the names as tiebreaker. + // + // api-http-route < api-tcp-route + tcpRoute.Resource.Generation = httpRoute.Resource.Generation + + related := loader.NewRelatedResources(). + AddComputedRoutesIDs(apiComputedRoutesID). + AddResources( + newService("api", apiServiceData), + newService("foo", fooServiceData), + httpRoute, + tcpRoute, + ) + + expect := []*ComputedRoutesResult{{ + ID: apiComputedRoutesID, + OwnerID: apiServiceID, + Data: &pbmesh.ComputedRoutes{ + PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ + "http": { + Config: &pbmesh.ComputedPortRoutes_Http{ + Http: &pbmesh.ComputedHTTPRoute{ + ParentRef: newParentRef(apiServiceRef, "http"), + Rules: []*pbmesh.ComputedHTTPRouteRule{ + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ + BackendTarget: backendName("foo", "http"), + }}, + }, + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, + }, + }, + }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("foo", "http"): { + BackendRef: newBackendRef(fooServiceRef, "http", ""), + Service: fooServiceData, + }, + }, + }, + }, + }, + }} + + expectPending := make(PendingStatuses) + expectPending[resource.NewReferenceKey(tcpRoute.Resource.Id)] = &PendingResourceStatusUpdate{ + ID: tcpRoute.Resource.Id, + Generation: tcpRoute.Resource.Generation, + NewConditions: []*pbresource.Condition{ + ConditionConflictNotBoundToParentRef( + apiServiceRef, + "http", + types.HTTPRouteType, + ), + }, + } + + run(t, related, expect, expectPending) + }) + + t.Run("two http routes", func(t *testing.T) { + apiServiceData := &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"api-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + }, + } + + fooServiceData := &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"foo-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + }, + } + + barServiceData := &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"bar-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + }, + } + + httpRoute1 := &pbmesh.HTTPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + newParentRef(newRef(catalog.ServiceType, "api"), "http"), + }, + Rules: []*pbmesh.HTTPRouteRule{{ + Matches: []*pbmesh.HTTPRouteMatch{{ + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, + Value: "/gir", + }, + }}, + BackendRefs: []*pbmesh.HTTPBackendRef{{ + BackendRef: newBackendRef(fooServiceRef, "", ""), + }}, + }}, + } + + httpRoute2 := &pbmesh.HTTPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + newParentRef(newRef(catalog.ServiceType, "api"), "http"), + }, + Rules: []*pbmesh.HTTPRouteRule{{ + Matches: []*pbmesh.HTTPRouteMatch{{ + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, + Value: "/zim", + }, + }}, + BackendRefs: []*pbmesh.HTTPBackendRef{{ + BackendRef: newBackendRef(barServiceRef, "", ""), + }}, + }}, + } + + related := loader.NewRelatedResources(). + AddComputedRoutesIDs(apiComputedRoutesID). + AddResources( + newService("api", apiServiceData), + newService("foo", fooServiceData), + newService("bar", barServiceData), + newHTTPRoute("api-http-route1", httpRoute1), + newHTTPRoute("api-http-route2", httpRoute2), + ) + + expect := []*ComputedRoutesResult{{ + ID: apiComputedRoutesID, + OwnerID: apiServiceID, + Data: &pbmesh.ComputedRoutes{ + PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ + "http": { + Config: &pbmesh.ComputedPortRoutes_Http{ + Http: &pbmesh.ComputedHTTPRoute{ + ParentRef: newParentRef(apiServiceRef, "http"), + Rules: []*pbmesh.ComputedHTTPRouteRule{ + { + Matches: []*pbmesh.HTTPRouteMatch{{ + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, + Value: "/gir", + }, + }}, + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ + BackendTarget: backendName("foo", "http"), + }}, + }, + { + Matches: []*pbmesh.HTTPRouteMatch{{ + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, + Value: "/zim", + }, + }}, + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ + BackendTarget: backendName("bar", "http"), + }}, + }, + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, + }, + }, + }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("foo", "http"): { + BackendRef: newBackendRef(fooServiceRef, "http", ""), + Service: fooServiceData, + }, + backendName("bar", "http"): { + BackendRef: newBackendRef(barServiceRef, "http", ""), + Service: barServiceData, + }, + }, + }, + }, + }, + }} + run(t, related, expect, nil) + }) + + t.Run("http route with empty match path", func(t *testing.T) { + apiServiceData := &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"api-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + }, + } + + fooServiceData := &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"foo-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + }, + } + + httpRoute1 := &pbmesh.HTTPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + newParentRef(newRef(catalog.ServiceType, "api"), "http"), + }, + Rules: []*pbmesh.HTTPRouteRule{{ + Matches: []*pbmesh.HTTPRouteMatch{{ + Path: nil, + }}, + BackendRefs: []*pbmesh.HTTPBackendRef{{ + BackendRef: newBackendRef(fooServiceRef, "", ""), + }}, + }}, + } + + related := loader.NewRelatedResources(). + AddComputedRoutesIDs(apiComputedRoutesID). + AddResources( + newService("api", apiServiceData), + newService("foo", fooServiceData), + newHTTPRoute("api-http-route1", httpRoute1), + ) + + expect := []*ComputedRoutesResult{{ + ID: apiComputedRoutesID, + OwnerID: apiServiceID, + Data: &pbmesh.ComputedRoutes{ + PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ + "http": { + Config: &pbmesh.ComputedPortRoutes_Http{ + Http: &pbmesh.ComputedHTTPRoute{ + ParentRef: newParentRef(apiServiceRef, "http"), + Rules: []*pbmesh.ComputedHTTPRouteRule{ + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ + BackendTarget: backendName("foo", "http"), + }}, + }, + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, + }, + }, + }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("foo", "http"): { + BackendRef: newBackendRef(fooServiceRef, "http", ""), + Service: fooServiceData, + }, + }, + }, + }, + }, + }} + run(t, related, expect, nil) + }) + + t.Run("stale-weird: destination with no service", func(t *testing.T) { + t.Run("http", func(t *testing.T) { + apiServiceData := &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"api-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + }, + } + + httpRoute1 := &pbmesh.HTTPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + newParentRef(newRef(catalog.ServiceType, "api"), "http"), + }, + Rules: []*pbmesh.HTTPRouteRule{{ + BackendRefs: []*pbmesh.HTTPBackendRef{{ + BackendRef: newBackendRef(fooServiceRef, "", ""), + }}, + }}, + } + + related := loader.NewRelatedResources(). + AddComputedRoutesIDs(apiComputedRoutesID). + AddResources( + newService("api", apiServiceData), + newHTTPRoute("api-http-route1", httpRoute1), + ) + + expect := []*ComputedRoutesResult{{ + ID: apiComputedRoutesID, + OwnerID: apiServiceID, + Data: &pbmesh.ComputedRoutes{ + PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ + "http": { + Config: &pbmesh.ComputedPortRoutes_Http{ + Http: &pbmesh.ComputedHTTPRoute{ + ParentRef: newParentRef(apiServiceRef, "http"), + Rules: []*pbmesh.ComputedHTTPRouteRule{ + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, + }, + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, + }, + }, + }, + }, + }, + }, + }, + }} + run(t, related, expect, nil) + }) + t.Run("grpc", func(t *testing.T) { + apiServiceData := &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"api-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + {TargetPort: "grpc", Protocol: pbcatalog.Protocol_PROTOCOL_GRPC}, + }, + } + + grpcRoute1 := &pbmesh.GRPCRoute{ + ParentRefs: []*pbmesh.ParentReference{ + newParentRef(newRef(catalog.ServiceType, "api"), "grpc"), + }, + Rules: []*pbmesh.GRPCRouteRule{{ + BackendRefs: []*pbmesh.GRPCBackendRef{{ + BackendRef: newBackendRef(fooServiceRef, "", ""), + }}, + }}, + } + + related := loader.NewRelatedResources(). + AddComputedRoutesIDs(apiComputedRoutesID). + AddResources( + newService("api", apiServiceData), + newGRPCRoute("api-grpc-route1", grpcRoute1), + ) + + expect := []*ComputedRoutesResult{{ + ID: apiComputedRoutesID, + OwnerID: apiServiceID, + Data: &pbmesh.ComputedRoutes{ + PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ + "grpc": { + Config: &pbmesh.ComputedPortRoutes_Grpc{ + Grpc: &pbmesh.ComputedGRPCRoute{ + ParentRef: newParentRef(apiServiceRef, "grpc"), + Rules: []*pbmesh.ComputedGRPCRouteRule{ + { + Matches: defaultGRPCRouteMatches(), + BackendRefs: []*pbmesh.ComputedGRPCBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, + }, + { + Matches: defaultGRPCRouteMatches(), + BackendRefs: []*pbmesh.ComputedGRPCBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, + }, + }, + }, + }, + }, + }, + }, + }} + run(t, related, expect, nil) + }) + t.Run("tcp", func(t *testing.T) { + apiServiceData := &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"api-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + {TargetPort: "tcp", Protocol: pbcatalog.Protocol_PROTOCOL_TCP}, + }, + } + + tcpRoute1 := &pbmesh.TCPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + newParentRef(newRef(catalog.ServiceType, "api"), "tcp"), + }, + Rules: []*pbmesh.TCPRouteRule{{ + BackendRefs: []*pbmesh.TCPBackendRef{{ + BackendRef: newBackendRef(fooServiceRef, "", ""), + }}, + }}, + } + + related := loader.NewRelatedResources(). + AddComputedRoutesIDs(apiComputedRoutesID). + AddResources( + newService("api", apiServiceData), + newTCPRoute("api-tcp-route1", tcpRoute1), + ) + + expect := []*ComputedRoutesResult{{ + ID: apiComputedRoutesID, + OwnerID: apiServiceID, + Data: &pbmesh.ComputedRoutes{ + PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ + "tcp": { + Config: &pbmesh.ComputedPortRoutes_Tcp{ + Tcp: &pbmesh.ComputedTCPRoute{ + ParentRef: newParentRef(apiServiceRef, "tcp"), + Rules: []*pbmesh.ComputedTCPRouteRule{ + { + BackendRefs: []*pbmesh.ComputedTCPBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, + }, + }, + }, + }, + }, + }, + }, + }} + run(t, related, expect, nil) + }) + }) + + t.Run("stale-weird: http destination with service not in mesh", func(t *testing.T) { + apiServiceData := &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"api-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + }, + } + + fooServiceData := &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"foo-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + }, + } + + httpRoute1 := &pbmesh.HTTPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + newParentRef(newRef(catalog.ServiceType, "api"), "http"), + }, + Rules: []*pbmesh.HTTPRouteRule{{ + BackendRefs: []*pbmesh.HTTPBackendRef{{ + BackendRef: newBackendRef(fooServiceRef, "", ""), + }}, + }}, + } + + related := loader.NewRelatedResources(). + AddComputedRoutesIDs(apiComputedRoutesID). + AddResources( + newService("api", apiServiceData), + newService("foo", fooServiceData), + newHTTPRoute("api-http-route1", httpRoute1), + ) + + expect := []*ComputedRoutesResult{{ + ID: apiComputedRoutesID, + OwnerID: apiServiceID, + Data: &pbmesh.ComputedRoutes{ + PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ + "http": { + Config: &pbmesh.ComputedPortRoutes_Http{ + Http: &pbmesh.ComputedHTTPRoute{ + ParentRef: newParentRef(apiServiceRef, "http"), + Rules: []*pbmesh.ComputedHTTPRouteRule{ + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, + }, + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, + }, + }, + }, + }, + }, + }, + }, + }} + run(t, related, expect, nil) + }) + + t.Run("http route with dest policy", func(t *testing.T) { + apiServiceData := &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"api-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + }, + } + + fooServiceData := &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"foo-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + }, + } + + httpRoute1 := &pbmesh.HTTPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + newParentRef(newRef(catalog.ServiceType, "api"), "http"), + }, + Rules: []*pbmesh.HTTPRouteRule{{ + Matches: []*pbmesh.HTTPRouteMatch{{ + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, + Value: "/", + }, + }}, + BackendRefs: []*pbmesh.HTTPBackendRef{{ + BackendRef: newBackendRef(fooServiceRef, "", ""), + }}, + }}, + } + + destPolicy := &pbmesh.DestinationPolicy{ + PortConfigs: map[string]*pbmesh.DestinationConfig{ + "http": { + ConnectTimeout: durationpb.New(55 * time.Second), + }, + }, + } + + related := loader.NewRelatedResources(). + AddComputedRoutesIDs(apiComputedRoutesID). + AddResources( + newService("api", apiServiceData), + newService("foo", fooServiceData), + newHTTPRoute("api-http-route1", httpRoute1), + newDestPolicy("foo", destPolicy), + ) + + expect := []*ComputedRoutesResult{{ + ID: apiComputedRoutesID, + OwnerID: apiServiceID, + Data: &pbmesh.ComputedRoutes{ + PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ + "http": { + Config: &pbmesh.ComputedPortRoutes_Http{ + Http: &pbmesh.ComputedHTTPRoute{ + ParentRef: newParentRef(apiServiceRef, "http"), + Rules: []*pbmesh.ComputedHTTPRouteRule{ + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ + BackendTarget: backendName("foo", "http"), + }}, + }, + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, + }, + }, + }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("foo", "http"): { + BackendRef: newBackendRef(fooServiceRef, "http", ""), + Service: fooServiceData, + DestinationPolicy: destPolicy, + }, + }, + }, + }, + }, + }} + run(t, related, expect, nil) + }) + + t.Run("http route with failover policy", func(t *testing.T) { + apiServiceData := &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"api-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + }, + } + + fooServiceData := &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"foo-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + }, + } + + barServiceData := &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"bar-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + }, + } + + httpRoute1 := &pbmesh.HTTPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + newParentRef(newRef(catalog.ServiceType, "api"), "http"), + }, + Rules: []*pbmesh.HTTPRouteRule{{ + Matches: []*pbmesh.HTTPRouteMatch{{ + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, + Value: "/", + }, + }}, + BackendRefs: []*pbmesh.HTTPBackendRef{{ + BackendRef: newBackendRef(fooServiceRef, "", ""), + }}, + }}, + } + + failoverPolicy := &pbcatalog.FailoverPolicy{ + Config: &pbcatalog.FailoverConfig{ + Destinations: []*pbcatalog.FailoverDestination{{ + Ref: barServiceRef, + }}, + }, + } + simplifiedFailoverPolicy := &pbcatalog.FailoverPolicy{ + PortConfigs: map[string]*pbcatalog.FailoverConfig{ + "http": &pbcatalog.FailoverConfig{ + Destinations: []*pbcatalog.FailoverDestination{{ + Ref: barServiceRef, + Port: "http", + }}, + }, + }, + } + + related := loader.NewRelatedResources(). + AddComputedRoutesIDs(apiComputedRoutesID). + AddResources( + newService("api", apiServiceData), + newService("foo", fooServiceData), + newService("bar", barServiceData), + newHTTPRoute("api-http-route1", httpRoute1), + newFailPolicy("foo", failoverPolicy), + ) + + expect := []*ComputedRoutesResult{{ + ID: apiComputedRoutesID, + OwnerID: apiServiceID, + Data: &pbmesh.ComputedRoutes{ + PortedConfigs: map[string]*pbmesh.ComputedPortRoutes{ + "http": { + Config: &pbmesh.ComputedPortRoutes_Http{ + Http: &pbmesh.ComputedHTTPRoute{ + ParentRef: newParentRef(apiServiceRef, "http"), + Rules: []*pbmesh.ComputedHTTPRouteRule{ + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ + BackendTarget: backendName("foo", "http"), + }}, + }, + { + Matches: defaultHTTPRouteMatches(), + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ + BackendTarget: types.NullRouteBackend, + }}, + }, + }, + }, + }, + Targets: map[string]*pbmesh.BackendTargetDetails{ + backendName("foo", "http"): { + BackendRef: newBackendRef(fooServiceRef, "http", ""), + Service: fooServiceData, + FailoverPolicy: simplifiedFailoverPolicy, + }, + }, + }, + }, + }, + }} + run(t, related, expect, nil) }) } From 547ff463e3b6e68dd37821fe81750281740a50e0 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Wed, 23 Aug 2023 10:15:45 -0500 Subject: [PATCH 35/49] lint --- internal/mesh/internal/controllers/routes/generate_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/mesh/internal/controllers/routes/generate_test.go b/internal/mesh/internal/controllers/routes/generate_test.go index ecf83da7bfd..022a4d50f95 100644 --- a/internal/mesh/internal/controllers/routes/generate_test.go +++ b/internal/mesh/internal/controllers/routes/generate_test.go @@ -49,7 +49,7 @@ func TestGenerateComputedRoutes(t *testing.T) { require.True(t, ok, "missing expected pending status for %v", rk) prototest.AssertDeepEqual(t, expectItem, gotItem) } - for rk, _ := range pending { + for rk := range pending { _, ok := expectPending[rk] require.True(t, ok, "extra pending status for %v", rk) } @@ -1454,7 +1454,7 @@ func TestGenerateComputedRoutes(t *testing.T) { } simplifiedFailoverPolicy := &pbcatalog.FailoverPolicy{ PortConfigs: map[string]*pbcatalog.FailoverConfig{ - "http": &pbcatalog.FailoverConfig{ + "http": { Destinations: []*pbcatalog.FailoverDestination{{ Ref: barServiceRef, Port: "http", From 2516db6e09c2249dc38ee0578bd8f6cbc2c0da43 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Wed, 23 Aug 2023 10:26:55 -0500 Subject: [PATCH 36/49] clean up this plumbing --- .../internal/controllers/routes/generate.go | 41 ++++++++----------- .../controllers/routes/intermediate.go | 23 ++--------- .../internal/controllers/routes/sort_rules.go | 14 ++++++- 3 files changed, 32 insertions(+), 46 deletions(-) diff --git a/internal/mesh/internal/controllers/routes/generate.go b/internal/mesh/internal/controllers/routes/generate.go index 3eaf85a2e1e..72f9829cf1f 100644 --- a/internal/mesh/internal/controllers/routes/generate.go +++ b/internal/mesh/internal/controllers/routes/generate.go @@ -190,20 +190,26 @@ func compile( if top.RouteType != routeNode.RouteType { // This should only happen with user-provided ones, since we // would never have two synthetic default routes at once. - res := routeNode.OriginalResource() - pending.AddConditions(resource.NewReferenceKey(res.Id), res, []*pbresource.Condition{ - ConditionConflictNotBoundToParentRef( - parentServiceRef, - port, - top.RouteType, - ), - }) + res := routeNode.OriginalResource + if res != nil { + pending.AddConditions(resource.NewReferenceKey(res.Id), res, []*pbresource.Condition{ + ConditionConflictNotBoundToParentRef( + parentServiceRef, + port, + top.RouteType, + ), + }) + } continue } top.AppendRulesFrom(routeNode) top.AddTargetsFrom(routeNode) } + // Clear this field as it's no longer used and doesn't make sense once + // this represents multiple xRoutes or defaults. + top.OriginalResource = nil + // Now we can do the big sort. gammaSortRouteRules(top) @@ -295,13 +301,8 @@ func compileHTTPRouteNode( route = protoClone(route) node := newInputRouteNode(port) - dec := &types.DecodedHTTPRoute{ - Resource: res, - Data: route, - } - node.RouteType = types.HTTPRouteType - node.HTTP = dec + node.OriginalResource = res node.HTTPRules = make([]*pbmesh.ComputedHTTPRouteRule, 0, len(route.Rules)) for _, rule := range route.Rules { irule := &pbmesh.ComputedHTTPRouteRule{ @@ -369,13 +370,9 @@ func compileGRPCRouteNode( route = protoClone(route) node := newInputRouteNode(port) - dec := &types.DecodedGRPCRoute{ - Resource: res, - Data: route, - } node.RouteType = types.GRPCRouteType - node.GRPC = dec + node.OriginalResource = res node.GRPCRules = make([]*pbmesh.ComputedGRPCRouteRule, 0, len(route.Rules)) for _, rule := range route.Rules { irule := &pbmesh.ComputedGRPCRouteRule{ @@ -435,13 +432,9 @@ func compileTCPRouteNode( route = protoClone(route) node := newInputRouteNode(port) - dec := &types.DecodedTCPRoute{ - Resource: res, - Data: route, - } node.RouteType = types.TCPRouteType - node.TCP = dec + node.OriginalResource = res node.TCPRules = make([]*pbmesh.ComputedTCPRouteRule, 0, len(route.Rules)) for _, rule := range route.Rules { irule := &pbmesh.ComputedTCPRouteRule{ diff --git a/internal/mesh/internal/controllers/routes/intermediate.go b/internal/mesh/internal/controllers/routes/intermediate.go index b5ac1c02095..d62e1b73dc4 100644 --- a/internal/mesh/internal/controllers/routes/intermediate.go +++ b/internal/mesh/internal/controllers/routes/intermediate.go @@ -25,13 +25,9 @@ type inputRouteNode struct { NewTargets map[string]*pbmesh.BackendTargetDetails - // These three are the originals. If something needs customization for - // compilation a shadow field will exist on this enclosing object instead. - // - // only one of these can be set to non-nil - HTTP *types.DecodedHTTPRoute - GRPC *types.DecodedGRPCRoute - TCP *types.DecodedTCPRoute + // This field is non-nil for nodes based on a single xRoute, and nil for + // composite nodes or default nodes. + OriginalResource *pbresource.Resource } func newInputRouteNode(port string) *inputRouteNode { @@ -74,16 +70,3 @@ func (n *inputRouteNode) AppendRulesFrom(next *inputRouteNode) { panic("impossible") } } - -func (n *inputRouteNode) OriginalResource() *pbresource.Resource { - switch { - case n.HTTP != nil: - return n.HTTP.GetResource() - case n.GRPC != nil: - return n.GRPC.GetResource() - case n.TCP != nil: - return n.TCP.GetResource() - default: - panic("impossible") - } -} diff --git a/internal/mesh/internal/controllers/routes/sort_rules.go b/internal/mesh/internal/controllers/routes/sort_rules.go index 0a12225140e..42ce6288a09 100644 --- a/internal/mesh/internal/controllers/routes/sort_rules.go +++ b/internal/mesh/internal/controllers/routes/sort_rules.go @@ -148,6 +148,12 @@ func (r *sortableHTTPRouteRules) Less(i, j int) bool { // } +// gammaInitialSortWrappedRoutes will sort the original inputs by the +// resource-envelope-only fields before we further stable sort by type-specific +// fields. +// +// If more than 1 route is provided the OriginalResource field must be set on +// all inputs (i.e. no synthetic default routes should be processed here). func gammaInitialSortWrappedRoutes(routes []*inputRouteNode) { if len(routes) < 2 { return @@ -157,13 +163,17 @@ func gammaInitialSortWrappedRoutes(routes []*inputRouteNode) { // stable sort take care of the ultimate tiebreakers. sort.SliceStable(routes, func(i, j int) bool { var ( - resA = routes[i].OriginalResource() - resB = routes[j].OriginalResource() + resA = routes[i].OriginalResource + resB = routes[j].OriginalResource genA = resA.Generation genB = resB.Generation ) + if resA == nil || resB == nil { + panic("some provided nodes lacked original resources") + } + // (END-1) The oldest Route based on creation timestamp. // // Because these are ULIDs, we should be able to lexicographically sort From 0f7466dd01699b30ce37679f82b75eb17bcb02db Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Wed, 23 Aug 2023 11:53:57 -0500 Subject: [PATCH 37/49] test initial sort --- .../controllers/routes/intermediate.go | 6 +- .../internal/controllers/routes/sort_rules.go | 2 +- .../controllers/routes/sort_rules_test.go | 216 ++++++++++++++++++ 3 files changed, 220 insertions(+), 4 deletions(-) create mode 100644 internal/mesh/internal/controllers/routes/sort_rules_test.go diff --git a/internal/mesh/internal/controllers/routes/intermediate.go b/internal/mesh/internal/controllers/routes/intermediate.go index d62e1b73dc4..fee6c58f23e 100644 --- a/internal/mesh/internal/controllers/routes/intermediate.go +++ b/internal/mesh/internal/controllers/routes/intermediate.go @@ -15,14 +15,14 @@ import ( type inputRouteNode struct { ParentPort string // always set + RouteType *pbresource.Type + Default bool + // only one of these can be set to non-empty HTTPRules []*pbmesh.ComputedHTTPRouteRule GRPCRules []*pbmesh.ComputedGRPCRouteRule TCPRules []*pbmesh.ComputedTCPRouteRule - RouteType *pbresource.Type - Default bool - NewTargets map[string]*pbmesh.BackendTargetDetails // This field is non-nil for nodes based on a single xRoute, and nil for diff --git a/internal/mesh/internal/controllers/routes/sort_rules.go b/internal/mesh/internal/controllers/routes/sort_rules.go index 42ce6288a09..06a9c228eda 100644 --- a/internal/mesh/internal/controllers/routes/sort_rules.go +++ b/internal/mesh/internal/controllers/routes/sort_rules.go @@ -161,7 +161,7 @@ func gammaInitialSortWrappedRoutes(routes []*inputRouteNode) { // First sort the input routes by the final criteria, so we can let the // stable sort take care of the ultimate tiebreakers. - sort.SliceStable(routes, func(i, j int) bool { + sort.Slice(routes, func(i, j int) bool { var ( resA = routes[i].OriginalResource resB = routes[j].OriginalResource diff --git a/internal/mesh/internal/controllers/routes/sort_rules_test.go b/internal/mesh/internal/controllers/routes/sort_rules_test.go new file mode 100644 index 00000000000..635a8e5fafc --- /dev/null +++ b/internal/mesh/internal/controllers/routes/sort_rules_test.go @@ -0,0 +1,216 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package routes + +import ( + "math/rand" + "sort" + "testing" + + "github.com/stretchr/testify/require" + "golang.org/x/exp/slices" + + "github.com/hashicorp/consul/internal/mesh/internal/types" + "github.com/hashicorp/consul/internal/resource" + rtest "github.com/hashicorp/consul/internal/resource/resourcetest" + pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" + "github.com/hashicorp/consul/proto-public/pbresource" + "github.com/hashicorp/consul/proto/private/prototest" +) + +func TestGammaInitialSortWrappedRoutes(t *testing.T) { + // These generations were created with ulid.Make().String() at 1 second + // intervals. They are in ascending order. + generations := []string{ + "01H8HGKHXCAJY7TRNJ06JGEZP9", + "01H8HGKJWMEYWKZATG44QZF0G7", + "01H8HGKKVW2339KFHAFXMEFRHP", + "01H8HGKMV45XAC2W1KCQ7KJ00J", + "01H8HGKNTCB3ZYN16AZ2KSXN54", + "01H8HGKPSN9V3QQXTQVZ1EQM2T", + "01H8HGKQRXMR8NY662AC520CDE", + "01H8HGKRR5C41RHGXY7H3N0JYX", + "01H8HGKSQDNSQ54VN86SBTR149", + "01H8HGKTPPRWFXWHKV90M2WW5R", + } + require.True(t, sort.StringsAreSorted(generations)) + + nodeID := func(node *inputRouteNode) string { + return resource.IDToString(node.OriginalResource.Id) + ":" + node.OriginalResource.Generation + } + + nodeIDSlice := func(nodes []*inputRouteNode) []string { + var out []string + for _, node := range nodes { + out = append(out, nodeID(node)) + } + return out + } + + // newNode will only populate the fields that the sorting function cares + // about. + newNode := func(tenancy *pbresource.Tenancy, name string, gen string) *inputRouteNode { + id := rtest.Resource(types.HTTPRouteType, name). + WithTenancy(tenancy). + ID() + + res := rtest.ResourceID(id). + WithGeneration(gen). + WithData(t, &pbmesh.HTTPRoute{}). + Build() + + return &inputRouteNode{ + OriginalResource: res, + } + } + + type testcase struct { + routes []*inputRouteNode + } + + run := func(t *testing.T, tc testcase) { + expect := nodeIDSlice(tc.routes) + + in := tc.routes + + if len(in) > 1 { + // Randomly permute it + for { + rand.Shuffle(len(in), func(i, j int) { + in[i], in[j] = in[j], in[i] + }) + curr := nodeIDSlice(tc.routes) + + if slices.Equal(expect, curr) { + // Loop until the shuffle was actually different. + } else { + break + } + + } + } + + gammaInitialSortWrappedRoutes(in) + + prototest.AssertDeepEqual(t, tc.routes, in) + } + + // Order: + // 1. generation older first + // 2. tenancy namespace A first + // 3. object name A first + cases := map[string]testcase{ + "empty": {}, + "one": { + routes: []*inputRouteNode{ + newNode(defaultTenancy(), "foo", generations[0]), + }, + }, + "two: by generation": { + routes: []*inputRouteNode{ + newNode(defaultTenancy(), "foo", generations[0]), + newNode(defaultTenancy(), "foo", generations[1]), + }, + }, + "two: by namespace": { + routes: []*inputRouteNode{ + newNode(&pbresource.Tenancy{Namespace: "aaa"}, "foo", generations[0]), + newNode(&pbresource.Tenancy{Namespace: "bbb"}, "foo", generations[0]), + }, + }, + "two: by name": { + routes: []*inputRouteNode{ + newNode(defaultTenancy(), "bar", generations[0]), + newNode(defaultTenancy(), "foo", generations[0]), + }, + }, + "two: by name with empty namespace": { + routes: []*inputRouteNode{ + newNode(nsTenancy(""), "bar", generations[0]), + newNode(nsTenancy(""), "foo", generations[0]), + }, + }, + "four: by generation": { + routes: []*inputRouteNode{ + newNode(defaultTenancy(), "foo", generations[0]), + newNode(defaultTenancy(), "foo", generations[1]), + newNode(defaultTenancy(), "foo", generations[2]), + newNode(defaultTenancy(), "foo", generations[3]), + }, + }, + "four: by name with some empty namespaces": { + routes: []*inputRouteNode{ + newNode(nsTenancy("aaa"), "foo", generations[0]), + newNode(nsTenancy("bbb"), "foo", generations[0]), + newNode(&pbresource.Tenancy{}, "bar", generations[0]), + newNode(&pbresource.Tenancy{}, "foo", generations[0]), + }, + }, + "mixed": { + // Seed this with data such that each later sort criteria should + // want to be more to the top than an earlier criteria would allow, + // to prove that the sort is going to have to shake out the way the + // algorithm wants it to for maximum algorithm exercise. + routes: []*inputRouteNode{ + // gen beats name + newNode(defaultTenancy(), "zzz", generations[0]), + newNode(defaultTenancy(), "aaa", generations[1]), + // gen beats ns + newNode(nsTenancy("zzz"), "foo", generations[2]), + newNode(nsTenancy("aaa"), "foo", generations[3]), + // ns beats name + newNode(nsTenancy("aaa"), "zzz", generations[4]), + newNode(nsTenancy("bbb"), "aaa", generations[5]), + }, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + run(t, tc) + }) + } +} + +func TestGammaSortHTTPRouteRules(t *testing.T) { + type testcase struct { + rules []*pbmesh.ComputedHTTPRouteRule + } + + run := func(t *testing.T, tc testcase) { + dup := protoSliceClone(tc.rules) + + gammaSortHTTPRouteRules(dup) + + prototest.AssertDeepEqual(t, tc.rules, dup) + } + + cases := map[string]testcase{ + // + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + run(t, tc) + }) + } +} + +func newID(typ *pbresource.Type, tenancy *pbresource.Tenancy, name string) *pbresource.ID { + return rtest.Resource(typ, name). + WithTenancy(tenancy). + ID() +} + +func nsTenancy(ns string) *pbresource.Tenancy { + return &pbresource.Tenancy{ + Partition: "default", + Namespace: ns, + PeerName: "local", + } +} + +func defaultTenancy() *pbresource.Tenancy { + return nsTenancy("default") +} From c946c95ac760ebb1e898025251a595797c2066ba Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Wed, 23 Aug 2023 12:08:45 -0500 Subject: [PATCH 38/49] More sort --- .../internal/controllers/routes/sort_rules.go | 1 - .../controllers/routes/sort_rules_test.go | 298 +++++++++++++++++- 2 files changed, 288 insertions(+), 11 deletions(-) diff --git a/internal/mesh/internal/controllers/routes/sort_rules.go b/internal/mesh/internal/controllers/routes/sort_rules.go index 06a9c228eda..ede2ee01523 100644 --- a/internal/mesh/internal/controllers/routes/sort_rules.go +++ b/internal/mesh/internal/controllers/routes/sort_rules.go @@ -145,7 +145,6 @@ func (r *sortableHTTPRouteRules) Less(i, j int) bool { // (5) Largest number of query param matches. return a.numQueryParams > b.numQueryParams - // } // gammaInitialSortWrappedRoutes will sort the original inputs by the diff --git a/internal/mesh/internal/controllers/routes/sort_rules_test.go b/internal/mesh/internal/controllers/routes/sort_rules_test.go index 635a8e5fafc..5c6429e412a 100644 --- a/internal/mesh/internal/controllers/routes/sort_rules_test.go +++ b/internal/mesh/internal/controllers/routes/sort_rules_test.go @@ -16,7 +16,6 @@ import ( rtest "github.com/hashicorp/consul/internal/resource/resourcetest" pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" "github.com/hashicorp/consul/proto-public/pbresource" - "github.com/hashicorp/consul/proto/private/prototest" ) func TestGammaInitialSortWrappedRoutes(t *testing.T) { @@ -72,10 +71,9 @@ func TestGammaInitialSortWrappedRoutes(t *testing.T) { run := func(t *testing.T, tc testcase) { expect := nodeIDSlice(tc.routes) - in := tc.routes - - if len(in) > 1 { + if len(tc.routes) > 1 { // Randomly permute it + in := tc.routes for { rand.Shuffle(len(in), func(i, j int) { in[i], in[j] = in[j], in[i] @@ -91,9 +89,11 @@ func TestGammaInitialSortWrappedRoutes(t *testing.T) { } } - gammaInitialSortWrappedRoutes(in) + gammaInitialSortWrappedRoutes(tc.routes) + + got := nodeIDSlice(tc.routes) - prototest.AssertDeepEqual(t, tc.routes, in) + require.Equal(t, expect, got) } // Order: @@ -178,16 +178,294 @@ func TestGammaSortHTTPRouteRules(t *testing.T) { rules []*pbmesh.ComputedHTTPRouteRule } + // In this test we will use the 'backend target' field to track the rule + // identity to make assertions easy. + + targetSlice := func(rules []*pbmesh.ComputedHTTPRouteRule) []string { + var out []string + for _, rule := range rules { + out = append(out, rule.BackendRefs[0].BackendTarget) + } + return out + } + run := func(t *testing.T, tc testcase) { - dup := protoSliceClone(tc.rules) + expect := targetSlice(tc.rules) + + if len(tc.rules) > 1 { + // Randomly permute it + in := tc.rules + for { + rand.Shuffle(len(in), func(i, j int) { + in[i], in[j] = in[j], in[i] + }) + curr := targetSlice(tc.rules) - gammaSortHTTPRouteRules(dup) + if slices.Equal(expect, curr) { + // Loop until the shuffle was actually different. + } else { + break + } - prototest.AssertDeepEqual(t, tc.rules, dup) + } + } + + gammaSortHTTPRouteRules(tc.rules) + + got := targetSlice(tc.rules) + + require.Equal(t, expect, got) } + newRule := func(target string, m ...*pbmesh.HTTPRouteMatch) *pbmesh.ComputedHTTPRouteRule { + return &pbmesh.ComputedHTTPRouteRule{ + Matches: m, + BackendRefs: []*pbmesh.ComputedHTTPBackendRef{{ + BackendTarget: target, + }}, + } + } + + // Rules: + // 1. exact path match exists + // 2. prefix match exists + // 3. prefix match has lots of characters + // 4. has method match + // 5. has lots of header matches + // 6. has lots of query param matches cases := map[string]testcase{ - // + "empty": {}, + "one": { + rules: []*pbmesh.ComputedHTTPRouteRule{ + newRule("r1", &pbmesh.HTTPRouteMatch{ + // + }), + }, + }, + "two: by exact path match exists": { + rules: []*pbmesh.ComputedHTTPRouteRule{ + newRule("r1", &pbmesh.HTTPRouteMatch{ + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_EXACT, + Value: "/", + }, + }), + newRule("r2", &pbmesh.HTTPRouteMatch{ + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, + Value: "/", + }, + }), + }, + }, + "two: by prefix path match exists": { + rules: []*pbmesh.ComputedHTTPRouteRule{ + newRule("r1", &pbmesh.HTTPRouteMatch{ + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, + Value: "/", + }, + }), + newRule("r2", &pbmesh.HTTPRouteMatch{ + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_REGEX, + Value: "/[a]", + }, + }), + }, + }, + "two: by prefix path match length": { + rules: []*pbmesh.ComputedHTTPRouteRule{ + newRule("r1", &pbmesh.HTTPRouteMatch{ + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, + Value: "/longer", + }, + }), + newRule("r2", &pbmesh.HTTPRouteMatch{ + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, + Value: "/short", + }, + }), + }, + }, + "two: by method match exists": { + rules: []*pbmesh.ComputedHTTPRouteRule{ + newRule("r1", &pbmesh.HTTPRouteMatch{ + Method: "GET", + }), + newRule("r2", &pbmesh.HTTPRouteMatch{ + Headers: []*pbmesh.HTTPHeaderMatch{{ + Type: pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_EXACT, + Name: "x-blah", + Value: "foo", + }}, + }), + }, + }, + "two: by header match quantity": { + rules: []*pbmesh.ComputedHTTPRouteRule{ + newRule("r1", &pbmesh.HTTPRouteMatch{ + Headers: []*pbmesh.HTTPHeaderMatch{ + { + Type: pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_EXACT, + Name: "x-blah", + Value: "foo", + }, + { + Type: pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_EXACT, + Name: "x-other", + Value: "bar", + }, + }, + }), + newRule("r2", &pbmesh.HTTPRouteMatch{ + Headers: []*pbmesh.HTTPHeaderMatch{{ + Type: pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_EXACT, + Name: "x-blah", + Value: "foo", + }}, + }), + }, + }, + "two: by query param match quantity": { + rules: []*pbmesh.ComputedHTTPRouteRule{ + newRule("r1", &pbmesh.HTTPRouteMatch{ + QueryParams: []*pbmesh.HTTPQueryParamMatch{ + { + Type: pbmesh.QueryParamMatchType_QUERY_PARAM_MATCH_TYPE_EXACT, + Name: "foo", + Value: "1", + }, + { + Type: pbmesh.QueryParamMatchType_QUERY_PARAM_MATCH_TYPE_EXACT, + Name: "bar", + Value: "1", + }, + }, + }), + newRule("r2", &pbmesh.HTTPRouteMatch{ + QueryParams: []*pbmesh.HTTPQueryParamMatch{{ + Type: pbmesh.QueryParamMatchType_QUERY_PARAM_MATCH_TYPE_EXACT, + Name: "foo", + Value: "1", + }}, + }), + }, + }, + "mixed: has path exact beats has path prefix when both are present": { + rules: []*pbmesh.ComputedHTTPRouteRule{ + newRule("r1", + &pbmesh.HTTPRouteMatch{ + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_EXACT, + Value: "/", + }, + }, + &pbmesh.HTTPRouteMatch{ + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, + Value: "/short", + }, + }, + ), + newRule("r2", &pbmesh.HTTPRouteMatch{ + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, + Value: "/longer", + }, + }), + }, + }, + "mixed: longer path prefix beats shorter when both are present": { + rules: []*pbmesh.ComputedHTTPRouteRule{ + newRule("r1", + &pbmesh.HTTPRouteMatch{ + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, + Value: "/longer", + }, + }, + &pbmesh.HTTPRouteMatch{ + Method: "GET", + }, + ), + newRule("r2", &pbmesh.HTTPRouteMatch{ + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, + Value: "/short", + }, + }), + }, + }, + "mixed: has method match beats header match when both are present": { + rules: []*pbmesh.ComputedHTTPRouteRule{ + newRule("r1", + &pbmesh.HTTPRouteMatch{ + Method: "GET", + }, + &pbmesh.HTTPRouteMatch{ + Headers: []*pbmesh.HTTPHeaderMatch{{ + Type: pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_EXACT, + Name: "x-blah", + Value: "foo", + }}, + }, + ), + newRule("r2", &pbmesh.HTTPRouteMatch{ + Headers: []*pbmesh.HTTPHeaderMatch{ + { + Type: pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_EXACT, + Name: "x-blah", + Value: "foo", + }, + { + Type: pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_EXACT, + Name: "x-other", + Value: "bar", + }, + }, + }), + }, + }, + "mixed: header match beats query param match when both are present": { + rules: []*pbmesh.ComputedHTTPRouteRule{ + newRule("r1", &pbmesh.HTTPRouteMatch{ + Headers: []*pbmesh.HTTPHeaderMatch{ + { + Type: pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_EXACT, + Name: "x-blah", + Value: "foo", + }, + { + Type: pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_EXACT, + Name: "x-other", + Value: "bar", + }, + }, + QueryParams: []*pbmesh.HTTPQueryParamMatch{{ + Type: pbmesh.QueryParamMatchType_QUERY_PARAM_MATCH_TYPE_EXACT, + Name: "foo", + Value: "1", + }}, + }), + newRule("r2", &pbmesh.HTTPRouteMatch{ + QueryParams: []*pbmesh.HTTPQueryParamMatch{ + { + Type: pbmesh.QueryParamMatchType_QUERY_PARAM_MATCH_TYPE_EXACT, + Name: "foo", + Value: "1", + }, + { + Type: pbmesh.QueryParamMatchType_QUERY_PARAM_MATCH_TYPE_EXACT, + Name: "bar", + Value: "1", + }, + }, + }), + }, + }, } for name, tc := range cases { From aef3c44455e80ad7eb5e5633fe14ac0e44edd71d Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Wed, 23 Aug 2023 14:41:39 -0500 Subject: [PATCH 39/49] reorder to appease lint --- internal/mesh/internal/controllers/routes/sort_rules.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/internal/mesh/internal/controllers/routes/sort_rules.go b/internal/mesh/internal/controllers/routes/sort_rules.go index ede2ee01523..338aeb0e140 100644 --- a/internal/mesh/internal/controllers/routes/sort_rules.go +++ b/internal/mesh/internal/controllers/routes/sort_rules.go @@ -164,15 +164,17 @@ func gammaInitialSortWrappedRoutes(routes []*inputRouteNode) { var ( resA = routes[i].OriginalResource resB = routes[j].OriginalResource - - genA = resA.Generation - genB = resB.Generation ) if resA == nil || resB == nil { panic("some provided nodes lacked original resources") } + var ( + genA = resA.Generation + genB = resB.Generation + ) + // (END-1) The oldest Route based on creation timestamp. // // Because these are ULIDs, we should be able to lexicographically sort From 14d345f1d949025d7698fa6e390fd2e1772e9d22 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Thu, 24 Aug 2023 10:58:29 -0500 Subject: [PATCH 40/49] update signatures to be friendlier to testing --- .../controllers/routes/loader/loader.go | 14 ++-- .../controllers/routes/loader/loader_test.go | 6 +- .../controllers/routes/loader/related.go | 75 +++++++++++++------ 3 files changed, 63 insertions(+), 32 deletions(-) diff --git a/internal/mesh/internal/controllers/routes/loader/loader.go b/internal/mesh/internal/controllers/routes/loader/loader.go index 0c8ae49b8f6..ec6a4f15839 100644 --- a/internal/mesh/internal/controllers/routes/loader/loader.go +++ b/internal/mesh/internal/controllers/routes/loader/loader.go @@ -157,7 +157,7 @@ func (l *loader) gatherXRoutesAsInput( routeData = route.Data } err = l.gatherSingleXRouteAsInput(ctx, logger, routeID, routeData, func() { - l.out.AddResource(route) + l.out.AddHTTPRoute(route) }) if err != nil { return fmt.Errorf("the resource service has returned an unexpected error loading %s: %w", routeID, err) @@ -172,7 +172,7 @@ func (l *loader) gatherXRoutesAsInput( routeData = route.Data } err = l.gatherSingleXRouteAsInput(ctx, logger, routeID, routeData, func() { - l.out.AddResource(route) + l.out.AddGRPCRoute(route) }) if err != nil { return fmt.Errorf("the resource service has returned an unexpected error loading %s: %w", routeID, err) @@ -187,7 +187,7 @@ func (l *loader) gatherXRoutesAsInput( routeData = route.Data } err = l.gatherSingleXRouteAsInput(ctx, logger, routeID, routeData, func() { - l.out.AddResource(route) + l.out.AddTCPRoute(route) }) if err != nil { return fmt.Errorf("the resource service has returned an unexpected error loading %s: %w", routeID, err) @@ -214,7 +214,7 @@ func (l *loader) loadUpstreamService( return err } if service != nil { - l.out.AddResource(service) + l.out.AddService(service) failoverPolicyID := changeResourceType(svcID, catalog.FailoverPolicyType) failoverPolicy, err := l.mem.GetFailoverPolicy(ctx, failoverPolicyID) @@ -224,7 +224,7 @@ func (l *loader) loadUpstreamService( } if failoverPolicy != nil { l.mapper.TrackFailoverPolicy(failoverPolicy) - l.out.AddResource(failoverPolicy) + l.out.AddFailoverPolicy(failoverPolicy) destRefs := failoverPolicy.Data.GetUnderlyingDestinationRefs() for _, destRef := range destRefs { @@ -237,7 +237,7 @@ func (l *loader) loadUpstreamService( return err } if failService != nil { - l.out.AddResource(failService) + l.out.AddService(failService) } } } else { @@ -251,7 +251,7 @@ func (l *loader) loadUpstreamService( return err } if destPolicy != nil { - l.out.AddResource(destPolicy) + l.out.AddDestinationPolicy(destPolicy) } } diff --git a/internal/mesh/internal/controllers/routes/loader/loader_test.go b/internal/mesh/internal/controllers/routes/loader/loader_test.go index 8981085ec14..726b5b0f801 100644 --- a/internal/mesh/internal/controllers/routes/loader/loader_test.go +++ b/internal/mesh/internal/controllers/routes/loader/loader_test.go @@ -380,7 +380,11 @@ func defaultTenancy() *pbresource.Tenancy { } } -func doubleMap(t *testing.T, list ...decodedResource) map[resource.ReferenceKey]map[resource.ReferenceKey]struct{} { +type resourceGetter interface { + GetResource() *pbresource.Resource +} + +func doubleMap(t *testing.T, list ...resourceGetter) map[resource.ReferenceKey]map[resource.ReferenceKey]struct{} { if len(list)%2 != 0 { t.Fatalf("list must have an even number of references") } diff --git a/internal/mesh/internal/controllers/routes/loader/related.go b/internal/mesh/internal/controllers/routes/loader/related.go index d051db2aa1f..281e2cc9c09 100644 --- a/internal/mesh/internal/controllers/routes/loader/related.go +++ b/internal/mesh/internal/controllers/routes/loader/related.go @@ -52,43 +52,72 @@ func (r *RelatedResources) AddComputedRoutesID(id *pbresource.ID) *RelatedResour return r } -func (r *RelatedResources) AddResources(list ...decodedResource) *RelatedResources { +// AddResources must only be called with valid *resource.DecodedResource[T] +// types. +// +// This is provided as a testing convenience. Non-test code should call the +// type-specific adder. +func (r *RelatedResources) AddResources(list ...any) *RelatedResources { for _, res := range list { - _ = r.AddResource(res) + r.AddResource(res) } return r } -type decodedResource interface { - GetResource() *pbresource.Resource -} - -func (r *RelatedResources) AddResource(res decodedResource) bool { +// AddResource must only be called with valid *resource.DecodedResource[T] types. +// +// This is provided as a testing convenience. Non-test code should call the +// type-specific adder. +func (r *RelatedResources) AddResource(res any) { if res == nil { - return false + return } switch dec := res.(type) { case *types.DecodedHTTPRoute: - r.addRouteSetEntries(dec.Resource, dec.Data) - return addResource(dec.Resource.Id, dec, r.HTTPRoutes) + r.AddHTTPRoute(dec) case *types.DecodedGRPCRoute: - r.addRouteSetEntries(dec.Resource, dec.Data) - return addResource(dec.Resource.Id, dec, r.GRPCRoutes) + r.AddGRPCRoute(dec) case *types.DecodedTCPRoute: - r.addRouteSetEntries(dec.Resource, dec.Data) - return addResource(dec.Resource.Id, dec, r.TCPRoutes) + r.AddTCPRoute(dec) + case *types.DecodedDestinationPolicy: + r.AddDestinationPolicy(dec) case *types.DecodedService: - return addResource(dec.Resource.Id, dec, r.Services) + r.AddService(dec) case *types.DecodedFailoverPolicy: - return addResource(dec.Resource.Id, dec, r.FailoverPolicies) - case *types.DecodedDestinationPolicy: - return addResource(dec.Resource.Id, dec, r.DestinationPolicies) + r.AddFailoverPolicy(dec) default: panic(fmt.Sprintf("unknown decoded resource type: %T", res)) } } +func (r *RelatedResources) AddHTTPRoute(dec *types.DecodedHTTPRoute) { + r.addRouteSetEntries(dec.Resource, dec.Data) + addResource(dec.Resource.Id, dec, r.HTTPRoutes) +} + +func (r *RelatedResources) AddGRPCRoute(dec *types.DecodedGRPCRoute) { + r.addRouteSetEntries(dec.Resource, dec.Data) + addResource(dec.Resource.Id, dec, r.GRPCRoutes) +} + +func (r *RelatedResources) AddTCPRoute(dec *types.DecodedTCPRoute) { + r.addRouteSetEntries(dec.Resource, dec.Data) + addResource(dec.Resource.Id, dec, r.TCPRoutes) +} + +func (r *RelatedResources) AddDestinationPolicy(dec *types.DecodedDestinationPolicy) { + addResource(dec.Resource.Id, dec, r.DestinationPolicies) +} + +func (r *RelatedResources) AddService(dec *types.DecodedService) { + addResource(dec.Resource.Id, dec, r.Services) +} + +func (r *RelatedResources) AddFailoverPolicy(dec *types.DecodedFailoverPolicy) { + addResource(dec.Resource.Id, dec, r.FailoverPolicies) +} + func (r *RelatedResources) addRouteSetEntries( res *pbresource.Resource, xroute types.XRouteData, @@ -191,15 +220,13 @@ func (r *RelatedResources) GetDestinationPolicyForService(ref resource.Reference return r.GetDestinationPolicy(destRef) } -func addResource[V any](id *pbresource.ID, res *V, m map[resource.ReferenceKey]*V) bool { +func addResource[V any](id *pbresource.ID, res *V, m map[resource.ReferenceKey]*V) { if res == nil { - return false + return } rk := resource.NewReferenceKey(id) - if _, ok := m[rk]; ok { - return false + if _, ok := m[rk]; !ok { + m[rk] = res } - m[rk] = res - return true } From 6864c73183199e40dae9566383905d2d3eff42e4 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Mon, 11 Sep 2023 16:10:03 -0500 Subject: [PATCH 41/49] fixup for scope --- .../controllers/routes/controller_test.go | 33 ++++++-- .../controllers/routes/generate_test.go | 22 +++++- .../controllers/routes/loader/loader_test.go | 22 +++--- .../controllers/routes/ref_validation_test.go | 4 +- .../routes/xroutemapper/xroutemapper_test.go | 78 ++++++++++--------- 5 files changed, 102 insertions(+), 57 deletions(-) diff --git a/internal/mesh/internal/controllers/routes/controller_test.go b/internal/mesh/internal/controllers/routes/controller_test.go index 929fff1fe2e..4e0923a519f 100644 --- a/internal/mesh/internal/controllers/routes/controller_test.go +++ b/internal/mesh/internal/controllers/routes/controller_test.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/consul/internal/catalog" "github.com/hashicorp/consul/internal/controller" "github.com/hashicorp/consul/internal/mesh/internal/types" + "github.com/hashicorp/consul/internal/resource" rtest "github.com/hashicorp/consul/internal/resource/resourcetest" pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v1alpha1" pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" @@ -53,11 +54,19 @@ func (suite *controllerSuite) TestController() { } var ( - apiServiceRef = rtest.Resource(catalog.ServiceType, "api").Reference("") - fooServiceRef = rtest.Resource(catalog.ServiceType, "foo").Reference("") - barServiceRef = rtest.Resource(catalog.ServiceType, "bar").Reference("") - - computedRoutesID = rtest.Resource(types.ComputedRoutesType, "api").ID() + apiServiceRef = rtest.Resource(catalog.ServiceType, "api"). + WithTenancy(resource.DefaultNamespacedTenancy()). + Reference("") + fooServiceRef = rtest.Resource(catalog.ServiceType, "foo"). + WithTenancy(resource.DefaultNamespacedTenancy()). + Reference("") + barServiceRef = rtest.Resource(catalog.ServiceType, "bar"). + WithTenancy(resource.DefaultNamespacedTenancy()). + Reference("") + + computedRoutesID = rtest.Resource(types.ComputedRoutesType, "api"). + WithTenancy(resource.DefaultNamespacedTenancy()). + ID() ) // Start out by creating a single port service and let it create the @@ -75,6 +84,7 @@ func (suite *controllerSuite) TestController() { } _ = rtest.Resource(catalog.ServiceType, "api"). + WithTenancy(resource.DefaultNamespacedTenancy()). WithData(suite.T(), apiServiceData). Write(suite.T(), suite.client) @@ -124,6 +134,7 @@ func (suite *controllerSuite) TestController() { } _ = rtest.Resource(catalog.ServiceType, "api"). + WithTenancy(resource.DefaultNamespacedTenancy()). WithData(suite.T(), apiServiceData). Write(suite.T(), suite.client) @@ -142,6 +153,7 @@ func (suite *controllerSuite) TestController() { } _ = rtest.Resource(catalog.ServiceType, "foo"). + WithTenancy(resource.DefaultNamespacedTenancy()). WithData(suite.T(), fooServiceData). Write(suite.T(), suite.client) @@ -256,6 +268,7 @@ func (suite *controllerSuite) TestController() { }}, } tcpRoute1ID := rtest.Resource(types.TCPRouteType, "api-tcp-route1"). + WithTenancy(resource.DefaultNamespacedTenancy()). WithData(suite.T(), tcpRoute1). Write(suite.T(), suite.client). Id @@ -272,6 +285,7 @@ func (suite *controllerSuite) TestController() { }}, } httpRoute1ID := rtest.Resource(types.HTTPRouteType, "api-http-route1"). + WithTenancy(resource.DefaultNamespacedTenancy()). WithData(suite.T(), httpRoute1). Write(suite.T(), suite.client). Id @@ -287,6 +301,7 @@ func (suite *controllerSuite) TestController() { }}, } grpcRoute1ID := rtest.Resource(types.GRPCRouteType, "api-grpc-route1"). + WithTenancy(resource.DefaultNamespacedTenancy()). WithData(suite.T(), grpcRoute1). Write(suite.T(), suite.client). Id @@ -416,6 +431,7 @@ func (suite *controllerSuite) TestController() { }}, } tcpRoute2ID := rtest.Resource(types.TCPRouteType, "api-tcp-route2"). + WithTenancy(resource.DefaultNamespacedTenancy()). WithData(suite.T(), tcpRoute2). Write(suite.T(), suite.client). Id @@ -438,6 +454,7 @@ func (suite *controllerSuite) TestController() { }}, } httpRoute2ID := rtest.Resource(types.HTTPRouteType, "api-http-route2"). + WithTenancy(resource.DefaultNamespacedTenancy()). WithData(suite.T(), httpRoute2). Write(suite.T(), suite.client). Id @@ -460,6 +477,7 @@ func (suite *controllerSuite) TestController() { }}, } grpcRoute2ID := rtest.Resource(types.GRPCRouteType, "api-grpc-route2"). + WithTenancy(resource.DefaultNamespacedTenancy()). WithData(suite.T(), grpcRoute2). Write(suite.T(), suite.client). Id @@ -644,6 +662,7 @@ func (suite *controllerSuite) TestController() { }}, } rtest.ResourceID(tcpRoute2ID). + WithTenancy(resource.DefaultNamespacedTenancy()). WithData(suite.T(), tcpRoute2). Write(suite.T(), suite.client) @@ -664,6 +683,7 @@ func (suite *controllerSuite) TestController() { }}, } rtest.ResourceID(httpRoute2ID). + WithTenancy(resource.DefaultNamespacedTenancy()). WithData(suite.T(), httpRoute2). Write(suite.T(), suite.client) @@ -685,6 +705,7 @@ func (suite *controllerSuite) TestController() { }}, } rtest.ResourceID(grpcRoute2ID). + WithTenancy(resource.DefaultNamespacedTenancy()). WithData(suite.T(), grpcRoute2). Write(suite.T(), suite.client) @@ -834,6 +855,7 @@ func (suite *controllerSuite) TestController() { }}, } grpcRoute1ID = rtest.Resource(types.GRPCRouteType, "zzz-bad-route"). + WithTenancy(resource.DefaultNamespacedTenancy()). WithData(suite.T(), grpcRoute1). Write(suite.T(), suite.client). Id @@ -970,6 +992,7 @@ func (suite *controllerSuite) TestController() { } _ = rtest.Resource(catalog.ServiceType, "api"). + WithTenancy(resource.DefaultNamespacedTenancy()). WithData(suite.T(), apiServiceData). Write(suite.T(), suite.client) diff --git a/internal/mesh/internal/controllers/routes/generate_test.go b/internal/mesh/internal/controllers/routes/generate_test.go index 022a4d50f95..02bb7ea695e 100644 --- a/internal/mesh/internal/controllers/routes/generate_test.go +++ b/internal/mesh/internal/controllers/routes/generate_test.go @@ -58,6 +58,7 @@ func TestGenerateComputedRoutes(t *testing.T) { newService := func(name string, data *pbcatalog.Service) *types.DecodedService { svc := rtest.Resource(catalog.ServiceType, name). + WithTenancy(resource.DefaultNamespacedTenancy()). WithData(t, data).Build() rtest.ValidateAndNormalize(t, registry, svc) return rtest.MustDecode[*pbcatalog.Service](t, svc) @@ -65,18 +66,21 @@ func TestGenerateComputedRoutes(t *testing.T) { newHTTPRoute := func(name string, data *pbmesh.HTTPRoute) *types.DecodedHTTPRoute { svc := rtest.Resource(types.HTTPRouteType, name). + WithTenancy(resource.DefaultNamespacedTenancy()). WithData(t, data).Build() rtest.ValidateAndNormalize(t, registry, svc) return rtest.MustDecode[*pbmesh.HTTPRoute](t, svc) } newGRPCRoute := func(name string, data *pbmesh.GRPCRoute) *types.DecodedGRPCRoute { svc := rtest.Resource(types.GRPCRouteType, name). + WithTenancy(resource.DefaultNamespacedTenancy()). WithData(t, data).Build() rtest.ValidateAndNormalize(t, registry, svc) return rtest.MustDecode[*pbmesh.GRPCRoute](t, svc) } newTCPRoute := func(name string, data *pbmesh.TCPRoute) *types.DecodedTCPRoute { svc := rtest.Resource(types.TCPRouteType, name). + WithTenancy(resource.DefaultNamespacedTenancy()). WithData(t, data).Build() rtest.ValidateAndNormalize(t, registry, svc) return rtest.MustDecode[*pbmesh.TCPRoute](t, svc) @@ -84,6 +88,7 @@ func TestGenerateComputedRoutes(t *testing.T) { newDestPolicy := func(name string, data *pbmesh.DestinationPolicy) *types.DecodedDestinationPolicy { policy := rtest.Resource(types.DestinationPolicyType, name). + WithTenancy(resource.DefaultNamespacedTenancy()). WithData(t, data).Build() rtest.ValidateAndNormalize(t, registry, policy) return rtest.MustDecode[*pbmesh.DestinationPolicy](t, policy) @@ -91,6 +96,7 @@ func TestGenerateComputedRoutes(t *testing.T) { newFailPolicy := func(name string, data *pbcatalog.FailoverPolicy) *types.DecodedFailoverPolicy { policy := rtest.Resource(catalog.FailoverPolicyType, name). + WithTenancy(resource.DefaultNamespacedTenancy()). WithData(t, data).Build() rtest.ValidateAndNormalize(t, registry, policy) return rtest.MustDecode[*pbcatalog.FailoverPolicy](t, policy) @@ -101,14 +107,22 @@ func TestGenerateComputedRoutes(t *testing.T) { } var ( - apiServiceID = rtest.Resource(catalog.ServiceType, "api").ID() + apiServiceID = rtest.Resource(catalog.ServiceType, "api"). + WithTenancy(resource.DefaultNamespacedTenancy()). + ID() apiServiceRef = resource.Reference(apiServiceID, "") - apiComputedRoutesID = rtest.Resource(types.ComputedRoutesType, "api").ID() + apiComputedRoutesID = rtest.Resource(types.ComputedRoutesType, "api"). + WithTenancy(resource.DefaultNamespacedTenancy()). + ID() - fooServiceID = rtest.Resource(catalog.ServiceType, "foo").ID() + fooServiceID = rtest.Resource(catalog.ServiceType, "foo"). + WithTenancy(resource.DefaultNamespacedTenancy()). + ID() fooServiceRef = resource.Reference(fooServiceID, "") - barServiceID = rtest.Resource(catalog.ServiceType, "bar").ID() + barServiceID = rtest.Resource(catalog.ServiceType, "bar"). + WithTenancy(resource.DefaultNamespacedTenancy()). + ID() barServiceRef = resource.Reference(barServiceID, "") ) diff --git a/internal/mesh/internal/controllers/routes/loader/loader_test.go b/internal/mesh/internal/controllers/routes/loader/loader_test.go index 726b5b0f801..b5fab009758 100644 --- a/internal/mesh/internal/controllers/routes/loader/loader_test.go +++ b/internal/mesh/internal/controllers/routes/loader/loader_test.go @@ -54,6 +54,7 @@ func TestLoadResourcesForComputedRoutes(t *testing.T) { writeHTTP := func(name string, data *pbmesh.HTTPRoute) *types.DecodedHTTPRoute { res := rtest.Resource(types.HTTPRouteType, name). + WithTenancy(resource.DefaultNamespacedTenancy()). WithData(t, data). Write(t, client) mapper.TrackXRoute(res.Id, data) @@ -64,6 +65,7 @@ func TestLoadResourcesForComputedRoutes(t *testing.T) { writeGRPC := func(name string, data *pbmesh.GRPCRoute) *types.DecodedGRPCRoute { res := rtest.Resource(types.GRPCRouteType, name). + WithTenancy(resource.DefaultNamespacedTenancy()). WithData(t, data). Write(t, client) mapper.TrackXRoute(res.Id, data) @@ -75,6 +77,7 @@ func TestLoadResourcesForComputedRoutes(t *testing.T) { writeTCP := func(name string, data *pbmesh.TCPRoute) *types.DecodedTCPRoute { res := rtest.Resource(types.TCPRouteType, name). + WithTenancy(resource.DefaultNamespacedTenancy()). WithData(t, data). Write(t, client) mapper.TrackXRoute(res.Id, data) @@ -86,6 +89,7 @@ func TestLoadResourcesForComputedRoutes(t *testing.T) { writeDestPolicy := func(name string, data *pbmesh.DestinationPolicy) *types.DecodedDestinationPolicy { res := rtest.Resource(types.DestinationPolicyType, name). + WithTenancy(resource.DefaultNamespacedTenancy()). WithData(t, data). Write(t, client) dec, err := resource.Decode[*pbmesh.DestinationPolicy](res) @@ -95,6 +99,7 @@ func TestLoadResourcesForComputedRoutes(t *testing.T) { writeFailover := func(name string, data *pbcatalog.FailoverPolicy) *types.DecodedFailoverPolicy { res := rtest.Resource(catalog.FailoverPolicyType, name). + WithTenancy(resource.DefaultNamespacedTenancy()). WithData(t, data). Write(t, client) dec, err := resource.Decode[*pbcatalog.FailoverPolicy](res) @@ -104,6 +109,7 @@ func TestLoadResourcesForComputedRoutes(t *testing.T) { writeService := func(name string, data *pbcatalog.Service) *types.DecodedService { res := rtest.Resource(catalog.ServiceType, name). + WithTenancy(resource.DefaultNamespacedTenancy()). WithData(t, data). Write(t, client) dec, err := resource.Decode[*pbcatalog.Service](res) @@ -145,12 +151,12 @@ func TestLoadResourcesForComputedRoutes(t *testing.T) { apiRoutesID := &pbresource.ID{ Type: types.ComputedRoutesType, - Tenancy: defaultTenancy(), + Tenancy: resource.DefaultNamespacedTenancy(), Name: "api", } adminRoutesID := &pbresource.ID{ Type: types.ComputedRoutesType, - Tenancy: defaultTenancy(), + Tenancy: resource.DefaultNamespacedTenancy(), Name: "admin", } @@ -369,15 +375,9 @@ func TestLoadResourcesForComputedRoutes(t *testing.T) { } func newRef(typ *pbresource.Type, name string) *pbresource.Reference { - return rtest.Resource(typ, name).Reference("") -} - -func defaultTenancy() *pbresource.Tenancy { - return &pbresource.Tenancy{ - Partition: "default", - Namespace: "default", - PeerName: "local", - } + return rtest.Resource(typ, name). + WithTenancy(resource.DefaultNamespacedTenancy()). + Reference("") } type resourceGetter interface { diff --git a/internal/mesh/internal/controllers/routes/ref_validation_test.go b/internal/mesh/internal/controllers/routes/ref_validation_test.go index 57e1c1ca22b..8577cca2c19 100644 --- a/internal/mesh/internal/controllers/routes/ref_validation_test.go +++ b/internal/mesh/internal/controllers/routes/ref_validation_test.go @@ -205,7 +205,9 @@ func TestComputeNewRouteRefConditions(t *testing.T) { } func newRef(typ *pbresource.Type, name string) *pbresource.Reference { - return rtest.Resource(typ, name).Reference("") + return rtest.Resource(typ, name). + WithTenancy(resource.DefaultNamespacedTenancy()). + Reference("") } type testServiceGetter struct { diff --git a/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper_test.go b/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper_test.go index 169f6d7c096..422db03c07f 100644 --- a/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper_test.go +++ b/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper_test.go @@ -79,13 +79,16 @@ func testMapper_Tracking(t *testing.T, typ *pbresource.Type, newRoute func(t *te newService := func(name string) *pbresource.Resource { svc := rtest.Resource(catalog.ServiceType, name). - WithData(t, &pbcatalog.Service{}).Build() + WithTenancy(resource.DefaultNamespacedTenancy()). + WithData(t, &pbcatalog.Service{}). + Build() rtest.ValidateAndNormalize(t, registry, svc) return svc } newDestPolicy := func(name string, dur time.Duration) *pbresource.Resource { policy := rtest.Resource(types.DestinationPolicyType, name). + WithTenancy(resource.DefaultNamespacedTenancy()). WithData(t, &pbmesh.DestinationPolicy{ PortConfigs: map[string]*pbmesh.DestinationConfig{ "http": { @@ -105,6 +108,7 @@ func testMapper_Tracking(t *testing.T, typ *pbresource.Type, newRoute func(t *te }) } policy := rtest.Resource(catalog.FailoverPolicyType, name). + WithTenancy(resource.DefaultNamespacedTenancy()). WithData(t, &pbcatalog.FailoverPolicy{ Config: &pbcatalog.FailoverConfig{ Destinations: dests, @@ -175,14 +179,16 @@ func testMapper_Tracking(t *testing.T, typ *pbresource.Type, newRoute func(t *te ) testutil.RunStep(t, "track a name-aligned xroute", func(t *testing.T) { // First route will also not cross any services. - route1 := rtest.Resource(typ, "route-1").WithData(t, newRoute(t, - []*pbmesh.ParentReference{ - {Ref: newRef(catalog.ServiceType, "api")}, - }, - []*pbmesh.BackendReference{ - newBackendRef("api"), - }, - )).Build() + route1 := rtest.Resource(typ, "route-1"). + WithTenancy(resource.DefaultNamespacedTenancy()). + WithData(t, newRoute(t, + []*pbmesh.ParentReference{ + {Ref: newRef(catalog.ServiceType, "api")}, + }, + []*pbmesh.BackendReference{ + newBackendRef("api"), + }, + )).Build() rtest.ValidateAndNormalize(t, registry, route1) requireTracking(t, m, route1, apiComputedRoutes) @@ -216,14 +222,16 @@ func testMapper_Tracking(t *testing.T, typ *pbresource.Type, newRoute func(t *te }) testutil.RunStep(t, "make the route cross services", func(t *testing.T) { - route1 = rtest.Resource(typ, "route-1").WithData(t, newRoute(t, - []*pbmesh.ParentReference{ - {Ref: newRef(catalog.ServiceType, "api")}, - }, - []*pbmesh.BackendReference{ - newBackendRef("www"), - }, - )).Build() + route1 = rtest.Resource(typ, "route-1"). + WithTenancy(resource.DefaultNamespacedTenancy()). + WithData(t, newRoute(t, + []*pbmesh.ParentReference{ + {Ref: newRef(catalog.ServiceType, "api")}, + }, + []*pbmesh.BackendReference{ + newBackendRef("www"), + }, + )).Build() rtest.ValidateAndNormalize(t, registry, route1) // Now witness the update. @@ -264,15 +272,17 @@ func testMapper_Tracking(t *testing.T, typ *pbresource.Type, newRoute func(t *te route2 *pbresource.Resource ) testutil.RunStep(t, "make another route sharing a parent with the first", func(t *testing.T) { - route2 = rtest.Resource(typ, "route-2").WithData(t, newRoute(t, - []*pbmesh.ParentReference{ - {Ref: newRef(catalog.ServiceType, "api")}, - {Ref: newRef(catalog.ServiceType, "foo")}, - }, - []*pbmesh.BackendReference{ - newBackendRef("bar"), - }, - )).Build() + route2 = rtest.Resource(typ, "route-2"). + WithTenancy(resource.DefaultNamespacedTenancy()). + WithData(t, newRoute(t, + []*pbmesh.ParentReference{ + {Ref: newRef(catalog.ServiceType, "api")}, + {Ref: newRef(catalog.ServiceType, "foo")}, + }, + []*pbmesh.BackendReference{ + newBackendRef("bar"), + }, + )).Build() rtest.ValidateAndNormalize(t, registry, route1) // Now witness a route with multiple parents, overlapping the other route. @@ -572,19 +582,15 @@ func newBackendRef(name string) *pbmesh.BackendReference { } func newRef(typ *pbresource.Type, name string) *pbresource.Reference { - return rtest.Resource(typ, name).Reference("") + return rtest.Resource(typ, name). + WithTenancy(resource.DefaultNamespacedTenancy()). + Reference("") } func newID(typ *pbresource.Type, name string) *pbresource.ID { - return rtest.Resource(typ, name).ID() -} - -func defaultTenancy() *pbresource.Tenancy { - return &pbresource.Tenancy{ - Partition: "default", - Namespace: "default", - PeerName: "local", - } + return rtest.Resource(typ, name). + WithTenancy(resource.DefaultNamespacedTenancy()). + ID() } func testDeduplicateRequests(reqs []controller.Request) []controller.Request { From cb7e0737b755b6e607b8dc10be3eec0e7a5634eb Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Mon, 11 Sep 2023 16:29:20 -0500 Subject: [PATCH 42/49] fix enumcover lint --- internal/mesh/internal/controllers/routes/generate.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/mesh/internal/controllers/routes/generate.go b/internal/mesh/internal/controllers/routes/generate.go index 72f9829cf1f..72fca36ab55 100644 --- a/internal/mesh/internal/controllers/routes/generate.go +++ b/internal/mesh/internal/controllers/routes/generate.go @@ -164,6 +164,8 @@ func compile( typ = types.TCPRouteType case pbcatalog.Protocol_PROTOCOL_MESH: fallthrough // to default + case pbcatalog.Protocol_PROTOCOL_UNSPECIFIED: + fallthrough // to default default: continue // not possible } From 4ec9640f386c9c5022cea7677e05ac6092fc1690 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Mon, 11 Sep 2023 16:43:14 -0500 Subject: [PATCH 43/49] use generics instead of generated methods --- .../routes/loader/dest_policy.gen.go | 46 ---------- .../routes/loader/failover_policy.gen.go | 46 ---------- .../routes/loader/gen_memoizer_funcs.sh | 92 ------------------- .../routes/loader/grpc_route.gen.go | 46 ---------- .../routes/loader/http_route.gen.go | 46 ---------- .../controllers/routes/loader/memoized.go | 65 +++++++++++-- .../controllers/routes/loader/service.gen.go | 46 ---------- .../routes/loader/tcp_route.gen.go | 46 ---------- 8 files changed, 58 insertions(+), 375 deletions(-) delete mode 100644 internal/mesh/internal/controllers/routes/loader/dest_policy.gen.go delete mode 100644 internal/mesh/internal/controllers/routes/loader/failover_policy.gen.go delete mode 100755 internal/mesh/internal/controllers/routes/loader/gen_memoizer_funcs.sh delete mode 100644 internal/mesh/internal/controllers/routes/loader/grpc_route.gen.go delete mode 100644 internal/mesh/internal/controllers/routes/loader/http_route.gen.go delete mode 100644 internal/mesh/internal/controllers/routes/loader/service.gen.go delete mode 100644 internal/mesh/internal/controllers/routes/loader/tcp_route.gen.go diff --git a/internal/mesh/internal/controllers/routes/loader/dest_policy.gen.go b/internal/mesh/internal/controllers/routes/loader/dest_policy.gen.go deleted file mode 100644 index ccffb6b5328..00000000000 --- a/internal/mesh/internal/controllers/routes/loader/dest_policy.gen.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 -package loader - -// Code generated by gen_memoizer_funcs.sh. DO NOT EDIT. - -import ( - "context" - "fmt" - - "github.com/hashicorp/consul/internal/catalog" - "github.com/hashicorp/consul/internal/mesh/internal/types" - "github.com/hashicorp/consul/internal/resource" - pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v1alpha1" - pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" - "github.com/hashicorp/consul/proto-public/pbresource" -) - -// Avoid unused imports in generated code. -var _ *pbmesh.ParentReference - -var _ *pbcatalog.Service - -var _ = types.HTTPRouteType - -var _ = catalog.ServiceType - -func (m *memoizingLoader) GetDestinationPolicy(ctx context.Context, id *pbresource.ID) (*types.DecodedDestinationPolicy, error) { - if !resource.EqualType(id.Type, types.DestinationPolicyType) { - return nil, fmt.Errorf("expected *pbmesh.DestinationPolicy, not %s", resource.TypeToString(id.Type)) - } - - rk := resource.NewReferenceKey(id) - - if cached, ok := m.mapDestinationPolicy[rk]; ok { - return cached, nil // cached value may be nil - } - - dec, err := resource.GetDecodedResource[*pbmesh.DestinationPolicy](ctx, m.client, id) - if err != nil { - return nil, err - } - - m.mapDestinationPolicy[rk] = dec - return dec, nil -} diff --git a/internal/mesh/internal/controllers/routes/loader/failover_policy.gen.go b/internal/mesh/internal/controllers/routes/loader/failover_policy.gen.go deleted file mode 100644 index 3eb56fb6e1f..00000000000 --- a/internal/mesh/internal/controllers/routes/loader/failover_policy.gen.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 -package loader - -// Code generated by gen_memoizer_funcs.sh. DO NOT EDIT. - -import ( - "context" - "fmt" - - "github.com/hashicorp/consul/internal/catalog" - "github.com/hashicorp/consul/internal/mesh/internal/types" - "github.com/hashicorp/consul/internal/resource" - pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v1alpha1" - pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" - "github.com/hashicorp/consul/proto-public/pbresource" -) - -// Avoid unused imports in generated code. -var _ *pbmesh.ParentReference - -var _ *pbcatalog.Service - -var _ = types.HTTPRouteType - -var _ = catalog.ServiceType - -func (m *memoizingLoader) GetFailoverPolicy(ctx context.Context, id *pbresource.ID) (*types.DecodedFailoverPolicy, error) { - if !resource.EqualType(id.Type, catalog.FailoverPolicyType) { - return nil, fmt.Errorf("expected *pbcatalog.FailoverPolicy, not %s", resource.TypeToString(id.Type)) - } - - rk := resource.NewReferenceKey(id) - - if cached, ok := m.mapFailoverPolicy[rk]; ok { - return cached, nil // cached value may be nil - } - - dec, err := resource.GetDecodedResource[*pbcatalog.FailoverPolicy](ctx, m.client, id) - if err != nil { - return nil, err - } - - m.mapFailoverPolicy[rk] = dec - return dec, nil -} diff --git a/internal/mesh/internal/controllers/routes/loader/gen_memoizer_funcs.sh b/internal/mesh/internal/controllers/routes/loader/gen_memoizer_funcs.sh deleted file mode 100755 index cc546340f6a..00000000000 --- a/internal/mesh/internal/controllers/routes/loader/gen_memoizer_funcs.sh +++ /dev/null @@ -1,92 +0,0 @@ -#!/usr/bin/env bash -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: BUSL-1.1 - -unset CDPATH - -set -euo pipefail - -# NOTE: this script could be made more generic if folks find it useful. - -if [[ $# -lt 2 ]]; then - echo "expected at least 2 args" >&2 - exit 1 -fi - -# first argument is output path -output="$1" -shift - -if [[ "$output" != *.go ]]; then - echo "output file should end with a .go suffix" >&2 - exit 2 -fi - -cat > "${output}.tmp" <&2 - exit 1 - fi - gopkg="types" - if [[ "$pkg" = "pbcatalog" ]]; then - gopkg="catalog" - fi - ###################### - cat >> "${output}.tmp" < Date: Mon, 11 Sep 2023 16:46:11 -0500 Subject: [PATCH 44/49] switch to resource.ReplaceType --- .../internal/controllers/routes/xroutemapper/util.go | 12 ++---------- .../controllers/routes/xroutemapper/xroutemapper.go | 8 ++++---- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/internal/mesh/internal/controllers/routes/xroutemapper/util.go b/internal/mesh/internal/controllers/routes/xroutemapper/util.go index e02131d7130..ba1c4143041 100644 --- a/internal/mesh/internal/controllers/routes/xroutemapper/util.go +++ b/internal/mesh/internal/controllers/routes/xroutemapper/util.go @@ -37,21 +37,13 @@ func backendRefSliceToRefSlice(backendRefs []*pbmesh.BackendReference) []resourc return backends } -func changeType(id *pbresource.ID, typ *pbresource.Type) *pbresource.ID { - return &pbresource.ID{ - Type: typ, - Tenancy: id.Tenancy, - Name: id.Name, - } -} - -func changeTypeForSlice(list []*pbresource.ID, typ *pbresource.Type) []*pbresource.ID { +func SliceReplaceType(list []*pbresource.ID, typ *pbresource.Type) []*pbresource.ID { if list == nil { return nil } out := make([]*pbresource.ID, 0, len(list)) for _, id := range list { - out = append(out, changeType(id, typ)) + out = append(out, resource.ReplaceType(typ, id)) } return out } diff --git a/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper.go b/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper.go index ce2e8bd755d..f59ce13e681 100644 --- a/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper.go +++ b/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper.go @@ -209,7 +209,7 @@ func (m *Mapper) MapFailoverPolicy( // Since this is name-aligned, just switch the type and find routes that // will route any traffic to this destination service. - svcID := changeType(res.Id, catalog.ServiceType) + svcID := resource.ReplaceType(catalog.ServiceType, res.Id) return m.mapXRouteDirectServiceRefToComputedRoutesByID(svcID) } @@ -235,7 +235,7 @@ func (m *Mapper) MapDestinationPolicy( // Since this is name-aligned, just switch the type and find routes that // will route any traffic to this destination service. - svcID := changeType(res.Id, catalog.ServiceType) + svcID := resource.ReplaceType(catalog.ServiceType, res.Id) return m.mapXRouteDirectServiceRefToComputedRoutesByID(svcID) } @@ -253,7 +253,7 @@ func (m *Mapper) MapService( // (case 2) First find all failover policies that have a reference to our input service. failPolicyIDs := m.failMapper.FailoverIDsByService(res.Id) - effectiveServiceIDs := changeTypeForSlice(failPolicyIDs, catalog.ServiceType) + effectiveServiceIDs := SliceReplaceType(failPolicyIDs, catalog.ServiceType) // (case 1) Do the direct mapping also. effectiveServiceIDs = append(effectiveServiceIDs, res.Id) @@ -278,7 +278,7 @@ func (m *Mapper) mapXRouteDirectServiceRefToComputedRoutesByID(svcID *pbresource // return 1 hit for the name aligned mesh config primaryReq := controller.Request{ - ID: changeType(svcID, types.ComputedRoutesType), + ID: resource.ReplaceType(types.ComputedRoutesType, svcID), } svcRef := resource.Reference(svcID, "") From 4f81494214b378c35cb9501ecb38555a3fc6a407 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Mon, 11 Sep 2023 16:47:25 -0500 Subject: [PATCH 45/49] use existing interface --- .../controllers/routes/xroutemapper/xroutemapper.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper.go b/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper.go index f59ce13e681..e983bbf4147 100644 --- a/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper.go +++ b/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper.go @@ -7,8 +7,6 @@ import ( "context" "fmt" - "google.golang.org/protobuf/proto" - "github.com/hashicorp/consul/internal/catalog" "github.com/hashicorp/consul/internal/controller" "github.com/hashicorp/consul/internal/mesh/internal/types" @@ -172,10 +170,7 @@ func (m *Mapper) MapTCPRoute(_ context.Context, _ controller.Runtime, res *pbres } // mapXRouteToComputedRoutes will map xRoute changes to ComputedRoutes changes. -func mapXRouteToComputedRoutes[T interface { - proto.Message - types.XRouteWithRefs -}](res *pbresource.Resource, m *Mapper) ([]controller.Request, error) { +func mapXRouteToComputedRoutes[T types.XRouteData](res *pbresource.Resource, m *Mapper) ([]controller.Request, error) { dec, err := resource.Decode[T](res) if err != nil { return nil, fmt.Errorf("error unmarshalling xRoute: %w", err) From f3cac9376c93539ef0ec9fe42c3d7d6b649f85b1 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Mon, 11 Sep 2023 16:48:02 -0500 Subject: [PATCH 46/49] unexport --- internal/mesh/internal/controllers/routes/xroutemapper/util.go | 2 +- .../internal/controllers/routes/xroutemapper/xroutemapper.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/mesh/internal/controllers/routes/xroutemapper/util.go b/internal/mesh/internal/controllers/routes/xroutemapper/util.go index ba1c4143041..1a2c8decffb 100644 --- a/internal/mesh/internal/controllers/routes/xroutemapper/util.go +++ b/internal/mesh/internal/controllers/routes/xroutemapper/util.go @@ -37,7 +37,7 @@ func backendRefSliceToRefSlice(backendRefs []*pbmesh.BackendReference) []resourc return backends } -func SliceReplaceType(list []*pbresource.ID, typ *pbresource.Type) []*pbresource.ID { +func sliceReplaceType(list []*pbresource.ID, typ *pbresource.Type) []*pbresource.ID { if list == nil { return nil } diff --git a/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper.go b/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper.go index e983bbf4147..862cef9c362 100644 --- a/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper.go +++ b/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper.go @@ -248,7 +248,7 @@ func (m *Mapper) MapService( // (case 2) First find all failover policies that have a reference to our input service. failPolicyIDs := m.failMapper.FailoverIDsByService(res.Id) - effectiveServiceIDs := SliceReplaceType(failPolicyIDs, catalog.ServiceType) + effectiveServiceIDs := sliceReplaceType(failPolicyIDs, catalog.ServiceType) // (case 1) Do the direct mapping also. effectiveServiceIDs = append(effectiveServiceIDs, res.Id) From 94cb21bdbf703e40b6079698669f9cbe646ddf3a Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Mon, 11 Sep 2023 17:03:05 -0500 Subject: [PATCH 47/49] add shared helper --- internal/controller/helper.go | 34 ++++++++ internal/controller/helper_test.go | 77 +++++++++++++++++++ .../controllers/routes/xroutemapper/util.go | 23 ------ .../routes/xroutemapper/xroutemapper.go | 4 +- 4 files changed, 113 insertions(+), 25 deletions(-) create mode 100644 internal/controller/helper.go create mode 100644 internal/controller/helper_test.go diff --git a/internal/controller/helper.go b/internal/controller/helper.go new file mode 100644 index 00000000000..381dea2ede6 --- /dev/null +++ b/internal/controller/helper.go @@ -0,0 +1,34 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package controller + +import ( + "github.com/hashicorp/consul/internal/resource" + "github.com/hashicorp/consul/proto-public/pbresource" +) + +// MakeRequests accepts a list of pbresource.ID and pbresource.Reference items, +// and mirrors them into a slice of []controller.Request items where the Type +// of of the items has replaced by 'typ'. +func MakeRequests[V resource.ReferenceOrID]( + typ *pbresource.Type, + refs []V, +) []Request { + if len(refs) == 0 { + return nil + } + + out := make([]Request, 0, len(refs)) + for _, ref := range refs { + out = append(out, Request{ + ID: &pbresource.ID{ + Type: typ, + Tenancy: ref.GetTenancy(), + Name: ref.GetName(), + }, + }) + } + + return out +} diff --git a/internal/controller/helper_test.go b/internal/controller/helper_test.go new file mode 100644 index 00000000000..47509e7a952 --- /dev/null +++ b/internal/controller/helper_test.go @@ -0,0 +1,77 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package controller + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/hashicorp/consul/internal/resource" + "github.com/hashicorp/consul/proto-public/pbresource" + "github.com/hashicorp/consul/proto/private/prototest" +) + +func TestMakeRequests(t *testing.T) { + redType := &pbresource.Type{ + Group: "colors", + GroupVersion: "vfake", + Kind: "red", + } + blueType := &pbresource.Type{ + Group: "colors", + GroupVersion: "vfake", + Kind: "blue", + } + + casparID := &pbresource.ID{ + Type: redType, + Tenancy: resource.DefaultNamespacedTenancy(), + Name: "caspar", + Uid: "ignored", + } + babypantsID := &pbresource.ID{ + Type: redType, + Tenancy: resource.DefaultNamespacedTenancy(), + Name: "babypants", + Uid: "ignored", + } + zimRef := &pbresource.Reference{ + Type: redType, + Tenancy: resource.DefaultNamespacedTenancy(), + Name: "zim", + Section: "ignored", + } + girRef := &pbresource.Reference{ + Type: redType, + Tenancy: resource.DefaultNamespacedTenancy(), + Name: "gir", + Section: "ignored", + } + + newBlueReq := func(name string) Request { + return Request{ + ID: &pbresource.ID{ + Type: blueType, + Tenancy: resource.DefaultNamespacedTenancy(), + Name: name, + }, + } + } + + require.Nil(t, MakeRequests[*pbresource.ID](blueType, nil)) + require.Nil(t, MakeRequests[*pbresource.Reference](blueType, nil)) + + prototest.AssertElementsMatch(t, []Request{ + newBlueReq("caspar"), newBlueReq("babypants"), + }, MakeRequests[*pbresource.ID](blueType, []*pbresource.ID{ + casparID, babypantsID, + })) + + prototest.AssertElementsMatch(t, []Request{ + newBlueReq("gir"), newBlueReq("zim"), + }, MakeRequests[*pbresource.Reference](blueType, []*pbresource.Reference{ + girRef, zimRef, + })) +} diff --git a/internal/mesh/internal/controllers/routes/xroutemapper/util.go b/internal/mesh/internal/controllers/routes/xroutemapper/util.go index 1a2c8decffb..87fcfeb5698 100644 --- a/internal/mesh/internal/controllers/routes/xroutemapper/util.go +++ b/internal/mesh/internal/controllers/routes/xroutemapper/util.go @@ -4,7 +4,6 @@ package xroutemapper import ( - "github.com/hashicorp/consul/internal/controller" "github.com/hashicorp/consul/internal/mesh/internal/types" "github.com/hashicorp/consul/internal/resource" pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" @@ -47,25 +46,3 @@ func sliceReplaceType(list []*pbresource.ID, typ *pbresource.Type) []*pbresource } return out } - -func makeControllerRequests[V resource.ReferenceOrID]( - typ *pbresource.Type, - refs []V, -) []controller.Request { - if len(refs) == 0 { - return nil - } - - out := make([]controller.Request, 0, len(refs)) - for _, ref := range refs { - out = append(out, controller.Request{ - ID: &pbresource.ID{ - Type: typ, - Tenancy: ref.GetTenancy(), - Name: ref.GetName(), - }, - }) - } - - return out -} diff --git a/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper.go b/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper.go index 862cef9c362..c49f0a6c270 100644 --- a/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper.go +++ b/internal/mesh/internal/controllers/routes/xroutemapper/xroutemapper.go @@ -180,7 +180,7 @@ func mapXRouteToComputedRoutes[T types.XRouteData](res *pbresource.Resource, m * m.TrackXRoute(res.Id, route) - return makeControllerRequests( + return controller.MakeRequests( types.ComputedRoutesType, parentRefSliceToRefSlice(route.GetParentRefs()), ), nil @@ -291,7 +291,7 @@ func (m *Mapper) mapXRouteDirectServiceRefToComputedRoutesByID(svcID *pbresource // Find all parent refs of this route. svcRefs := m.ParentServiceRefsByRouteID(routeID) - out = append(out, makeControllerRequests( + out = append(out, controller.MakeRequests( types.ComputedRoutesType, svcRefs, )...) From f64b24317b1b5cc8aa1d1437c98cd750583b942a Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Mon, 11 Sep 2023 17:04:38 -0500 Subject: [PATCH 48/49] remove extra suspenders --- .../mesh/internal/controllers/routes/xroutemapper/util.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/internal/mesh/internal/controllers/routes/xroutemapper/util.go b/internal/mesh/internal/controllers/routes/xroutemapper/util.go index 87fcfeb5698..6ba99bb65b9 100644 --- a/internal/mesh/internal/controllers/routes/xroutemapper/util.go +++ b/internal/mesh/internal/controllers/routes/xroutemapper/util.go @@ -4,7 +4,6 @@ package xroutemapper import ( - "github.com/hashicorp/consul/internal/mesh/internal/types" "github.com/hashicorp/consul/internal/resource" pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" "github.com/hashicorp/consul/proto-public/pbresource" @@ -16,7 +15,7 @@ func parentRefSliceToRefSlice(parentRefs []*pbmesh.ParentReference) []resource.R } parents := make([]resource.ReferenceOrID, 0, len(parentRefs)) for _, parentRef := range parentRefs { - if parentRef.Ref != nil && types.IsServiceType(parentRef.Ref.Type) { + if parentRef.Ref != nil { parents = append(parents, parentRef.Ref) } } @@ -29,7 +28,7 @@ func backendRefSliceToRefSlice(backendRefs []*pbmesh.BackendReference) []resourc } backends := make([]resource.ReferenceOrID, 0, len(backendRefs)) for _, backendRef := range backendRefs { - if backendRef.Ref != nil && types.IsServiceType(backendRef.Ref.Type) { + if backendRef.Ref != nil { backends = append(backends, backendRef.Ref) } } From b4e15ab687197c6d0b23f155ed59928ed4af183e Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Mon, 11 Sep 2023 17:06:06 -0500 Subject: [PATCH 49/49] this was dead code --- internal/mesh/internal/types/xroute.go | 54 -------------------------- 1 file changed, 54 deletions(-) diff --git a/internal/mesh/internal/types/xroute.go b/internal/mesh/internal/types/xroute.go index 0b8af6be42a..e9fe6df3826 100644 --- a/internal/mesh/internal/types/xroute.go +++ b/internal/mesh/internal/types/xroute.go @@ -13,7 +13,6 @@ import ( "github.com/hashicorp/consul/internal/catalog" "github.com/hashicorp/consul/internal/resource" pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" - "github.com/hashicorp/consul/proto-public/pbresource" ) type XRouteData interface { @@ -26,59 +25,6 @@ type XRouteWithRefs interface { GetUnderlyingBackendRefs() []*pbmesh.BackendReference } -type DecodedXRoute struct { - Resource *pbresource.Resource - HTTP *pbmesh.HTTPRoute - GRPC *pbmesh.GRPCRoute - TCP *pbmesh.TCPRoute -} - -var _ XRouteWithRefs = (*DecodedXRoute)(nil) - -func (d *DecodedXRoute) ToDecodedHTTPRoute() *DecodedHTTPRoute { - return &DecodedHTTPRoute{Resource: d.Resource, Data: d.HTTP} -} - -func (d *DecodedXRoute) ToDecodedGRPCRoute() *DecodedGRPCRoute { - return &DecodedGRPCRoute{Resource: d.Resource, Data: d.GRPC} -} - -func (d *DecodedXRoute) ToDecodedTCPRoute() *DecodedTCPRoute { - return &DecodedTCPRoute{Resource: d.Resource, Data: d.TCP} -} - -func (d *DecodedXRoute) GetParentRefs() []*pbmesh.ParentReference { - if d == nil { - return nil - } - switch { - case d.HTTP != nil: - return d.HTTP.GetParentRefs() - case d.GRPC != nil: - return d.GRPC.GetParentRefs() - case d.TCP != nil: - return d.TCP.GetParentRefs() - default: - return nil - } -} - -func (d *DecodedXRoute) GetUnderlyingBackendRefs() []*pbmesh.BackendReference { - if d == nil { - return nil - } - switch { - case d.HTTP != nil: - return d.HTTP.GetUnderlyingBackendRefs() - case d.GRPC != nil: - return d.GRPC.GetUnderlyingBackendRefs() - case d.TCP != nil: - return d.TCP.GetUnderlyingBackendRefs() - default: - return nil - } -} - type portedRefKey struct { Key resource.ReferenceKey Port string