grpc-go/xds/internal/resolver/watch_service_test.go

169 lines
7.8 KiB
Go

/*
*
* Copyright 2020 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package resolver_test
import (
"context"
"testing"
"github.com/envoyproxy/go-control-plane/pkg/wellknown"
"github.com/google/uuid"
"google.golang.org/grpc/internal/testutils"
"google.golang.org/grpc/internal/testutils/xds/e2e"
"google.golang.org/grpc/resolver"
"google.golang.org/protobuf/types/known/wrapperspb"
v3listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
v3routepb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
v3routerpb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v3"
v3httppb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
)
// Tests the case where the listener resource starts pointing to a new route
// configuration resource after the xDS resolver has successfully resolved the
// service name and pushed an update on the channel. The test verifies that the
// resolver stops requesting the old route configuration resource and requests
// the new resource, and once successfully resolved, verifies that the update
// from the resolver matches expected service config.
func (s) TestServiceWatch_ListenerPointsToNewRouteConfiguration(t *testing.T) {
// Spin up an xDS management server for the test.
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
nodeID := uuid.New().String()
mgmtServer, lisCh, routeCfgCh, bc := setupManagementServerForTest(ctx, t, nodeID)
// Configure resources on the management server.
listeners := []*v3listenerpb.Listener{e2e.DefaultClientListener(defaultTestServiceName, defaultTestRouteConfigName)}
routes := []*v3routepb.RouteConfiguration{e2e.DefaultRouteConfig(defaultTestRouteConfigName, defaultTestServiceName, defaultTestClusterName)}
configureResourcesOnManagementServer(ctx, t, mgmtServer, nodeID, listeners, routes)
stateCh, _, _ := buildResolverForTarget(t, resolver.Target{URL: *testutils.MustParseURL("xds:///" + defaultTestServiceName)}, bc)
// Verify initial update from the resolver.
waitForResourceNames(ctx, t, lisCh, []string{defaultTestServiceName})
waitForResourceNames(ctx, t, routeCfgCh, []string{defaultTestRouteConfigName})
verifyUpdateFromResolver(ctx, t, stateCh, wantDefaultServiceConfig)
// Update the listener resource to point to a new route configuration name.
// Leave the old route configuration resource unchanged.
newTestRouteConfigName := defaultTestRouteConfigName + "-new"
listeners = []*v3listenerpb.Listener{e2e.DefaultClientListener(defaultTestServiceName, newTestRouteConfigName)}
configureResourcesOnManagementServer(ctx, t, mgmtServer, nodeID, listeners, routes)
// Verify that the new route configuration resource is requested.
waitForResourceNames(ctx, t, routeCfgCh, []string{newTestRouteConfigName})
// Update the old route configuration resource by adding a new route.
routes[0].VirtualHosts[0].Routes = append(routes[0].VirtualHosts[0].Routes, &v3routepb.Route{
Match: &v3routepb.RouteMatch{
PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/foo/bar"},
CaseSensitive: &wrapperspb.BoolValue{Value: false},
},
Action: &v3routepb.Route_Route{
Route: &v3routepb.RouteAction{
ClusterSpecifier: &v3routepb.RouteAction_Cluster{Cluster: "some-random-cluster"},
},
},
})
configureResourcesOnManagementServer(ctx, t, mgmtServer, nodeID, listeners, routes)
// Wait for no update from the resolver.
verifyNoUpdateFromResolver(ctx, t, stateCh)
// Update the management server with the new route configuration resource.
routes = append(routes, e2e.DefaultRouteConfig(newTestRouteConfigName, defaultTestServiceName, defaultTestClusterName))
configureResourcesOnManagementServer(ctx, t, mgmtServer, nodeID, listeners, routes)
// Ensure update from the resolver.
verifyUpdateFromResolver(ctx, t, stateCh, wantDefaultServiceConfig)
}
// Tests the case where the listener resource changes to contain an inline route
// configuration and changes back to having a route configuration resource name.
// Verifies that the expected xDS resource names are requested by the resolver
// and that the update from the resolver matches expected service config.
func (s) TestServiceWatch_ListenerPointsToInlineRouteConfiguration(t *testing.T) {
// Spin up an xDS management server for the test.
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
nodeID := uuid.New().String()
mgmtServer, lisCh, routeCfgCh, bc := setupManagementServerForTest(ctx, t, nodeID)
// Configure resources on the management server.
listeners := []*v3listenerpb.Listener{e2e.DefaultClientListener(defaultTestServiceName, defaultTestRouteConfigName)}
routes := []*v3routepb.RouteConfiguration{e2e.DefaultRouteConfig(defaultTestRouteConfigName, defaultTestServiceName, defaultTestClusterName)}
configureResourcesOnManagementServer(ctx, t, mgmtServer, nodeID, listeners, routes)
stateCh, _, _ := buildResolverForTarget(t, resolver.Target{URL: *testutils.MustParseURL("xds:///" + defaultTestServiceName)}, bc)
// Verify initial update from the resolver.
waitForResourceNames(ctx, t, lisCh, []string{defaultTestServiceName})
waitForResourceNames(ctx, t, routeCfgCh, []string{defaultTestRouteConfigName})
verifyUpdateFromResolver(ctx, t, stateCh, wantDefaultServiceConfig)
// Update listener to contain an inline route configuration.
hcm := testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
RouteConfig: &v3routepb.RouteConfiguration{
Name: defaultTestRouteConfigName,
VirtualHosts: []*v3routepb.VirtualHost{{
Domains: []string{defaultTestServiceName},
Routes: []*v3routepb.Route{{
Match: &v3routepb.RouteMatch{
PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/"},
},
Action: &v3routepb.Route_Route{
Route: &v3routepb.RouteAction{
ClusterSpecifier: &v3routepb.RouteAction_Cluster{Cluster: defaultTestClusterName},
},
},
}},
}},
},
},
HttpFilters: []*v3httppb.HttpFilter{e2e.HTTPFilter("router", &v3routerpb.Router{})},
})
listeners = []*v3listenerpb.Listener{{
Name: defaultTestServiceName,
ApiListener: &v3listenerpb.ApiListener{ApiListener: hcm},
FilterChains: []*v3listenerpb.FilterChain{{
Name: "filter-chain-name",
Filters: []*v3listenerpb.Filter{{
Name: wellknown.HTTPConnectionManager,
ConfigType: &v3listenerpb.Filter_TypedConfig{TypedConfig: hcm},
}},
}},
}}
configureResourcesOnManagementServer(ctx, t, mgmtServer, nodeID, listeners, nil)
// Verify that the old route configuration is not requested anymore.
waitForResourceNames(ctx, t, routeCfgCh, []string{})
verifyUpdateFromResolver(ctx, t, stateCh, wantDefaultServiceConfig)
// Update listener back to contain a route configuration name.
listeners = []*v3listenerpb.Listener{e2e.DefaultClientListener(defaultTestServiceName, defaultTestRouteConfigName)}
configureResourcesOnManagementServer(ctx, t, mgmtServer, nodeID, listeners, routes)
// Verify that that route configuration resource is requested.
waitForResourceNames(ctx, t, routeCfgCh, []string{defaultTestRouteConfigName})
// Verify that appropriate SC is pushed on the channel.
verifyUpdateFromResolver(ctx, t, stateCh, wantDefaultServiceConfig)
}