mirror of https://github.com/grpc/grpc-go.git
497 lines
16 KiB
Go
497 lines
16 KiB
Go
/*
|
|
*
|
|
* Copyright 2021 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 xdsclient_test
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
v3clusterpb "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
|
|
v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
|
|
v3listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
|
|
v3routepb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
|
|
v3httppb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/google/go-cmp/cmp/cmpopts"
|
|
"google.golang.org/protobuf/testing/protocmp"
|
|
"google.golang.org/protobuf/types/known/anypb"
|
|
"google.golang.org/protobuf/types/known/durationpb"
|
|
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/credentials/insecure"
|
|
"google.golang.org/grpc/internal/testutils"
|
|
xdstestutils "google.golang.org/grpc/xds/internal/testutils"
|
|
"google.golang.org/grpc/xds/internal/xdsclient"
|
|
"google.golang.org/grpc/xds/internal/xdsclient/bootstrap"
|
|
)
|
|
|
|
const defaultTestWatchExpiryTimeout = 500 * time.Millisecond
|
|
|
|
func (s) TestLDSConfigDump(t *testing.T) {
|
|
const testVersion = "test-version-lds"
|
|
var (
|
|
ldsTargets = []string{"lds.target.good:0000", "lds.target.good:1111"}
|
|
routeConfigNames = []string{"route-config-0", "route-config-1"}
|
|
listenerRaws = make(map[string]*anypb.Any, len(ldsTargets))
|
|
)
|
|
|
|
for i := range ldsTargets {
|
|
listenersT := &v3listenerpb.Listener{
|
|
Name: ldsTargets[i],
|
|
ApiListener: &v3listenerpb.ApiListener{
|
|
ApiListener: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
|
|
RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{
|
|
Rds: &v3httppb.Rds{
|
|
ConfigSource: &v3corepb.ConfigSource{
|
|
ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{Ads: &v3corepb.AggregatedConfigSource{}},
|
|
},
|
|
RouteConfigName: routeConfigNames[i],
|
|
},
|
|
},
|
|
CommonHttpProtocolOptions: &v3corepb.HttpProtocolOptions{
|
|
MaxStreamDuration: durationpb.New(time.Second),
|
|
},
|
|
}),
|
|
},
|
|
}
|
|
listenerRaws[ldsTargets[i]] = testutils.MarshalAny(listenersT)
|
|
}
|
|
|
|
client, err := xdsclient.NewWithConfigForTesting(&bootstrap.Config{
|
|
BalancerName: testXDSServer,
|
|
Creds: grpc.WithTransportCredentials(insecure.NewCredentials()),
|
|
NodeProto: xdstestutils.EmptyNodeProtoV2,
|
|
}, defaultTestWatchExpiryTimeout)
|
|
if err != nil {
|
|
t.Fatalf("failed to create client: %v", err)
|
|
}
|
|
defer client.Close()
|
|
updateHandler := client.(xdsclient.UpdateHandler)
|
|
|
|
// Expected unknown.
|
|
if err := compareDump(client.DumpLDS, "", map[string]xdsclient.UpdateWithMD{}); err != nil {
|
|
t.Fatalf(err.Error())
|
|
}
|
|
|
|
wantRequested := make(map[string]xdsclient.UpdateWithMD)
|
|
for _, n := range ldsTargets {
|
|
cancel := client.WatchListener(n, func(update xdsclient.ListenerUpdate, err error) {})
|
|
defer cancel()
|
|
wantRequested[n] = xdsclient.UpdateWithMD{MD: xdsclient.UpdateMetadata{Status: xdsclient.ServiceStatusRequested}}
|
|
}
|
|
// Expected requested.
|
|
if err := compareDump(client.DumpLDS, "", wantRequested); err != nil {
|
|
t.Fatalf(err.Error())
|
|
}
|
|
|
|
update0 := make(map[string]xdsclient.ListenerUpdate)
|
|
want0 := make(map[string]xdsclient.UpdateWithMD)
|
|
for n, r := range listenerRaws {
|
|
update0[n] = xdsclient.ListenerUpdate{Raw: r}
|
|
want0[n] = xdsclient.UpdateWithMD{
|
|
MD: xdsclient.UpdateMetadata{Version: testVersion},
|
|
Raw: r,
|
|
}
|
|
}
|
|
updateHandler.NewListeners(update0, xdsclient.UpdateMetadata{Version: testVersion})
|
|
|
|
// Expect ACK.
|
|
if err := compareDump(client.DumpLDS, testVersion, want0); err != nil {
|
|
t.Fatalf(err.Error())
|
|
}
|
|
|
|
const nackVersion = "lds-version-nack"
|
|
var nackErr = fmt.Errorf("lds nack error")
|
|
updateHandler.NewListeners(
|
|
map[string]xdsclient.ListenerUpdate{
|
|
ldsTargets[0]: {},
|
|
},
|
|
xdsclient.UpdateMetadata{
|
|
ErrState: &xdsclient.UpdateErrorMetadata{
|
|
Version: nackVersion,
|
|
Err: nackErr,
|
|
},
|
|
},
|
|
)
|
|
|
|
// Expect NACK for [0], but old ACK for [1].
|
|
wantDump := make(map[string]xdsclient.UpdateWithMD)
|
|
// Though resource 0 was NACKed, the dump should show the previous ACKed raw
|
|
// message, as well as the NACK error.
|
|
wantDump[ldsTargets[0]] = xdsclient.UpdateWithMD{
|
|
MD: xdsclient.UpdateMetadata{
|
|
Version: testVersion,
|
|
ErrState: &xdsclient.UpdateErrorMetadata{
|
|
Version: nackVersion,
|
|
Err: nackErr,
|
|
},
|
|
},
|
|
Raw: listenerRaws[ldsTargets[0]],
|
|
}
|
|
|
|
wantDump[ldsTargets[1]] = xdsclient.UpdateWithMD{
|
|
MD: xdsclient.UpdateMetadata{Version: testVersion},
|
|
Raw: listenerRaws[ldsTargets[1]],
|
|
}
|
|
if err := compareDump(client.DumpLDS, nackVersion, wantDump); err != nil {
|
|
t.Fatalf(err.Error())
|
|
}
|
|
}
|
|
|
|
func (s) TestRDSConfigDump(t *testing.T) {
|
|
const testVersion = "test-version-rds"
|
|
var (
|
|
listenerNames = []string{"lds.target.good:0000", "lds.target.good:1111"}
|
|
rdsTargets = []string{"route-config-0", "route-config-1"}
|
|
clusterNames = []string{"cluster-0", "cluster-1"}
|
|
routeRaws = make(map[string]*anypb.Any, len(rdsTargets))
|
|
)
|
|
|
|
for i := range rdsTargets {
|
|
routeConfigT := &v3routepb.RouteConfiguration{
|
|
Name: rdsTargets[i],
|
|
VirtualHosts: []*v3routepb.VirtualHost{
|
|
{
|
|
Domains: []string{listenerNames[i]},
|
|
Routes: []*v3routepb.Route{{
|
|
Match: &v3routepb.RouteMatch{PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: ""}},
|
|
Action: &v3routepb.Route_Route{
|
|
Route: &v3routepb.RouteAction{
|
|
ClusterSpecifier: &v3routepb.RouteAction_Cluster{Cluster: clusterNames[i]},
|
|
},
|
|
},
|
|
}},
|
|
},
|
|
},
|
|
}
|
|
|
|
routeRaws[rdsTargets[i]] = testutils.MarshalAny(routeConfigT)
|
|
}
|
|
|
|
client, err := xdsclient.NewWithConfigForTesting(&bootstrap.Config{
|
|
BalancerName: testXDSServer,
|
|
Creds: grpc.WithTransportCredentials(insecure.NewCredentials()),
|
|
NodeProto: xdstestutils.EmptyNodeProtoV2,
|
|
}, defaultTestWatchExpiryTimeout)
|
|
if err != nil {
|
|
t.Fatalf("failed to create client: %v", err)
|
|
}
|
|
defer client.Close()
|
|
updateHandler := client.(xdsclient.UpdateHandler)
|
|
|
|
// Expected unknown.
|
|
if err := compareDump(client.DumpRDS, "", map[string]xdsclient.UpdateWithMD{}); err != nil {
|
|
t.Fatalf(err.Error())
|
|
}
|
|
|
|
wantRequested := make(map[string]xdsclient.UpdateWithMD)
|
|
for _, n := range rdsTargets {
|
|
cancel := client.WatchRouteConfig(n, func(update xdsclient.RouteConfigUpdate, err error) {})
|
|
defer cancel()
|
|
wantRequested[n] = xdsclient.UpdateWithMD{MD: xdsclient.UpdateMetadata{Status: xdsclient.ServiceStatusRequested}}
|
|
}
|
|
// Expected requested.
|
|
if err := compareDump(client.DumpRDS, "", wantRequested); err != nil {
|
|
t.Fatalf(err.Error())
|
|
}
|
|
|
|
update0 := make(map[string]xdsclient.RouteConfigUpdate)
|
|
want0 := make(map[string]xdsclient.UpdateWithMD)
|
|
for n, r := range routeRaws {
|
|
update0[n] = xdsclient.RouteConfigUpdate{Raw: r}
|
|
want0[n] = xdsclient.UpdateWithMD{
|
|
MD: xdsclient.UpdateMetadata{Version: testVersion},
|
|
Raw: r,
|
|
}
|
|
}
|
|
updateHandler.NewRouteConfigs(update0, xdsclient.UpdateMetadata{Version: testVersion})
|
|
|
|
// Expect ACK.
|
|
if err := compareDump(client.DumpRDS, testVersion, want0); err != nil {
|
|
t.Fatalf(err.Error())
|
|
}
|
|
|
|
const nackVersion = "rds-version-nack"
|
|
var nackErr = fmt.Errorf("rds nack error")
|
|
updateHandler.NewRouteConfigs(
|
|
map[string]xdsclient.RouteConfigUpdate{
|
|
rdsTargets[0]: {},
|
|
},
|
|
xdsclient.UpdateMetadata{
|
|
ErrState: &xdsclient.UpdateErrorMetadata{
|
|
Version: nackVersion,
|
|
Err: nackErr,
|
|
},
|
|
},
|
|
)
|
|
|
|
// Expect NACK for [0], but old ACK for [1].
|
|
wantDump := make(map[string]xdsclient.UpdateWithMD)
|
|
// Though resource 0 was NACKed, the dump should show the previous ACKed raw
|
|
// message, as well as the NACK error.
|
|
wantDump[rdsTargets[0]] = xdsclient.UpdateWithMD{
|
|
MD: xdsclient.UpdateMetadata{
|
|
Version: testVersion,
|
|
ErrState: &xdsclient.UpdateErrorMetadata{
|
|
Version: nackVersion,
|
|
Err: nackErr,
|
|
},
|
|
},
|
|
Raw: routeRaws[rdsTargets[0]],
|
|
}
|
|
wantDump[rdsTargets[1]] = xdsclient.UpdateWithMD{
|
|
MD: xdsclient.UpdateMetadata{Version: testVersion},
|
|
Raw: routeRaws[rdsTargets[1]],
|
|
}
|
|
if err := compareDump(client.DumpRDS, nackVersion, wantDump); err != nil {
|
|
t.Fatalf(err.Error())
|
|
}
|
|
}
|
|
|
|
func (s) TestCDSConfigDump(t *testing.T) {
|
|
const testVersion = "test-version-cds"
|
|
var (
|
|
cdsTargets = []string{"cluster-0", "cluster-1"}
|
|
serviceNames = []string{"service-0", "service-1"}
|
|
clusterRaws = make(map[string]*anypb.Any, len(cdsTargets))
|
|
)
|
|
|
|
for i := range cdsTargets {
|
|
clusterT := &v3clusterpb.Cluster{
|
|
Name: cdsTargets[i],
|
|
ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS},
|
|
EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{
|
|
EdsConfig: &v3corepb.ConfigSource{
|
|
ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{
|
|
Ads: &v3corepb.AggregatedConfigSource{},
|
|
},
|
|
},
|
|
ServiceName: serviceNames[i],
|
|
},
|
|
LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN,
|
|
LrsServer: &v3corepb.ConfigSource{
|
|
ConfigSourceSpecifier: &v3corepb.ConfigSource_Self{
|
|
Self: &v3corepb.SelfConfigSource{},
|
|
},
|
|
},
|
|
}
|
|
|
|
clusterRaws[cdsTargets[i]] = testutils.MarshalAny(clusterT)
|
|
}
|
|
|
|
client, err := xdsclient.NewWithConfigForTesting(&bootstrap.Config{
|
|
BalancerName: testXDSServer,
|
|
Creds: grpc.WithTransportCredentials(insecure.NewCredentials()),
|
|
NodeProto: xdstestutils.EmptyNodeProtoV2,
|
|
}, defaultTestWatchExpiryTimeout)
|
|
if err != nil {
|
|
t.Fatalf("failed to create client: %v", err)
|
|
}
|
|
defer client.Close()
|
|
updateHandler := client.(xdsclient.UpdateHandler)
|
|
|
|
// Expected unknown.
|
|
if err := compareDump(client.DumpCDS, "", map[string]xdsclient.UpdateWithMD{}); err != nil {
|
|
t.Fatalf(err.Error())
|
|
}
|
|
|
|
wantRequested := make(map[string]xdsclient.UpdateWithMD)
|
|
for _, n := range cdsTargets {
|
|
cancel := client.WatchCluster(n, func(update xdsclient.ClusterUpdate, err error) {})
|
|
defer cancel()
|
|
wantRequested[n] = xdsclient.UpdateWithMD{MD: xdsclient.UpdateMetadata{Status: xdsclient.ServiceStatusRequested}}
|
|
}
|
|
// Expected requested.
|
|
if err := compareDump(client.DumpCDS, "", wantRequested); err != nil {
|
|
t.Fatalf(err.Error())
|
|
}
|
|
|
|
update0 := make(map[string]xdsclient.ClusterUpdate)
|
|
want0 := make(map[string]xdsclient.UpdateWithMD)
|
|
for n, r := range clusterRaws {
|
|
update0[n] = xdsclient.ClusterUpdate{Raw: r}
|
|
want0[n] = xdsclient.UpdateWithMD{
|
|
MD: xdsclient.UpdateMetadata{Version: testVersion},
|
|
Raw: r,
|
|
}
|
|
}
|
|
updateHandler.NewClusters(update0, xdsclient.UpdateMetadata{Version: testVersion})
|
|
|
|
// Expect ACK.
|
|
if err := compareDump(client.DumpCDS, testVersion, want0); err != nil {
|
|
t.Fatalf(err.Error())
|
|
}
|
|
|
|
const nackVersion = "cds-version-nack"
|
|
var nackErr = fmt.Errorf("cds nack error")
|
|
updateHandler.NewClusters(
|
|
map[string]xdsclient.ClusterUpdate{
|
|
cdsTargets[0]: {},
|
|
},
|
|
xdsclient.UpdateMetadata{
|
|
ErrState: &xdsclient.UpdateErrorMetadata{
|
|
Version: nackVersion,
|
|
Err: nackErr,
|
|
},
|
|
},
|
|
)
|
|
|
|
// Expect NACK for [0], but old ACK for [1].
|
|
wantDump := make(map[string]xdsclient.UpdateWithMD)
|
|
// Though resource 0 was NACKed, the dump should show the previous ACKed raw
|
|
// message, as well as the NACK error.
|
|
wantDump[cdsTargets[0]] = xdsclient.UpdateWithMD{
|
|
MD: xdsclient.UpdateMetadata{
|
|
Version: testVersion,
|
|
ErrState: &xdsclient.UpdateErrorMetadata{
|
|
Version: nackVersion,
|
|
Err: nackErr,
|
|
},
|
|
},
|
|
Raw: clusterRaws[cdsTargets[0]],
|
|
}
|
|
wantDump[cdsTargets[1]] = xdsclient.UpdateWithMD{
|
|
MD: xdsclient.UpdateMetadata{Version: testVersion},
|
|
Raw: clusterRaws[cdsTargets[1]],
|
|
}
|
|
if err := compareDump(client.DumpCDS, nackVersion, wantDump); err != nil {
|
|
t.Fatalf(err.Error())
|
|
}
|
|
}
|
|
|
|
func (s) TestEDSConfigDump(t *testing.T) {
|
|
const testVersion = "test-version-cds"
|
|
var (
|
|
edsTargets = []string{"cluster-0", "cluster-1"}
|
|
localityNames = []string{"locality-0", "locality-1"}
|
|
addrs = []string{"addr0:123", "addr1:456"}
|
|
endpointRaws = make(map[string]*anypb.Any, len(edsTargets))
|
|
)
|
|
|
|
for i := range edsTargets {
|
|
clab0 := xdstestutils.NewClusterLoadAssignmentBuilder(edsTargets[i], nil)
|
|
clab0.AddLocality(localityNames[i], 1, 1, []string{addrs[i]}, nil)
|
|
claT := clab0.Build()
|
|
|
|
endpointRaws[edsTargets[i]] = testutils.MarshalAny(claT)
|
|
}
|
|
|
|
client, err := xdsclient.NewWithConfigForTesting(&bootstrap.Config{
|
|
BalancerName: testXDSServer,
|
|
Creds: grpc.WithTransportCredentials(insecure.NewCredentials()),
|
|
NodeProto: xdstestutils.EmptyNodeProtoV2,
|
|
}, defaultTestWatchExpiryTimeout)
|
|
if err != nil {
|
|
t.Fatalf("failed to create client: %v", err)
|
|
}
|
|
defer client.Close()
|
|
updateHandler := client.(xdsclient.UpdateHandler)
|
|
|
|
// Expected unknown.
|
|
if err := compareDump(client.DumpEDS, "", map[string]xdsclient.UpdateWithMD{}); err != nil {
|
|
t.Fatalf(err.Error())
|
|
}
|
|
|
|
wantRequested := make(map[string]xdsclient.UpdateWithMD)
|
|
for _, n := range edsTargets {
|
|
cancel := client.WatchEndpoints(n, func(update xdsclient.EndpointsUpdate, err error) {})
|
|
defer cancel()
|
|
wantRequested[n] = xdsclient.UpdateWithMD{MD: xdsclient.UpdateMetadata{Status: xdsclient.ServiceStatusRequested}}
|
|
}
|
|
// Expected requested.
|
|
if err := compareDump(client.DumpEDS, "", wantRequested); err != nil {
|
|
t.Fatalf(err.Error())
|
|
}
|
|
|
|
update0 := make(map[string]xdsclient.EndpointsUpdate)
|
|
want0 := make(map[string]xdsclient.UpdateWithMD)
|
|
for n, r := range endpointRaws {
|
|
update0[n] = xdsclient.EndpointsUpdate{Raw: r}
|
|
want0[n] = xdsclient.UpdateWithMD{
|
|
MD: xdsclient.UpdateMetadata{Version: testVersion},
|
|
Raw: r,
|
|
}
|
|
}
|
|
updateHandler.NewEndpoints(update0, xdsclient.UpdateMetadata{Version: testVersion})
|
|
|
|
// Expect ACK.
|
|
if err := compareDump(client.DumpEDS, testVersion, want0); err != nil {
|
|
t.Fatalf(err.Error())
|
|
}
|
|
|
|
const nackVersion = "eds-version-nack"
|
|
var nackErr = fmt.Errorf("eds nack error")
|
|
updateHandler.NewEndpoints(
|
|
map[string]xdsclient.EndpointsUpdate{
|
|
edsTargets[0]: {},
|
|
},
|
|
xdsclient.UpdateMetadata{
|
|
ErrState: &xdsclient.UpdateErrorMetadata{
|
|
Version: nackVersion,
|
|
Err: nackErr,
|
|
},
|
|
},
|
|
)
|
|
|
|
// Expect NACK for [0], but old ACK for [1].
|
|
wantDump := make(map[string]xdsclient.UpdateWithMD)
|
|
// Though resource 0 was NACKed, the dump should show the previous ACKed raw
|
|
// message, as well as the NACK error.
|
|
wantDump[edsTargets[0]] = xdsclient.UpdateWithMD{
|
|
MD: xdsclient.UpdateMetadata{
|
|
Version: testVersion,
|
|
ErrState: &xdsclient.UpdateErrorMetadata{
|
|
Version: nackVersion,
|
|
Err: nackErr,
|
|
},
|
|
},
|
|
Raw: endpointRaws[edsTargets[0]],
|
|
}
|
|
wantDump[edsTargets[1]] = xdsclient.UpdateWithMD{
|
|
MD: xdsclient.UpdateMetadata{Version: testVersion},
|
|
Raw: endpointRaws[edsTargets[1]],
|
|
}
|
|
if err := compareDump(client.DumpEDS, nackVersion, wantDump); err != nil {
|
|
t.Fatalf(err.Error())
|
|
}
|
|
}
|
|
|
|
func compareDump(dumpFunc func() (string, map[string]xdsclient.UpdateWithMD), wantVersion string, wantDump interface{}) error {
|
|
v, dump := dumpFunc()
|
|
if v != wantVersion {
|
|
return fmt.Errorf("Dump() returned version %q, want %q", v, wantVersion)
|
|
}
|
|
cmpOpts := cmp.Options{
|
|
cmpopts.EquateEmpty(),
|
|
cmp.Comparer(func(a, b time.Time) bool { return true }),
|
|
cmp.Comparer(func(x, y error) bool {
|
|
if x == nil || y == nil {
|
|
return x == nil && y == nil
|
|
}
|
|
return x.Error() == y.Error()
|
|
}),
|
|
protocmp.Transform(),
|
|
}
|
|
if diff := cmp.Diff(dump, wantDump, cmpOpts); diff != "" {
|
|
return fmt.Errorf("Dump() returned unexpected dump, diff (-got +want): %s", diff)
|
|
}
|
|
return nil
|
|
}
|