Some checks failed
codecov / upload (push) Failing after 13m59s
CodeQL / Analyze (push) Failing after 1s
govulncheck / govulncheck (.) (push) Failing after 1s
govulncheck / govulncheck (./cmd/protoc-gen-go-grpc) (push) Failing after 1s
govulncheck / govulncheck (./gcp/observability) (push) Failing after 1s
govulncheck / govulncheck (./security/advancedtls) (push) Failing after 1s
govulncheck / govulncheck (./stats/opencensus) (push) Failing after 1s
Testing / vet-proto (push) Failing after 1s
Testing / tests (arm, latest) (push) Waiting to run
Testing / extras (latest) (push) Failing after 1s
Testing / static checks (latest-1) (push) Failing after 1s
Testing / tests (-race, latest) (push) Failing after 1s
Testing / tests (i386, latest) (push) Failing after 1s
Testing / tests (latest) (push) Failing after 1s
Testing / tests (latest-1) (push) Failing after 1s
Lock Threads / lock (push) Failing after 1s
Stale bot / stale (push) Failing after 1s
180 lines
5.9 KiB
Go
180 lines
5.9 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 e2e
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/cespare/xxhash/v2"
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/google/go-cmp/cmp/cmpopts"
|
|
channelzpb "google.golang.org/grpc/channelz/grpc_channelz_v1"
|
|
"google.golang.org/grpc/internal/xds/clients"
|
|
"google.golang.org/grpc/internal/xds/xdsclient/xdsresource"
|
|
"google.golang.org/grpc/resolver"
|
|
)
|
|
|
|
func verifySubConnStates(t *testing.T, scs []*channelzpb.Subchannel, want map[channelzpb.ChannelConnectivityState_State]int) {
|
|
t.Helper()
|
|
var scStatsCount = map[channelzpb.ChannelConnectivityState_State]int{}
|
|
for _, sc := range scs {
|
|
scStatsCount[sc.Data.State.State]++
|
|
}
|
|
if diff := cmp.Diff(scStatsCount, want); diff != "" {
|
|
t.Fatalf("got unexpected number of subchannels in state Ready, %v, scs: %v", diff, scs)
|
|
}
|
|
}
|
|
|
|
// This function determines the stable, canonical order for any two
|
|
// resolver.Endpoint structs.
|
|
func lessEndpoint(a, b resolver.Endpoint) bool {
|
|
return getHash(a) < getHash(b)
|
|
}
|
|
|
|
func getHash(e resolver.Endpoint) uint64 {
|
|
h := xxhash.New()
|
|
|
|
// We iterate through all addresses to ensure the hash represents
|
|
// the full endpoint identity.
|
|
for _, addr := range e.Addresses {
|
|
h.Write([]byte(addr.Addr))
|
|
h.Write([]byte(addr.ServerName))
|
|
}
|
|
|
|
return h.Sum64()
|
|
}
|
|
|
|
// VerifyXDSConfig waits for an update on the xdsCh channel or an error on the
|
|
// errCh channel, and verifies that the received XDSConfig matches the expected
|
|
// XDS configuration.
|
|
func VerifyXDSConfig(ctx context.Context, xdsCh chan *xdsresource.XDSConfig, errCh chan error, want *xdsresource.XDSConfig) error {
|
|
select {
|
|
case <-ctx.Done():
|
|
return fmt.Errorf("timeout waiting for update from dependency manager")
|
|
case update := <-xdsCh:
|
|
if err := CompareXDSConfig(update, want); err != nil {
|
|
return err
|
|
}
|
|
case err := <-errCh:
|
|
return fmt.Errorf("received unexpected error from dependency manager: %v", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CompareXDSConfig compares two XDSConfig structs and returns an error if they
|
|
// do not match.
|
|
func CompareXDSConfig(gotXDSConfig, wantXDSConfig *xdsresource.XDSConfig) error {
|
|
cmpOpts := []cmp.Option{
|
|
cmpopts.EquateEmpty(),
|
|
cmpopts.IgnoreFields(xdsresource.HTTPFilter{}, "Filter", "Config"),
|
|
cmpopts.IgnoreFields(xdsresource.ListenerUpdate{}, "Raw"),
|
|
cmpopts.IgnoreFields(xdsresource.RouteConfigUpdate{}, "Raw"),
|
|
cmpopts.IgnoreFields(xdsresource.ClusterUpdate{}, "Raw", "LBPolicy", "TelemetryLabels"),
|
|
cmpopts.IgnoreFields(xdsresource.EndpointsUpdate{}, "Raw"),
|
|
// Used for EndpointConfig.ResolutionNote and ClusterResult.Err fields.
|
|
cmp.Transformer("ErrorsToString", func(in error) string {
|
|
if in == nil {
|
|
return "" // Treat nil as an empty string
|
|
}
|
|
s := in.Error()
|
|
|
|
// Replace all sequences of whitespace (including newlines and
|
|
// tabs) with a single standard space.
|
|
s = regexp.MustCompile(`\s+`).ReplaceAllString(s, " ")
|
|
|
|
// Trim any leading/trailing space that might be left over and
|
|
// return error as string.
|
|
return strings.TrimSpace(s)
|
|
}),
|
|
cmpopts.SortSlices(lessEndpoint),
|
|
}
|
|
if diff := cmp.Diff(gotXDSConfig, wantXDSConfig, cmpOpts...); diff != "" {
|
|
return fmt.Errorf("received unexpected update from dependency manager. Diff (-got +want):\n%v", diff)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// MakeXDSConfig creates a mock XDSConfig with a standard structure containing
|
|
// a Listener, RouteConfig, VirtualHost, and a single EDS Cluster, populated
|
|
// with the provided parameters.
|
|
func MakeXDSConfig(servicename, routeConfigName, clusterName, edsServiceName, addr string) *xdsresource.XDSConfig {
|
|
prefix := "/"
|
|
return &xdsresource.XDSConfig{
|
|
Listener: &xdsresource.ListenerUpdate{
|
|
APIListener: &xdsresource.HTTPConnectionManagerConfig{
|
|
RouteConfigName: routeConfigName,
|
|
HTTPFilters: []xdsresource.HTTPFilter{{Name: "router"}},
|
|
},
|
|
},
|
|
RouteConfig: &xdsresource.RouteConfigUpdate{
|
|
VirtualHosts: []*xdsresource.VirtualHost{
|
|
{
|
|
Domains: []string{servicename},
|
|
Routes: []*xdsresource.Route{{
|
|
Prefix: &prefix,
|
|
WeightedClusters: []xdsresource.WeightedCluster{{Name: clusterName, Weight: 100}},
|
|
ActionType: xdsresource.RouteActionRoute,
|
|
}},
|
|
},
|
|
},
|
|
},
|
|
VirtualHost: &xdsresource.VirtualHost{
|
|
Domains: []string{servicename},
|
|
Routes: []*xdsresource.Route{{
|
|
Prefix: &prefix,
|
|
WeightedClusters: []xdsresource.WeightedCluster{{Name: clusterName, Weight: 100}},
|
|
ActionType: xdsresource.RouteActionRoute},
|
|
},
|
|
},
|
|
Clusters: map[string]*xdsresource.ClusterResult{
|
|
clusterName: {
|
|
Config: xdsresource.ClusterConfig{Cluster: &xdsresource.ClusterUpdate{
|
|
ClusterType: xdsresource.ClusterTypeEDS,
|
|
ClusterName: clusterName,
|
|
EDSServiceName: edsServiceName,
|
|
},
|
|
EndpointConfig: &xdsresource.EndpointConfig{
|
|
EDSUpdate: &xdsresource.EndpointsUpdate{
|
|
Localities: []xdsresource.Locality{
|
|
{ID: clients.Locality{
|
|
Region: "region-1",
|
|
Zone: "zone-1",
|
|
SubZone: "subzone-1",
|
|
},
|
|
Endpoints: []xdsresource.Endpoint{
|
|
{
|
|
ResolverEndpoint: resolver.Endpoint{Addresses: []resolver.Address{{Addr: addr}}},
|
|
HealthStatus: xdsresource.EndpointHealthStatusUnknown,
|
|
Weight: 1,
|
|
},
|
|
},
|
|
Weight: 1,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|