refactor: move service to service validation to troubleshoot package (#16132)

This is to reduce the dependency on xds from within the troubleshoot package.
This commit is contained in:
Nitya Dhanushkodi 2023-02-02 22:18:10 -08:00 committed by GitHub
parent ffd311c2b7
commit 6151bcfa75
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 179 additions and 123 deletions

View file

@ -8,6 +8,7 @@ import (
"text/template" "text/template"
envoy_cluster_v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" envoy_cluster_v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
"github.com/hashicorp/consul/agent/xds/testcommon"
testinf "github.com/mitchellh/go-testing-interface" testinf "github.com/mitchellh/go-testing-interface"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -769,7 +770,7 @@ func TestClustersFromSnapshot(t *testing.T) {
latestEnvoyVersion := proxysupport.EnvoyVersions[0] latestEnvoyVersion := proxysupport.EnvoyVersions[0]
for _, envoyVersion := range proxysupport.EnvoyVersions { for _, envoyVersion := range proxysupport.EnvoyVersions {
sf, err := determineSupportedProxyFeaturesFromString(envoyVersion) sf, err := xdscommon.DetermineSupportedProxyFeaturesFromString(envoyVersion)
require.NoError(t, err) require.NoError(t, err)
t.Run("envoy-"+envoyVersion, func(t *testing.T) { t.Run("envoy-"+envoyVersion, func(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
@ -780,10 +781,10 @@ func TestClustersFromSnapshot(t *testing.T) {
// We need to replace the TLS certs with deterministic ones to make golden // We need to replace the TLS certs with deterministic ones to make golden
// files workable. Note we don't update these otherwise they'd change // files workable. Note we don't update these otherwise they'd change
// golder files for every test case and so not be any use! // golder files for every test case and so not be any use!
setupTLSRootsAndLeaf(t, snap) testcommon.SetupTLSRootsAndLeaf(t, snap)
// Need server just for logger dependency // Need server just for logger dependency
g := newResourceGenerator(testutil.Logger(t), nil, false) g := NewResourceGenerator(testutil.Logger(t), nil, false)
g.ProxyFeatures = sf g.ProxyFeatures = sf
clusters, err := g.clustersFromSnapshot(snap) clusters, err := g.clustersFromSnapshot(snap)
@ -861,25 +862,6 @@ func customAppClusterJSON(t testinf.T, opts customClusterJSONOptions) string {
return buf.String() return buf.String()
} }
func setupTLSRootsAndLeaf(t *testing.T, snap *proxycfg.ConfigSnapshot) {
if snap.Leaf() != nil {
switch snap.Kind {
case structs.ServiceKindConnectProxy:
snap.ConnectProxy.Leaf.CertPEM = loadTestResource(t, "test-leaf-cert")
snap.ConnectProxy.Leaf.PrivateKeyPEM = loadTestResource(t, "test-leaf-key")
case structs.ServiceKindIngressGateway:
snap.IngressGateway.Leaf.CertPEM = loadTestResource(t, "test-leaf-cert")
snap.IngressGateway.Leaf.PrivateKeyPEM = loadTestResource(t, "test-leaf-key")
case structs.ServiceKindMeshGateway:
snap.MeshGateway.Leaf.CertPEM = loadTestResource(t, "test-leaf-cert")
snap.MeshGateway.Leaf.PrivateKeyPEM = loadTestResource(t, "test-leaf-key")
}
}
if snap.Roots != nil {
snap.Roots.Roots[0].RootCert = loadTestResource(t, "test-root-cert")
}
}
func TestEnvoyLBConfig_InjectToCluster(t *testing.T) { func TestEnvoyLBConfig_InjectToCluster(t *testing.T) {
var tests = []struct { var tests = []struct {
name string name string

View file

@ -11,14 +11,10 @@ import (
"time" "time"
"github.com/armon/go-metrics" "github.com/armon/go-metrics"
envoy_cluster_v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
envoy_endpoint_v3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"
envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
envoy_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
envoy_discovery_v3 "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" envoy_discovery_v3 "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
"github.com/hashicorp/go-hclog"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
@ -121,7 +117,7 @@ func (s *Server) processDelta(stream ADSDeltaStream, reqCh <-chan *envoy_discove
currentVersions = make(map[string]map[string]string) currentVersions = make(map[string]map[string]string)
) )
generator := newResourceGenerator( generator := NewResourceGenerator(
s.Logger.Named(logging.XDS).With("xdsVersion", "v3"), s.Logger.Named(logging.XDS).With("xdsVersion", "v3"),
s.CfgFetcher, s.CfgFetcher,
true, true,
@ -206,7 +202,7 @@ func (s *Server) processDelta(stream ADSDeltaStream, reqCh <-chan *envoy_discove
if node == nil && req.Node != nil { if node == nil && req.Node != nil {
node = req.Node node = req.Node
var err error var err error
generator.ProxyFeatures, err = determineSupportedProxyFeatures(req.Node) generator.ProxyFeatures, err = xdscommon.DetermineSupportedProxyFeatures(req.Node)
if err != nil { if err != nil {
return status.Errorf(codes.InvalidArgument, err.Error()) return status.Errorf(codes.InvalidArgument, err.Error())
} }
@ -239,13 +235,13 @@ func (s *Server) processDelta(stream ADSDeltaStream, reqCh <-chan *envoy_discove
} }
cfgSnap = cs cfgSnap = cs
newRes, err := generator.allResourcesFromSnapshot(cfgSnap) newRes, err := generator.AllResourcesFromSnapshot(cfgSnap)
if err != nil { if err != nil {
return status.Errorf(codes.Unavailable, "failed to generate all xDS resources from the snapshot: %v", err) return status.Errorf(codes.Unavailable, "failed to generate all xDS resources from the snapshot: %v", err)
} }
// index and hash the xDS structures // index and hash the xDS structures
newResourceMap := indexResources(generator.Logger, newRes) newResourceMap := xdscommon.IndexResources(generator.Logger, newRes)
if s.ResourceMapMutateFn != nil { if s.ResourceMapMutateFn != nil {
s.ResourceMapMutateFn(newResourceMap) s.ResourceMapMutateFn(newResourceMap)
@ -592,7 +588,7 @@ func newDeltaType(
// Recv handles new discovery requests from envoy. // Recv handles new discovery requests from envoy.
// //
// Returns true the first time a type receives a request. // Returns true the first time a type receives a request.
func (t *xDSDeltaType) Recv(req *envoy_discovery_v3.DeltaDiscoveryRequest, sf supportedProxyFeatures) deltaRecvResponse { func (t *xDSDeltaType) Recv(req *envoy_discovery_v3.DeltaDiscoveryRequest, sf xdscommon.SupportedProxyFeatures) deltaRecvResponse {
if t == nil { if t == nil {
return deltaRecvUnknownType // not something we care about return deltaRecvUnknownType // not something we care about
} }
@ -987,39 +983,6 @@ func populateChildIndexMap(resourceMap *xdscommon.IndexedResources) error {
return nil return nil
} }
func indexResources(logger hclog.Logger, resources map[string][]proto.Message) *xdscommon.IndexedResources {
data := xdscommon.EmptyIndexedResources()
for typeURL, typeRes := range resources {
for _, res := range typeRes {
name := getResourceName(res)
if name == "" {
logger.Warn("skipping unexpected xDS type found in delta snapshot", "typeURL", typeURL)
} else {
data.Index[typeURL][name] = res
}
}
}
return data
}
func getResourceName(res proto.Message) string {
// NOTE: this only covers types that we currently care about for LDS/RDS/CDS/EDS
switch x := res.(type) {
case *envoy_listener_v3.Listener: // LDS
return x.Name
case *envoy_route_v3.RouteConfiguration: // RDS
return x.Name
case *envoy_cluster_v3.Cluster: // CDS
return x.Name
case *envoy_endpoint_v3.ClusterLoadAssignment: // EDS
return x.ClusterName
default:
return ""
}
}
func hashResourceMap(resources map[string]proto.Message) (map[string]string, error) { func hashResourceMap(resources map[string]proto.Message) (map[string]string, error) {
m := make(map[string]string) m := make(map[string]string)
for name, res := range resources { for name, res := range resources {

View file

@ -12,6 +12,7 @@ import (
envoy_endpoint_v3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" envoy_endpoint_v3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"
envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
envoy_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" envoy_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
"github.com/hashicorp/consul/agent/xds/testcommon"
testinf "github.com/mitchellh/go-testing-interface" testinf "github.com/mitchellh/go-testing-interface"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
@ -212,7 +213,7 @@ end`,
latestEnvoyVersion := proxysupport.EnvoyVersions[0] latestEnvoyVersion := proxysupport.EnvoyVersions[0]
for _, envoyVersion := range proxysupport.EnvoyVersions { for _, envoyVersion := range proxysupport.EnvoyVersions {
sf, err := determineSupportedProxyFeaturesFromString(envoyVersion) sf, err := xdscommon.DetermineSupportedProxyFeaturesFromString(envoyVersion)
require.NoError(t, err) require.NoError(t, err)
t.Run("envoy-"+envoyVersion, func(t *testing.T) { t.Run("envoy-"+envoyVersion, func(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
@ -223,15 +224,15 @@ end`,
// We need to replace the TLS certs with deterministic ones to make golden // We need to replace the TLS certs with deterministic ones to make golden
// files workable. Note we don't update these otherwise they'd change // files workable. Note we don't update these otherwise they'd change
// golden files for every test case and so not be any use! // golden files for every test case and so not be any use!
setupTLSRootsAndLeaf(t, snap) testcommon.SetupTLSRootsAndLeaf(t, snap)
g := newResourceGenerator(testutil.Logger(t), nil, false) g := NewResourceGenerator(testutil.Logger(t), nil, false)
g.ProxyFeatures = sf g.ProxyFeatures = sf
res, err := g.allResourcesFromSnapshot(snap) res, err := g.AllResourcesFromSnapshot(snap)
require.NoError(t, err) require.NoError(t, err)
indexedResources := indexResources(g.Logger, res) indexedResources := xdscommon.IndexResources(g.Logger, res)
cfgs := extensionruntime.GetRuntimeConfigurations(snap) cfgs := extensionruntime.GetRuntimeConfigurations(snap)
for _, extensions := range cfgs { for _, extensions := range cfgs {
for _, ext := range extensions { for _, ext := range extensions {

View file

@ -1538,7 +1538,7 @@ func assertDeltaResponse(t *testing.T, got, want *envoy_discovery_v3.DeltaDiscov
func mustMakeVersionMap(t *testing.T, resources ...proto.Message) map[string]string { func mustMakeVersionMap(t *testing.T, resources ...proto.Message) map[string]string {
m := make(map[string]string) m := make(map[string]string)
for _, res := range resources { for _, res := range resources {
name := getResourceName(res) name := xdscommon.GetResourceName(res)
m[name] = mustHashResource(t, res) m[name] = mustHashResource(t, res)
} }
return m return m

View file

@ -8,6 +8,7 @@ import (
envoy_cluster_v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" envoy_cluster_v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
envoy_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" envoy_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
envoy_endpoint_v3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" envoy_endpoint_v3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"
"github.com/hashicorp/consul/agent/xds/xdscommon"
"github.com/hashicorp/go-bexpr" "github.com/hashicorp/go-bexpr"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
@ -782,7 +783,7 @@ func (s *ResourceGenerator) makeExportedUpstreamEndpointsForMeshGateway(cfgSnap
return nil, err return nil, err
} }
for _, endpoints := range clusterEndpoints { for _, endpoints := range clusterEndpoints {
clusterName := getResourceName(endpoints) clusterName := xdscommon.GetResourceName(endpoints)
if _, ok := populatedExportedClusters[clusterName]; ok { if _, ok := populatedExportedClusters[clusterName]; ok {
continue continue
} }

View file

@ -7,6 +7,7 @@ import (
envoy_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" envoy_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
envoy_endpoint_v3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" envoy_endpoint_v3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"
"github.com/hashicorp/consul/agent/xds/testcommon"
"github.com/mitchellh/copystructure" "github.com/mitchellh/copystructure"
testinf "github.com/mitchellh/go-testing-interface" testinf "github.com/mitchellh/go-testing-interface"
@ -500,7 +501,7 @@ func TestEndpointsFromSnapshot(t *testing.T) {
latestEnvoyVersion := proxysupport.EnvoyVersions[0] latestEnvoyVersion := proxysupport.EnvoyVersions[0]
for _, envoyVersion := range proxysupport.EnvoyVersions { for _, envoyVersion := range proxysupport.EnvoyVersions {
sf, err := determineSupportedProxyFeaturesFromString(envoyVersion) sf, err := xdscommon.DetermineSupportedProxyFeaturesFromString(envoyVersion)
require.NoError(t, err) require.NoError(t, err)
t.Run("envoy-"+envoyVersion, func(t *testing.T) { t.Run("envoy-"+envoyVersion, func(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
@ -511,10 +512,10 @@ func TestEndpointsFromSnapshot(t *testing.T) {
// We need to replace the TLS certs with deterministic ones to make golden // We need to replace the TLS certs with deterministic ones to make golden
// files workable. Note we don't update these otherwise they'd change // files workable. Note we don't update these otherwise they'd change
// golden files for every test case and so not be any use! // golden files for every test case and so not be any use!
setupTLSRootsAndLeaf(t, snap) testcommon.SetupTLSRootsAndLeaf(t, snap)
// Need server just for logger dependency // Need server just for logger dependency
g := newResourceGenerator(testutil.Logger(t), nil, false) g := NewResourceGenerator(testutil.Logger(t), nil, false)
g.ProxyFeatures = sf g.ProxyFeatures = sf
endpoints, err := g.endpointsFromSnapshot(snap) endpoints, err := g.endpointsFromSnapshot(snap)

View file

@ -123,14 +123,6 @@ func golden(t *testing.T, name, subname, latestSubname, got string) string {
return string(expected) return string(expected)
} }
func loadTestResource(t *testing.T, name string) string {
t.Helper()
expected, err := os.ReadFile(filepath.Join("testdata", name+".golden"))
require.NoError(t, err)
return string(expected)
}
func protoToJSON(t *testing.T, pb proto.Message) string { func protoToJSON(t *testing.T, pb proto.Message) string {
t.Helper() t.Helper()
m := protojson.MarshalOptions{ m := protojson.MarshalOptions{

View file

@ -7,6 +7,7 @@ import (
"testing" "testing"
"text/template" "text/template"
"github.com/hashicorp/consul/agent/xds/testcommon"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
@ -890,7 +891,7 @@ func TestListenersFromSnapshot(t *testing.T) {
latestEnvoyVersion := proxysupport.EnvoyVersions[0] latestEnvoyVersion := proxysupport.EnvoyVersions[0]
for _, envoyVersion := range proxysupport.EnvoyVersions { for _, envoyVersion := range proxysupport.EnvoyVersions {
sf, err := determineSupportedProxyFeaturesFromString(envoyVersion) sf, err := xdscommon.DetermineSupportedProxyFeaturesFromString(envoyVersion)
require.NoError(t, err) require.NoError(t, err)
t.Run("envoy-"+envoyVersion, func(t *testing.T) { t.Run("envoy-"+envoyVersion, func(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
@ -904,14 +905,14 @@ func TestListenersFromSnapshot(t *testing.T) {
// We need to replace the TLS certs with deterministic ones to make golden // We need to replace the TLS certs with deterministic ones to make golden
// files workable. Note we don't update these otherwise they'd change // files workable. Note we don't update these otherwise they'd change
// golder files for every test case and so not be any use! // golder files for every test case and so not be any use!
setupTLSRootsAndLeaf(t, snap) testcommon.SetupTLSRootsAndLeaf(t, snap)
if tt.setup != nil { if tt.setup != nil {
tt.setup(snap) tt.setup(snap)
} }
// Need server just for logger dependency // Need server just for logger dependency
g := newResourceGenerator(testutil.Logger(t), nil, false) g := NewResourceGenerator(testutil.Logger(t), nil, false)
g.ProxyFeatures = sf g.ProxyFeatures = sf
if tt.generatorSetup != nil { if tt.generatorSetup != nil {
tt.generatorSetup(g) tt.generatorSetup(g)

View file

@ -3,11 +3,11 @@ package xds
import ( import (
"fmt" "fmt"
"github.com/hashicorp/consul/agent/xds/xdscommon"
"github.com/hashicorp/go-hclog" "github.com/hashicorp/go-hclog"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
"github.com/hashicorp/consul/agent/proxycfg" "github.com/hashicorp/consul/agent/proxycfg"
"github.com/hashicorp/consul/agent/xds/xdscommon"
) )
// ResourceGenerator is associated with a single gRPC stream and creates xDS // ResourceGenerator is associated with a single gRPC stream and creates xDS
@ -17,10 +17,10 @@ type ResourceGenerator struct {
CfgFetcher ConfigFetcher CfgFetcher ConfigFetcher
IncrementalXDS bool IncrementalXDS bool
ProxyFeatures supportedProxyFeatures ProxyFeatures xdscommon.SupportedProxyFeatures
} }
func newResourceGenerator( func NewResourceGenerator(
logger hclog.Logger, logger hclog.Logger,
cfgFetcher ConfigFetcher, cfgFetcher ConfigFetcher,
incrementalXDS bool, incrementalXDS bool,
@ -32,7 +32,7 @@ func newResourceGenerator(
} }
} }
func (g *ResourceGenerator) allResourcesFromSnapshot(cfgSnap *proxycfg.ConfigSnapshot) (map[string][]proto.Message, error) { func (g *ResourceGenerator) AllResourcesFromSnapshot(cfgSnap *proxycfg.ConfigSnapshot) (map[string][]proto.Message, error) {
all := make(map[string][]proto.Message) all := make(map[string][]proto.Message)
for _, typeUrl := range []string{xdscommon.ListenerType, xdscommon.RouteType, xdscommon.ClusterType, xdscommon.EndpointType} { for _, typeUrl := range []string{xdscommon.ListenerType, xdscommon.RouteType, xdscommon.ClusterType, xdscommon.EndpointType} {
res, err := g.resourcesFromSnapshot(typeUrl, cfgSnap) res, err := g.resourcesFromSnapshot(typeUrl, cfgSnap)

View file

@ -9,6 +9,8 @@ import (
envoy_endpoint_v3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" envoy_endpoint_v3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"
envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
envoy_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" envoy_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
"github.com/hashicorp/consul/agent/xds/testcommon"
"github.com/hashicorp/consul/agent/xds/xdscommon"
testinf "github.com/mitchellh/go-testing-interface" testinf "github.com/mitchellh/go-testing-interface"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -16,7 +18,6 @@ import (
"github.com/hashicorp/consul/agent/proxycfg" "github.com/hashicorp/consul/agent/proxycfg"
"github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/agent/xds/proxysupport" "github.com/hashicorp/consul/agent/xds/proxysupport"
"github.com/hashicorp/consul/agent/xds/xdscommon"
"github.com/hashicorp/consul/sdk/testutil" "github.com/hashicorp/consul/sdk/testutil"
) )
@ -47,7 +48,7 @@ func TestAllResourcesFromSnapshot(t *testing.T) {
run := func( run := func(
t *testing.T, t *testing.T,
sf supportedProxyFeatures, sf xdscommon.SupportedProxyFeatures,
envoyVersion string, envoyVersion string,
latestEnvoyVersion string, latestEnvoyVersion string,
tt testcase, tt testcase,
@ -61,20 +62,20 @@ func TestAllResourcesFromSnapshot(t *testing.T) {
// We need to replace the TLS certs with deterministic ones to make golden // We need to replace the TLS certs with deterministic ones to make golden
// files workable. Note we don't update these otherwise they'd change // files workable. Note we don't update these otherwise they'd change
// golder files for every test case and so not be any use! // golder files for every test case and so not be any use!
setupTLSRootsAndLeaf(t, snap) testcommon.SetupTLSRootsAndLeaf(t, snap)
if tt.setup != nil { if tt.setup != nil {
tt.setup(snap) tt.setup(snap)
} }
// Need server just for logger dependency // Need server just for logger dependency
g := newResourceGenerator(testutil.Logger(t), nil, false) g := NewResourceGenerator(testutil.Logger(t), nil, false)
g.ProxyFeatures = sf g.ProxyFeatures = sf
if tt.generatorSetup != nil { if tt.generatorSetup != nil {
tt.generatorSetup(g) tt.generatorSetup(g)
} }
resources, err := g.allResourcesFromSnapshot(snap) resources, err := g.AllResourcesFromSnapshot(snap)
require.NoError(t, err) require.NoError(t, err)
typeUrls := []string{ typeUrls := []string{
@ -168,7 +169,7 @@ func TestAllResourcesFromSnapshot(t *testing.T) {
latestEnvoyVersion := proxysupport.EnvoyVersions[0] latestEnvoyVersion := proxysupport.EnvoyVersions[0]
for _, envoyVersion := range proxysupport.EnvoyVersions { for _, envoyVersion := range proxysupport.EnvoyVersions {
sf, err := determineSupportedProxyFeaturesFromString(envoyVersion) sf, err := xdscommon.DetermineSupportedProxyFeaturesFromString(envoyVersion)
require.NoError(t, err) require.NoError(t, err)
t.Run("envoy-"+envoyVersion, func(t *testing.T) { t.Run("envoy-"+envoyVersion, func(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {

View file

@ -7,6 +7,7 @@ import (
"time" "time"
envoy_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" envoy_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
"github.com/hashicorp/consul/agent/xds/testcommon"
testinf "github.com/mitchellh/go-testing-interface" testinf "github.com/mitchellh/go-testing-interface"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -190,7 +191,7 @@ func TestRoutesFromSnapshot(t *testing.T) {
latestEnvoyVersion := proxysupport.EnvoyVersions[0] latestEnvoyVersion := proxysupport.EnvoyVersions[0]
for _, envoyVersion := range proxysupport.EnvoyVersions { for _, envoyVersion := range proxysupport.EnvoyVersions {
sf, err := determineSupportedProxyFeaturesFromString(envoyVersion) sf, err := xdscommon.DetermineSupportedProxyFeaturesFromString(envoyVersion)
require.NoError(t, err) require.NoError(t, err)
t.Run("envoy-"+envoyVersion, func(t *testing.T) { t.Run("envoy-"+envoyVersion, func(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
@ -201,9 +202,9 @@ func TestRoutesFromSnapshot(t *testing.T) {
// We need to replace the TLS certs with deterministic ones to make golden // We need to replace the TLS certs with deterministic ones to make golden
// files workable. Note we don't update these otherwise they'd change // files workable. Note we don't update these otherwise they'd change
// golden files for every test case and so not be any use! // golden files for every test case and so not be any use!
setupTLSRootsAndLeaf(t, snap) testcommon.SetupTLSRootsAndLeaf(t, snap)
g := newResourceGenerator(testutil.Logger(t), nil, false) g := NewResourceGenerator(testutil.Logger(t), nil, false)
g.ProxyFeatures = sf g.ProxyFeatures = sf
routes, err := g.routesFromSnapshot(snap) routes, err := g.routesFromSnapshot(snap)

View file

@ -7,6 +7,7 @@ import (
"time" "time"
envoy_discovery_v3 "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" envoy_discovery_v3 "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
"github.com/hashicorp/consul/agent/xds/xdscommon"
"github.com/armon/go-metrics" "github.com/armon/go-metrics"
"github.com/armon/go-metrics/prometheus" "github.com/armon/go-metrics/prometheus"
@ -20,7 +21,6 @@ import (
"github.com/hashicorp/consul/agent/grpc-external/limiter" "github.com/hashicorp/consul/agent/grpc-external/limiter"
"github.com/hashicorp/consul/agent/proxycfg" "github.com/hashicorp/consul/agent/proxycfg"
"github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/agent/xds/xdscommon"
) )
var ( var (

View file

@ -0,0 +1,37 @@
package testcommon
import (
"os"
"path/filepath"
"testing"
"github.com/hashicorp/consul/agent/proxycfg"
"github.com/hashicorp/consul/agent/structs"
"github.com/stretchr/testify/require"
)
func SetupTLSRootsAndLeaf(t *testing.T, snap *proxycfg.ConfigSnapshot) {
if snap.Leaf() != nil {
switch snap.Kind {
case structs.ServiceKindConnectProxy:
snap.ConnectProxy.Leaf.CertPEM = loadTestResource(t, "test-leaf-cert")
snap.ConnectProxy.Leaf.PrivateKeyPEM = loadTestResource(t, "test-leaf-key")
case structs.ServiceKindIngressGateway:
snap.IngressGateway.Leaf.CertPEM = loadTestResource(t, "test-leaf-cert")
snap.IngressGateway.Leaf.PrivateKeyPEM = loadTestResource(t, "test-leaf-key")
case structs.ServiceKindMeshGateway:
snap.MeshGateway.Leaf.CertPEM = loadTestResource(t, "test-leaf-cert")
snap.MeshGateway.Leaf.PrivateKeyPEM = loadTestResource(t, "test-leaf-key")
}
}
if snap.Roots != nil {
snap.Roots.Roots[0].RootCert = loadTestResource(t, "test-root-cert")
}
}
func loadTestResource(t *testing.T, name string) string {
t.Helper()
expected, err := os.ReadFile(filepath.Join("testdata", name+".golden"))
require.NoError(t, err)
return string(expected)
}

View file

@ -8,6 +8,7 @@ import (
"github.com/hashicorp/consul/agent/connect" "github.com/hashicorp/consul/agent/connect"
"github.com/hashicorp/consul/agent/grpc-external/limiter" "github.com/hashicorp/consul/agent/grpc-external/limiter"
"github.com/hashicorp/consul/agent/xds/xdscommon"
envoy_cluster_v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" envoy_cluster_v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
envoy_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" envoy_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
@ -371,7 +372,7 @@ func makeTestResource(t *testing.T, raw interface{}) *envoy_discovery_v3.Resourc
require.NoError(t, err) require.NoError(t, err)
return &envoy_discovery_v3.Resource{ return &envoy_discovery_v3.Resource{
Name: getResourceName(res), Name: xdscommon.GetResourceName(res),
Version: mustHashResource(t, res), Version: mustHashResource(t, res),
Resource: any, Resource: any,
} }

View file

@ -1,4 +1,4 @@
package xds package xdscommon
import ( import (
"fmt" "fmt"
@ -23,35 +23,35 @@ type unsupportedVersion struct {
Why string Why string
} }
type supportedProxyFeatures struct { type SupportedProxyFeatures struct {
// Put feature switches here when necessary. For reference, The most recent remove of a feature flag was removed in // Put feature switches here when necessary. For reference, The most recent remove of a feature flag was removed in
// <insert PR here>. // <insert PR here>.
} }
func determineSupportedProxyFeatures(node *envoy_core_v3.Node) (supportedProxyFeatures, error) { func DetermineSupportedProxyFeatures(node *envoy_core_v3.Node) (SupportedProxyFeatures, error) {
version := determineEnvoyVersionFromNode(node) version := determineEnvoyVersionFromNode(node)
return determineSupportedProxyFeaturesFromVersion(version) return determineSupportedProxyFeaturesFromVersion(version)
} }
func determineSupportedProxyFeaturesFromString(vs string) (supportedProxyFeatures, error) { func DetermineSupportedProxyFeaturesFromString(vs string) (SupportedProxyFeatures, error) {
version := version.Must(version.NewVersion(vs)) version := version.Must(version.NewVersion(vs))
return determineSupportedProxyFeaturesFromVersion(version) return determineSupportedProxyFeaturesFromVersion(version)
} }
func determineSupportedProxyFeaturesFromVersion(version *version.Version) (supportedProxyFeatures, error) { func determineSupportedProxyFeaturesFromVersion(version *version.Version) (SupportedProxyFeatures, error) {
if version == nil { if version == nil {
// This would happen on either extremely old builds OR perhaps on // This would happen on either extremely old builds OR perhaps on
// custom builds. Should we error? // custom builds. Should we error?
return supportedProxyFeatures{}, nil return SupportedProxyFeatures{}, nil
} }
if version.LessThan(minSupportedVersion) { if version.LessThan(minSupportedVersion) {
return supportedProxyFeatures{}, fmt.Errorf("Envoy %s is too old and is not supported by Consul", version) return SupportedProxyFeatures{}, fmt.Errorf("Envoy %s is too old and is not supported by Consul", version)
} }
for _, uv := range specificUnsupportedVersions { for _, uv := range specificUnsupportedVersions {
if version.Equal(uv.Version) { if version.Equal(uv.Version) {
return supportedProxyFeatures{}, fmt.Errorf( return SupportedProxyFeatures{}, fmt.Errorf(
"Envoy %s is too old of a point release and is not supported by Consul because it %s. "+ "Envoy %s is too old of a point release and is not supported by Consul because it %s. "+
"Please upgrade to version %s.", "Please upgrade to version %s.",
version, version,
@ -61,7 +61,7 @@ func determineSupportedProxyFeaturesFromVersion(version *version.Version) (suppo
} }
} }
sf := supportedProxyFeatures{} sf := SupportedProxyFeatures{}
// when feature flags necessary, populate here by calling version.LessThan(...) // when feature flags necessary, populate here by calling version.LessThan(...)

View file

@ -1,4 +1,4 @@
package xds package xdscommon
import ( import (
"testing" "testing"
@ -72,7 +72,7 @@ func TestDetermineSupportedProxyFeaturesFromString(t *testing.T) {
) )
type testcase struct { type testcase struct {
expect supportedProxyFeatures expect SupportedProxyFeatures
expectErr string expectErr string
} }
@ -129,7 +129,7 @@ func TestDetermineSupportedProxyFeaturesFromString(t *testing.T) {
for _, v := range []string{ for _, v := range []string{
"1.18.0", "1.18.1", "1.18.2", "1.18.3", "1.18.4", "1.18.5", "1.18.6", "1.18.0", "1.18.1", "1.18.2", "1.18.3", "1.18.4", "1.18.5", "1.18.6",
} { } {
cases[v] = testcase{expect: supportedProxyFeatures{ cases[v] = testcase{expect: SupportedProxyFeatures{
ForceLDSandCDSToAlwaysUseWildcardsOnReconnect: true, ForceLDSandCDSToAlwaysUseWildcardsOnReconnect: true,
}} }}
} }
@ -140,13 +140,13 @@ func TestDetermineSupportedProxyFeaturesFromString(t *testing.T) {
"1.23.0", "1.23.1", "1.23.2", "1.23.0", "1.23.1", "1.23.2",
"1.24.0", "1.24.0",
} { } {
cases[v] = testcase{expect: supportedProxyFeatures{}} cases[v] = testcase{expect: SupportedProxyFeatures{}}
} }
for name, tc := range cases { for name, tc := range cases {
tc := tc tc := tc
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
sf, err := determineSupportedProxyFeaturesFromString(name) sf, err := DetermineSupportedProxyFeaturesFromString(name)
if tc.expectErr == "" { if tc.expectErr == "" {
require.Equal(t, tc.expect, sf) require.Equal(t, tc.expect, sf)
} else { } else {

View file

@ -1,6 +1,11 @@
package xdscommon package xdscommon
import ( import (
envoy_cluster_v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
envoy_endpoint_v3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"
envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
envoy_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
"github.com/hashicorp/go-hclog"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
) )
@ -53,6 +58,39 @@ type IndexedResources struct {
ChildIndex map[string]map[string][]string ChildIndex map[string]map[string][]string
} }
func IndexResources(logger hclog.Logger, resources map[string][]proto.Message) *IndexedResources {
data := EmptyIndexedResources()
for typeURL, typeRes := range resources {
for _, res := range typeRes {
name := GetResourceName(res)
if name == "" {
logger.Warn("skipping unexpected xDS type found in delta snapshot", "typeURL", typeURL)
} else {
data.Index[typeURL][name] = res
}
}
}
return data
}
func GetResourceName(res proto.Message) string {
// NOTE: this only covers types that we currently care about for LDS/RDS/CDS/EDS
switch x := res.(type) {
case *envoy_listener_v3.Listener: // LDS
return x.Name
case *envoy_route_v3.RouteConfiguration: // RDS
return x.Name
case *envoy_cluster_v3.Cluster: // CDS
return x.Name
case *envoy_endpoint_v3.ClusterLoadAssignment: // EDS
return x.ClusterName
default:
return ""
}
}
func EmptyIndexedResources() *IndexedResources { func EmptyIndexedResources() *IndexedResources {
return &IndexedResources{ return &IndexedResources{
Index: map[string]map[string]proto.Message{ Index: map[string]map[string]proto.Message{

View file

@ -0,0 +1,16 @@
-----BEGIN CERTIFICATE-----
MIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ
VGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG
A1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR
AB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7
SkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD
AgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6
NDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6
NWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf
ZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6
ZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw
WQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1
NTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG
SM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA
pY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=
-----END CERTIFICATE-----

View file

@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49
AwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav
q5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==
-----END EC PRIVATE KEY-----

View file

@ -0,0 +1,15 @@
-----BEGIN CERTIFICATE-----
MIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ
VGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG
A1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx
AsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2
ThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD
AQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi
ZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3
Mzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi
MjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1
ZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x
MTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG
SM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA
oR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=
-----END CERTIFICATE-----

View file

@ -1,4 +1,4 @@
package xds package validateupstream
import ( import (
"github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/acl"

View file

@ -1,4 +1,4 @@
package xds package validateupstream
import ( import (
"io" "io"
@ -7,6 +7,8 @@ import (
envoy_admin_v3 "github.com/envoyproxy/go-control-plane/envoy/admin/v3" envoy_admin_v3 "github.com/envoyproxy/go-control-plane/envoy/admin/v3"
"github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/agent/xds"
"github.com/hashicorp/consul/agent/xds/testcommon"
testinf "github.com/mitchellh/go-testing-interface" testinf "github.com/mitchellh/go-testing-interface"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -205,7 +207,7 @@ func TestValidateUpstreams(t *testing.T) {
} }
latestEnvoyVersion := proxysupport.EnvoyVersions[0] latestEnvoyVersion := proxysupport.EnvoyVersions[0]
sf, err := determineSupportedProxyFeaturesFromString(latestEnvoyVersion) sf, err := xdscommon.DetermineSupportedProxyFeaturesFromString(latestEnvoyVersion)
require.NoError(t, err) require.NoError(t, err)
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
@ -215,15 +217,15 @@ func TestValidateUpstreams(t *testing.T) {
// We need to replace the TLS certs with deterministic ones to make golden // We need to replace the TLS certs with deterministic ones to make golden
// files workable. Note we don't update these otherwise they'd change // files workable. Note we don't update these otherwise they'd change
// golden files for every test case and so not be any use! // golden files for every test case and so not be any use!
setupTLSRootsAndLeaf(t, snap) testcommon.SetupTLSRootsAndLeaf(t, snap)
g := newResourceGenerator(testutil.Logger(t), nil, false) g := xds.NewResourceGenerator(testutil.Logger(t), nil, false)
g.ProxyFeatures = sf g.ProxyFeatures = sf
res, err := g.allResourcesFromSnapshot(snap) res, err := g.AllResourcesFromSnapshot(snap)
require.NoError(t, err) require.NoError(t, err)
indexedResources := indexResources(g.Logger, res) indexedResources := xdscommon.IndexResources(g.Logger, res)
if tt.patcher != nil { if tt.patcher != nil {
indexedResources = tt.patcher(indexedResources) indexedResources = tt.patcher(indexedResources)
} }
@ -249,8 +251,7 @@ func TestValidateUpstreams(t *testing.T) {
} }
} }
// TODO make config.json and clusters.json use an http upstream with L7 config entries for more confidence. func TestValidate2(t *testing.T) {
func TestValidate(t *testing.T) {
indexedResources := getConfig(t) indexedResources := getConfig(t)
clusters := getClusters(t) clusters := getClusters(t)
err := Validate(indexedResources, service, "", "", true, clusters) err := Validate(indexedResources, service, "", "", true, clusters)