From 696aa1bbd20f5b9b6557a8db9259e2995843fd50 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" <4903+rboyer@users.noreply.github.com> Date: Mon, 18 Sep 2023 16:19:54 -0500 Subject: [PATCH] mesh: update xds controller to synthesize empty endpoints when no endpoints ref is found (#18835) --- .../internal/controllers/xds/controller.go | 54 ++++++++++++------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/internal/mesh/internal/controllers/xds/controller.go b/internal/mesh/internal/controllers/xds/controller.go index f2aac840c1..604181a49e 100644 --- a/internal/mesh/internal/controllers/xds/controller.go +++ b/internal/mesh/internal/controllers/xds/controller.go @@ -24,6 +24,7 @@ import ( ) const ControllerName = "consul.io/xds-controller" + const defaultTenancy = "default" func Controller(endpointsMapper *bimapper.Mapper, updater ProxyUpdater, fetcher TrustBundleFetcher, leafCertManager *leafcert.Manager, leafMapper *LeafMapper, leafCancels *LeafCancels, datacenter string) controller.Controller { @@ -158,39 +159,52 @@ func (r *xdsReconciler) Reconcile(ctx context.Context, rt controller.Runtime, re // Step 1: Resolve the reference by looking up the ServiceEndpoints. // serviceEndpoints will not be nil unless there is an error. - serviceEndpoints, err := getServiceEndpoints(ctx, rt, endpointRef.Id) - if err != nil { - rt.Logger.Error("error reading service endpoint", "id", endpointRef.Id, "error", err) - // Set the status. - statusCondition = status.ConditionRejectedErrorReadingEndpoints(status.KeyFromID(endpointRef.Id), err.Error()) - status.WriteStatusIfChanged(ctx, rt, pstResource, statusCondition) + // + // TODO(rb/v2): note we should expose a flag on the endpointRef indicating if the user + // wants the absence of an Endpoints to imply returning a slice of no data, vs failing outright. + // In xdsv1 we call this the "allowEmpty" semantic. Here we are assuming "allowEmpty=true" + var psEndpoints *pbproxystate.Endpoints + if endpointRef.Id != nil { + serviceEndpoints, err := getServiceEndpoints(ctx, rt, endpointRef.Id) + if err != nil { + rt.Logger.Error("error reading service endpoint", "id", endpointRef.Id, "error", err) + // Set the status. + statusCondition = status.ConditionRejectedErrorReadingEndpoints(status.KeyFromID(endpointRef.Id), err.Error()) + status.WriteStatusIfChanged(ctx, rt, pstResource, statusCondition) - return err - } + return err + } - // Step 2: Translate it into pbproxystate.Endpoints. - psEndpoints, err := generateProxyStateEndpoints(serviceEndpoints, endpointRef.Port) - if err != nil { - rt.Logger.Error("error translating service endpoints to proxy state endpoints", "endpoint", endpointRef.Id, "error", err) + // Step 2: Translate it into pbproxystate.Endpoints. + psEndpoints, err = generateProxyStateEndpoints(serviceEndpoints, endpointRef.Port) + if err != nil { + rt.Logger.Error("error translating service endpoints to proxy state endpoints", "endpoint", endpointRef.Id, "error", err) - // Set the status. - statusCondition = status.ConditionRejectedCreatingProxyStateEndpoints(status.KeyFromID(endpointRef.Id), err.Error()) - status.WriteStatusIfChanged(ctx, rt, pstResource, statusCondition) + // Set the status. + statusCondition = status.ConditionRejectedCreatingProxyStateEndpoints(status.KeyFromID(endpointRef.Id), err.Error()) + status.WriteStatusIfChanged(ctx, rt, pstResource, statusCondition) - return err + return err + } + } else { + psEndpoints = &pbproxystate.Endpoints{} } // Step 3: Add the endpoints to ProxyState. proxyStateTemplate.Template.ProxyState.Endpoints[xdsClusterName] = psEndpoints - // Track all the endpoints that are used by this ProxyStateTemplate, so we can use this for step 4. - endpointResourceRef := resource.Reference(endpointRef.Id, "") - endpointsInProxyStateTemplate = append(endpointsInProxyStateTemplate, endpointResourceRef) - + if endpointRef.Id != nil { + // Track all the endpoints that are used by this ProxyStateTemplate, so we can use this for step 4. + endpointResourceRef := resource.Reference(endpointRef.Id, "") + endpointsInProxyStateTemplate = append(endpointsInProxyStateTemplate, endpointResourceRef) + } } // Step 4: Track relationships between ProxyStateTemplates and ServiceEndpoints. r.endpointsMapper.TrackItem(req.ID, endpointsInProxyStateTemplate) + if len(endpointsInProxyStateTemplate) == 0 { + r.endpointsMapper.UntrackItem(req.ID) + } // Iterate through leaf certificate references. // For each leaf certificate reference, the controller should: