consul/agent/xdsv2/resources.go

92 lines
3.7 KiB
Go
Raw Normal View History

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package xdsv2
import (
"fmt"
"github.com/hashicorp/go-hclog"
"google.golang.org/protobuf/proto"
"github.com/hashicorp/consul/envoyextensions/xdscommon"
proxytracker "github.com/hashicorp/consul/internal/mesh/proxy-tracker"
)
// ResourceGenerator is associated with a single gRPC stream and creates xDS
// resources for a single client.
type ResourceGenerator struct {
Logger hclog.Logger
ProxyFeatures xdscommon.SupportedProxyFeatures
}
// NewResourceGenerator will create a new ResourceGenerator.
func NewResourceGenerator(
logger hclog.Logger,
) *ResourceGenerator {
return &ResourceGenerator{
Logger: logger,
}
}
// ProxyResources is the main state used to convert proxyState resources to Envoy resources.
type ProxyResources struct {
// proxyState is the final proxyState computed by Consul controllers.
proxyState *proxytracker.ProxyState
// envoyResources is a map of each resource type (listener, endpoint, route, cluster, etc.)
// with a corresponding map of k/v pairs of resource name to envoy proto message.
// map[string]map[string]proto.Message is used over map[string][]proto.Message because
// AllResourcesFromIR() will create envoy resource by walking the object graph from listener
// to endpoint. In the process, the same resource might be referenced more than once,
// so the map is used to prevent duplicate resources being created and also will use
// an O(1) lookup to see if it exists (it actually will set the map key rather than
// checks everywhere) where as each lookup would be O(n) with a []proto structure.
envoyResources map[string]map[string]proto.Message
}
func (g *ResourceGenerator) AllResourcesFromIR(proxyState *proxytracker.ProxyState) (map[string][]proto.Message, error) {
pr := &ProxyResources{
proxyState: proxyState,
envoyResources: make(map[string]map[string]proto.Message),
}
pr.envoyResources[xdscommon.ListenerType] = make(map[string]proto.Message)
pr.envoyResources[xdscommon.RouteType] = make(map[string]proto.Message)
pr.envoyResources[xdscommon.ClusterType] = make(map[string]proto.Message)
pr.envoyResources[xdscommon.EndpointType] = make(map[string]proto.Message)
err := pr.makeEnvoyResourceGraphsStartingFromListeners()
if err != nil {
return nil, fmt.Errorf("failed to generate xDS resources for ProxyState: %v", err)
}
// Now account for Clusters that did not have a destination.
for name := range proxyState.Clusters {
if _, ok := pr.envoyResources[xdscommon.ClusterType][name]; !ok {
pr.addEnvoyClustersAndEndpointsToEnvoyResources(name)
}
}
envoyResources := convertResourceMapsToResourceArrays(pr.envoyResources)
return envoyResources, nil
}
// convertResourceMapsToResourceArrays will convert map[string]map[string]proto.Message, which is used to
// prevent duplicate resource being created, to map[string][]proto.Message which is used by Delta server.
func convertResourceMapsToResourceArrays(resourceMap map[string]map[string]proto.Message) map[string][]proto.Message {
resources := make(map[string][]proto.Message)
resources[xdscommon.ListenerType] = make([]proto.Message, 0)
resources[xdscommon.RouteType] = make([]proto.Message, 0)
resources[xdscommon.ClusterType] = make([]proto.Message, 0)
resources[xdscommon.EndpointType] = make([]proto.Message, 0)
// This conversion incurs processing cost which is done once in the generating envoy resources.
// This tradeoff is preferable to doing array scan every time an envoy resource needs to be
// to pr.envoyResource to see if it already exists.
for resourceTypeName, resourceMap := range resourceMap {
for _, resource := range resourceMap {
resources[resourceTypeName] = append(resources[resourceTypeName], resource)
}
}
return resources
}