diff --git a/agent/xds/clusters_test.go b/agent/xds/clusters_test.go index c5fe91b4a..c3813c722 100644 --- a/agent/xds/clusters_test.go +++ b/agent/xds/clusters_test.go @@ -8,6 +8,7 @@ import ( "text/template" 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" "github.com/stretchr/testify/require" @@ -769,7 +770,7 @@ func TestClustersFromSnapshot(t *testing.T) { latestEnvoyVersion := proxysupport.EnvoyVersions[0] for _, envoyVersion := range proxysupport.EnvoyVersions { - sf, err := determineSupportedProxyFeaturesFromString(envoyVersion) + sf, err := xdscommon.DetermineSupportedProxyFeaturesFromString(envoyVersion) require.NoError(t, err) t.Run("envoy-"+envoyVersion, func(t *testing.T) { 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 // files workable. Note we don't update these otherwise they'd change // 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 - g := newResourceGenerator(testutil.Logger(t), nil, false) + g := NewResourceGenerator(testutil.Logger(t), nil, false) g.ProxyFeatures = sf clusters, err := g.clustersFromSnapshot(snap) @@ -861,25 +862,6 @@ func customAppClusterJSON(t testinf.T, opts customClusterJSONOptions) 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) { var tests = []struct { name string diff --git a/agent/xds/delta.go b/agent/xds/delta.go index a376341c4..416fdff8b 100644 --- a/agent/xds/delta.go +++ b/agent/xds/delta.go @@ -11,14 +11,10 @@ import ( "time" "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_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" 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/status" "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) ) - generator := newResourceGenerator( + generator := NewResourceGenerator( s.Logger.Named(logging.XDS).With("xdsVersion", "v3"), s.CfgFetcher, true, @@ -206,7 +202,7 @@ func (s *Server) processDelta(stream ADSDeltaStream, reqCh <-chan *envoy_discove if node == nil && req.Node != nil { node = req.Node var err error - generator.ProxyFeatures, err = determineSupportedProxyFeatures(req.Node) + generator.ProxyFeatures, err = xdscommon.DetermineSupportedProxyFeatures(req.Node) if err != nil { return status.Errorf(codes.InvalidArgument, err.Error()) } @@ -239,13 +235,13 @@ func (s *Server) processDelta(stream ADSDeltaStream, reqCh <-chan *envoy_discove } cfgSnap = cs - newRes, err := generator.allResourcesFromSnapshot(cfgSnap) + newRes, err := generator.AllResourcesFromSnapshot(cfgSnap) if err != nil { return status.Errorf(codes.Unavailable, "failed to generate all xDS resources from the snapshot: %v", err) } // index and hash the xDS structures - newResourceMap := indexResources(generator.Logger, newRes) + newResourceMap := xdscommon.IndexResources(generator.Logger, newRes) if s.ResourceMapMutateFn != nil { s.ResourceMapMutateFn(newResourceMap) @@ -592,7 +588,7 @@ func newDeltaType( // Recv handles new discovery requests from envoy. // // 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 { return deltaRecvUnknownType // not something we care about } @@ -987,39 +983,6 @@ func populateChildIndexMap(resourceMap *xdscommon.IndexedResources) error { 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) { m := make(map[string]string) for name, res := range resources { diff --git a/agent/xds/delta_envoy_extender_oss_test.go b/agent/xds/delta_envoy_extender_oss_test.go index 3bdfd2724..cd548c41d 100644 --- a/agent/xds/delta_envoy_extender_oss_test.go +++ b/agent/xds/delta_envoy_extender_oss_test.go @@ -12,6 +12,7 @@ import ( 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/consul/agent/xds/testcommon" testinf "github.com/mitchellh/go-testing-interface" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" @@ -212,7 +213,7 @@ end`, latestEnvoyVersion := proxysupport.EnvoyVersions[0] for _, envoyVersion := range proxysupport.EnvoyVersions { - sf, err := determineSupportedProxyFeaturesFromString(envoyVersion) + sf, err := xdscommon.DetermineSupportedProxyFeaturesFromString(envoyVersion) require.NoError(t, err) t.Run("envoy-"+envoyVersion, func(t *testing.T) { for _, tt := range tests { @@ -223,15 +224,15 @@ end`, // 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 // 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 - res, err := g.allResourcesFromSnapshot(snap) + res, err := g.AllResourcesFromSnapshot(snap) require.NoError(t, err) - indexedResources := indexResources(g.Logger, res) + indexedResources := xdscommon.IndexResources(g.Logger, res) cfgs := extensionruntime.GetRuntimeConfigurations(snap) for _, extensions := range cfgs { for _, ext := range extensions { diff --git a/agent/xds/delta_test.go b/agent/xds/delta_test.go index b3cb9ace0..0b7359da5 100644 --- a/agent/xds/delta_test.go +++ b/agent/xds/delta_test.go @@ -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 { m := make(map[string]string) for _, res := range resources { - name := getResourceName(res) + name := xdscommon.GetResourceName(res) m[name] = mustHashResource(t, res) } return m diff --git a/agent/xds/endpoints.go b/agent/xds/endpoints.go index 9c76f43f8..dd22f1c34 100644 --- a/agent/xds/endpoints.go +++ b/agent/xds/endpoints.go @@ -8,6 +8,7 @@ import ( 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_endpoint_v3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" + "github.com/hashicorp/consul/agent/xds/xdscommon" "github.com/hashicorp/go-bexpr" "google.golang.org/protobuf/proto" @@ -782,7 +783,7 @@ func (s *ResourceGenerator) makeExportedUpstreamEndpointsForMeshGateway(cfgSnap return nil, err } for _, endpoints := range clusterEndpoints { - clusterName := getResourceName(endpoints) + clusterName := xdscommon.GetResourceName(endpoints) if _, ok := populatedExportedClusters[clusterName]; ok { continue } diff --git a/agent/xds/endpoints_test.go b/agent/xds/endpoints_test.go index 725fff00f..d87abb5b8 100644 --- a/agent/xds/endpoints_test.go +++ b/agent/xds/endpoints_test.go @@ -7,6 +7,7 @@ import ( 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" + "github.com/hashicorp/consul/agent/xds/testcommon" "github.com/mitchellh/copystructure" testinf "github.com/mitchellh/go-testing-interface" @@ -500,7 +501,7 @@ func TestEndpointsFromSnapshot(t *testing.T) { latestEnvoyVersion := proxysupport.EnvoyVersions[0] for _, envoyVersion := range proxysupport.EnvoyVersions { - sf, err := determineSupportedProxyFeaturesFromString(envoyVersion) + sf, err := xdscommon.DetermineSupportedProxyFeaturesFromString(envoyVersion) require.NoError(t, err) t.Run("envoy-"+envoyVersion, func(t *testing.T) { 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 // files workable. Note we don't update these otherwise they'd change // 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 - g := newResourceGenerator(testutil.Logger(t), nil, false) + g := NewResourceGenerator(testutil.Logger(t), nil, false) g.ProxyFeatures = sf endpoints, err := g.endpointsFromSnapshot(snap) diff --git a/agent/xds/golden_test.go b/agent/xds/golden_test.go index f00b60609..420285203 100644 --- a/agent/xds/golden_test.go +++ b/agent/xds/golden_test.go @@ -123,14 +123,6 @@ func golden(t *testing.T, name, subname, latestSubname, got string) string { 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 { t.Helper() m := protojson.MarshalOptions{ diff --git a/agent/xds/listeners_test.go b/agent/xds/listeners_test.go index a97a77d76..c22e43c50 100644 --- a/agent/xds/listeners_test.go +++ b/agent/xds/listeners_test.go @@ -7,6 +7,7 @@ import ( "testing" "text/template" + "github.com/hashicorp/consul/agent/xds/testcommon" "github.com/stretchr/testify/assert" 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] for _, envoyVersion := range proxysupport.EnvoyVersions { - sf, err := determineSupportedProxyFeaturesFromString(envoyVersion) + sf, err := xdscommon.DetermineSupportedProxyFeaturesFromString(envoyVersion) require.NoError(t, err) t.Run("envoy-"+envoyVersion, func(t *testing.T) { 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 // files workable. Note we don't update these otherwise they'd change // golder files for every test case and so not be any use! - setupTLSRootsAndLeaf(t, snap) + testcommon.SetupTLSRootsAndLeaf(t, snap) if tt.setup != nil { tt.setup(snap) } // Need server just for logger dependency - g := newResourceGenerator(testutil.Logger(t), nil, false) + g := NewResourceGenerator(testutil.Logger(t), nil, false) g.ProxyFeatures = sf if tt.generatorSetup != nil { tt.generatorSetup(g) diff --git a/agent/xds/resources.go b/agent/xds/resources.go index 5c065862a..41b6adfe4 100644 --- a/agent/xds/resources.go +++ b/agent/xds/resources.go @@ -3,11 +3,11 @@ package xds import ( "fmt" + "github.com/hashicorp/consul/agent/xds/xdscommon" "github.com/hashicorp/go-hclog" "google.golang.org/protobuf/proto" "github.com/hashicorp/consul/agent/proxycfg" - "github.com/hashicorp/consul/agent/xds/xdscommon" ) // ResourceGenerator is associated with a single gRPC stream and creates xDS @@ -17,10 +17,10 @@ type ResourceGenerator struct { CfgFetcher ConfigFetcher IncrementalXDS bool - ProxyFeatures supportedProxyFeatures + ProxyFeatures xdscommon.SupportedProxyFeatures } -func newResourceGenerator( +func NewResourceGenerator( logger hclog.Logger, cfgFetcher ConfigFetcher, 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) for _, typeUrl := range []string{xdscommon.ListenerType, xdscommon.RouteType, xdscommon.ClusterType, xdscommon.EndpointType} { res, err := g.resourcesFromSnapshot(typeUrl, cfgSnap) diff --git a/agent/xds/resources_test.go b/agent/xds/resources_test.go index 9c707cd5b..b27d3a528 100644 --- a/agent/xds/resources_test.go +++ b/agent/xds/resources_test.go @@ -9,6 +9,8 @@ import ( 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/consul/agent/xds/testcommon" + "github.com/hashicorp/consul/agent/xds/xdscommon" testinf "github.com/mitchellh/go-testing-interface" "github.com/stretchr/testify/require" @@ -16,7 +18,6 @@ import ( "github.com/hashicorp/consul/agent/proxycfg" "github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/xds/proxysupport" - "github.com/hashicorp/consul/agent/xds/xdscommon" "github.com/hashicorp/consul/sdk/testutil" ) @@ -47,7 +48,7 @@ func TestAllResourcesFromSnapshot(t *testing.T) { run := func( t *testing.T, - sf supportedProxyFeatures, + sf xdscommon.SupportedProxyFeatures, envoyVersion string, latestEnvoyVersion string, tt testcase, @@ -61,20 +62,20 @@ func TestAllResourcesFromSnapshot(t *testing.T) { // 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 // golder files for every test case and so not be any use! - setupTLSRootsAndLeaf(t, snap) + testcommon.SetupTLSRootsAndLeaf(t, snap) if tt.setup != nil { tt.setup(snap) } // Need server just for logger dependency - g := newResourceGenerator(testutil.Logger(t), nil, false) + g := NewResourceGenerator(testutil.Logger(t), nil, false) g.ProxyFeatures = sf if tt.generatorSetup != nil { tt.generatorSetup(g) } - resources, err := g.allResourcesFromSnapshot(snap) + resources, err := g.AllResourcesFromSnapshot(snap) require.NoError(t, err) typeUrls := []string{ @@ -168,7 +169,7 @@ func TestAllResourcesFromSnapshot(t *testing.T) { latestEnvoyVersion := proxysupport.EnvoyVersions[0] for _, envoyVersion := range proxysupport.EnvoyVersions { - sf, err := determineSupportedProxyFeaturesFromString(envoyVersion) + sf, err := xdscommon.DetermineSupportedProxyFeaturesFromString(envoyVersion) require.NoError(t, err) t.Run("envoy-"+envoyVersion, func(t *testing.T) { for _, tt := range tests { diff --git a/agent/xds/routes_test.go b/agent/xds/routes_test.go index c9aaf612f..c5e7b9ce7 100644 --- a/agent/xds/routes_test.go +++ b/agent/xds/routes_test.go @@ -7,6 +7,7 @@ import ( "time" 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" "github.com/stretchr/testify/require" @@ -190,7 +191,7 @@ func TestRoutesFromSnapshot(t *testing.T) { latestEnvoyVersion := proxysupport.EnvoyVersions[0] for _, envoyVersion := range proxysupport.EnvoyVersions { - sf, err := determineSupportedProxyFeaturesFromString(envoyVersion) + sf, err := xdscommon.DetermineSupportedProxyFeaturesFromString(envoyVersion) require.NoError(t, err) t.Run("envoy-"+envoyVersion, func(t *testing.T) { 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 // files workable. Note we don't update these otherwise they'd change // 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 routes, err := g.routesFromSnapshot(snap) diff --git a/agent/xds/server.go b/agent/xds/server.go index 79fd47223..6fc0f13e5 100644 --- a/agent/xds/server.go +++ b/agent/xds/server.go @@ -7,6 +7,7 @@ import ( "time" 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/prometheus" @@ -20,7 +21,6 @@ import ( "github.com/hashicorp/consul/agent/grpc-external/limiter" "github.com/hashicorp/consul/agent/proxycfg" "github.com/hashicorp/consul/agent/structs" - "github.com/hashicorp/consul/agent/xds/xdscommon" ) var ( diff --git a/agent/xds/testcommon/testcommon.go b/agent/xds/testcommon/testcommon.go new file mode 100644 index 000000000..c310d0980 --- /dev/null +++ b/agent/xds/testcommon/testcommon.go @@ -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) +} diff --git a/agent/xds/xds_protocol_helpers_test.go b/agent/xds/xds_protocol_helpers_test.go index b1094ae8d..36829f3e7 100644 --- a/agent/xds/xds_protocol_helpers_test.go +++ b/agent/xds/xds_protocol_helpers_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/consul/agent/connect" "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_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) return &envoy_discovery_v3.Resource{ - Name: getResourceName(res), + Name: xdscommon.GetResourceName(res), Version: mustHashResource(t, res), Resource: any, } diff --git a/agent/xds/envoy_versioning.go b/agent/xds/xdscommon/envoy_versioning.go similarity index 84% rename from agent/xds/envoy_versioning.go rename to agent/xds/xdscommon/envoy_versioning.go index 25d198c51..d43e7267d 100644 --- a/agent/xds/envoy_versioning.go +++ b/agent/xds/xdscommon/envoy_versioning.go @@ -1,4 +1,4 @@ -package xds +package xdscommon import ( "fmt" @@ -23,35 +23,35 @@ type unsupportedVersion struct { 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 // . } -func determineSupportedProxyFeatures(node *envoy_core_v3.Node) (supportedProxyFeatures, error) { +func DetermineSupportedProxyFeatures(node *envoy_core_v3.Node) (SupportedProxyFeatures, error) { version := determineEnvoyVersionFromNode(node) return determineSupportedProxyFeaturesFromVersion(version) } -func determineSupportedProxyFeaturesFromString(vs string) (supportedProxyFeatures, error) { +func DetermineSupportedProxyFeaturesFromString(vs string) (SupportedProxyFeatures, error) { version := version.Must(version.NewVersion(vs)) return determineSupportedProxyFeaturesFromVersion(version) } -func determineSupportedProxyFeaturesFromVersion(version *version.Version) (supportedProxyFeatures, error) { +func determineSupportedProxyFeaturesFromVersion(version *version.Version) (SupportedProxyFeatures, error) { if version == nil { // This would happen on either extremely old builds OR perhaps on // custom builds. Should we error? - return supportedProxyFeatures{}, nil + return SupportedProxyFeatures{}, nil } 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 { 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. "+ "Please upgrade to version %s.", 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(...) diff --git a/agent/xds/envoy_versioning_test.go b/agent/xds/xdscommon/envoy_versioning_test.go similarity index 95% rename from agent/xds/envoy_versioning_test.go rename to agent/xds/xdscommon/envoy_versioning_test.go index 6d749b4fa..833e3014e 100644 --- a/agent/xds/envoy_versioning_test.go +++ b/agent/xds/xdscommon/envoy_versioning_test.go @@ -1,4 +1,4 @@ -package xds +package xdscommon import ( "testing" @@ -72,7 +72,7 @@ func TestDetermineSupportedProxyFeaturesFromString(t *testing.T) { ) type testcase struct { - expect supportedProxyFeatures + expect SupportedProxyFeatures expectErr string } @@ -129,7 +129,7 @@ func TestDetermineSupportedProxyFeaturesFromString(t *testing.T) { 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", } { - cases[v] = testcase{expect: supportedProxyFeatures{ + cases[v] = testcase{expect: SupportedProxyFeatures{ ForceLDSandCDSToAlwaysUseWildcardsOnReconnect: true, }} } @@ -140,13 +140,13 @@ func TestDetermineSupportedProxyFeaturesFromString(t *testing.T) { "1.23.0", "1.23.1", "1.23.2", "1.24.0", } { - cases[v] = testcase{expect: supportedProxyFeatures{}} + cases[v] = testcase{expect: SupportedProxyFeatures{}} } for name, tc := range cases { tc := tc t.Run(name, func(t *testing.T) { - sf, err := determineSupportedProxyFeaturesFromString(name) + sf, err := DetermineSupportedProxyFeaturesFromString(name) if tc.expectErr == "" { require.Equal(t, tc.expect, sf) } else { diff --git a/agent/xds/xdscommon/xdscommon.go b/agent/xds/xdscommon/xdscommon.go index 24d96e696..990cd0104 100644 --- a/agent/xds/xdscommon/xdscommon.go +++ b/agent/xds/xdscommon/xdscommon.go @@ -1,6 +1,11 @@ package xdscommon 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" ) @@ -53,6 +58,39 @@ type IndexedResources struct { 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 { return &IndexedResources{ Index: map[string]map[string]proto.Message{ diff --git a/troubleshoot/validateupstream/testdata/test-leaf-cert.golden b/troubleshoot/validateupstream/testdata/test-leaf-cert.golden new file mode 100644 index 000000000..d5ff38910 --- /dev/null +++ b/troubleshoot/validateupstream/testdata/test-leaf-cert.golden @@ -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----- diff --git a/troubleshoot/validateupstream/testdata/test-leaf-key.golden b/troubleshoot/validateupstream/testdata/test-leaf-key.golden new file mode 100644 index 000000000..5c8b61854 --- /dev/null +++ b/troubleshoot/validateupstream/testdata/test-leaf-key.golden @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49 +AwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav +q5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ== +-----END EC PRIVATE KEY----- diff --git a/troubleshoot/validateupstream/testdata/test-root-cert.golden b/troubleshoot/validateupstream/testdata/test-root-cert.golden new file mode 100644 index 000000000..f7392b085 --- /dev/null +++ b/troubleshoot/validateupstream/testdata/test-root-cert.golden @@ -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----- diff --git a/agent/xds/testdata/validateupstream/clusters.json b/troubleshoot/validateupstream/testdata/validateupstream/clusters.json similarity index 100% rename from agent/xds/testdata/validateupstream/clusters.json rename to troubleshoot/validateupstream/testdata/validateupstream/clusters.json diff --git a/agent/xds/testdata/validateupstream/config.json b/troubleshoot/validateupstream/testdata/validateupstream/config.json similarity index 100% rename from agent/xds/testdata/validateupstream/config.json rename to troubleshoot/validateupstream/testdata/validateupstream/config.json diff --git a/agent/xds/validateupstream.go b/troubleshoot/validateupstream/validateupstream.go similarity index 99% rename from agent/xds/validateupstream.go rename to troubleshoot/validateupstream/validateupstream.go index 994d01af7..a7ad976fc 100644 --- a/agent/xds/validateupstream.go +++ b/troubleshoot/validateupstream/validateupstream.go @@ -1,4 +1,4 @@ -package xds +package validateupstream import ( "github.com/hashicorp/consul/acl" diff --git a/agent/xds/validateupstream_test.go b/troubleshoot/validateupstream/validateupstream_test.go similarity index 95% rename from agent/xds/validateupstream_test.go rename to troubleshoot/validateupstream/validateupstream_test.go index e43c6f84f..25cae6e48 100644 --- a/agent/xds/validateupstream_test.go +++ b/troubleshoot/validateupstream/validateupstream_test.go @@ -1,4 +1,4 @@ -package xds +package validateupstream import ( "io" @@ -7,6 +7,8 @@ import ( envoy_admin_v3 "github.com/envoyproxy/go-control-plane/envoy/admin/v3" "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" "github.com/stretchr/testify/require" @@ -205,7 +207,7 @@ func TestValidateUpstreams(t *testing.T) { } latestEnvoyVersion := proxysupport.EnvoyVersions[0] - sf, err := determineSupportedProxyFeaturesFromString(latestEnvoyVersion) + sf, err := xdscommon.DetermineSupportedProxyFeaturesFromString(latestEnvoyVersion) require.NoError(t, err) for _, tt := range tests { 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 // files workable. Note we don't update these otherwise they'd change // 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 - res, err := g.allResourcesFromSnapshot(snap) + res, err := g.AllResourcesFromSnapshot(snap) require.NoError(t, err) - indexedResources := indexResources(g.Logger, res) + indexedResources := xdscommon.IndexResources(g.Logger, res) if tt.patcher != nil { 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 TestValidate(t *testing.T) { +func TestValidate2(t *testing.T) { indexedResources := getConfig(t) clusters := getClusters(t) err := Validate(indexedResources, service, "", "", true, clusters)