mirror of https://github.com/grpc/grpc-go.git
1793 lines
59 KiB
Go
1793 lines
59 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 xdsclient
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
v1typepb "github.com/cncf/udpa/go/udpa/type/v1"
|
|
v3routepb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
|
|
"github.com/golang/protobuf/proto"
|
|
spb "github.com/golang/protobuf/ptypes/struct"
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/google/go-cmp/cmp/cmpopts"
|
|
"google.golang.org/protobuf/types/known/durationpb"
|
|
|
|
"google.golang.org/grpc/internal/testutils"
|
|
"google.golang.org/grpc/internal/xds/env"
|
|
"google.golang.org/grpc/xds/internal/httpfilter"
|
|
_ "google.golang.org/grpc/xds/internal/httpfilter/router"
|
|
"google.golang.org/grpc/xds/internal/testutils/e2e"
|
|
"google.golang.org/grpc/xds/internal/version"
|
|
|
|
v2xdspb "github.com/envoyproxy/go-control-plane/envoy/api/v2"
|
|
v2corepb "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
|
|
v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
|
|
v2httppb "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/http_connection_manager/v2"
|
|
v2listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v2"
|
|
v3listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
|
|
v3httppb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
|
|
v3tlspb "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
|
|
anypb "github.com/golang/protobuf/ptypes/any"
|
|
wrapperspb "github.com/golang/protobuf/ptypes/wrappers"
|
|
)
|
|
|
|
func (s) TestUnmarshalListener_ClientSide(t *testing.T) {
|
|
const (
|
|
v2LDSTarget = "lds.target.good:2222"
|
|
v3LDSTarget = "lds.target.good:3333"
|
|
v2RouteConfigName = "v2RouteConfig"
|
|
v3RouteConfigName = "v3RouteConfig"
|
|
routeName = "routeName"
|
|
testVersion = "test-version-lds-client"
|
|
)
|
|
|
|
var (
|
|
v2Lis = testutils.MarshalAny(&v2xdspb.Listener{
|
|
Name: v2LDSTarget,
|
|
ApiListener: &v2listenerpb.ApiListener{
|
|
ApiListener: testutils.MarshalAny(&v2httppb.HttpConnectionManager{
|
|
RouteSpecifier: &v2httppb.HttpConnectionManager_Rds{
|
|
Rds: &v2httppb.Rds{
|
|
ConfigSource: &v2corepb.ConfigSource{
|
|
ConfigSourceSpecifier: &v2corepb.ConfigSource_Ads{Ads: &v2corepb.AggregatedConfigSource{}},
|
|
},
|
|
RouteConfigName: v2RouteConfigName,
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
})
|
|
customFilter = &v3httppb.HttpFilter{
|
|
Name: "customFilter",
|
|
ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: customFilterConfig},
|
|
}
|
|
typedStructFilter = &v3httppb.HttpFilter{
|
|
Name: "customFilter",
|
|
ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: wrappedCustomFilterTypedStructConfig},
|
|
}
|
|
customOptionalFilter = &v3httppb.HttpFilter{
|
|
Name: "customFilter",
|
|
ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: customFilterConfig},
|
|
IsOptional: true,
|
|
}
|
|
customFilter2 = &v3httppb.HttpFilter{
|
|
Name: "customFilter2",
|
|
ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: customFilterConfig},
|
|
}
|
|
errFilter = &v3httppb.HttpFilter{
|
|
Name: "errFilter",
|
|
ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: errFilterConfig},
|
|
}
|
|
errOptionalFilter = &v3httppb.HttpFilter{
|
|
Name: "errFilter",
|
|
ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: errFilterConfig},
|
|
IsOptional: true,
|
|
}
|
|
clientOnlyCustomFilter = &v3httppb.HttpFilter{
|
|
Name: "clientOnlyCustomFilter",
|
|
ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: clientOnlyCustomFilterConfig},
|
|
}
|
|
serverOnlyCustomFilter = &v3httppb.HttpFilter{
|
|
Name: "serverOnlyCustomFilter",
|
|
ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: serverOnlyCustomFilterConfig},
|
|
}
|
|
serverOnlyOptionalCustomFilter = &v3httppb.HttpFilter{
|
|
Name: "serverOnlyOptionalCustomFilter",
|
|
ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: serverOnlyCustomFilterConfig},
|
|
IsOptional: true,
|
|
}
|
|
unknownFilter = &v3httppb.HttpFilter{
|
|
Name: "unknownFilter",
|
|
ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: unknownFilterConfig},
|
|
}
|
|
unknownOptionalFilter = &v3httppb.HttpFilter{
|
|
Name: "unknownFilter",
|
|
ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: unknownFilterConfig},
|
|
IsOptional: true,
|
|
}
|
|
v3LisWithInlineRoute = testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
ApiListener: &v3listenerpb.ApiListener{
|
|
ApiListener: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
|
|
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
|
|
RouteConfig: &v3routepb.RouteConfiguration{
|
|
Name: routeName,
|
|
VirtualHosts: []*v3routepb.VirtualHost{{
|
|
Domains: []string{v3LDSTarget},
|
|
Routes: []*v3routepb.Route{{
|
|
Match: &v3routepb.RouteMatch{
|
|
PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/"},
|
|
},
|
|
Action: &v3routepb.Route_Route{
|
|
Route: &v3routepb.RouteAction{
|
|
ClusterSpecifier: &v3routepb.RouteAction_Cluster{Cluster: clusterName},
|
|
}}}}}}},
|
|
},
|
|
HttpFilters: []*v3httppb.HttpFilter{emptyRouterFilter},
|
|
CommonHttpProtocolOptions: &v3corepb.HttpProtocolOptions{
|
|
MaxStreamDuration: durationpb.New(time.Second),
|
|
},
|
|
}),
|
|
},
|
|
})
|
|
v3LisWithFilters = func(fs ...*v3httppb.HttpFilter) *anypb.Any {
|
|
fs = append(fs, emptyRouterFilter)
|
|
return testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
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: v3RouteConfigName,
|
|
},
|
|
},
|
|
CommonHttpProtocolOptions: &v3corepb.HttpProtocolOptions{
|
|
MaxStreamDuration: durationpb.New(time.Second),
|
|
},
|
|
HttpFilters: fs,
|
|
}),
|
|
},
|
|
})
|
|
}
|
|
errMD = UpdateMetadata{
|
|
Status: ServiceStatusNACKed,
|
|
Version: testVersion,
|
|
ErrState: &UpdateErrorMetadata{
|
|
Version: testVersion,
|
|
Err: cmpopts.AnyError,
|
|
},
|
|
}
|
|
)
|
|
|
|
tests := []struct {
|
|
name string
|
|
resources []*anypb.Any
|
|
wantUpdate map[string]ListenerUpdateErrTuple
|
|
wantMD UpdateMetadata
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "non-listener resource",
|
|
resources: []*anypb.Any{{TypeUrl: version.V3HTTPConnManagerURL}},
|
|
wantMD: errMD,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "badly marshaled listener resource",
|
|
resources: []*anypb.Any{
|
|
{
|
|
TypeUrl: version.V3ListenerURL,
|
|
Value: func() []byte {
|
|
lis := &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
ApiListener: &v3listenerpb.ApiListener{
|
|
ApiListener: &anypb.Any{
|
|
TypeUrl: version.V3HTTPConnManagerURL,
|
|
Value: []byte{1, 2, 3, 4},
|
|
},
|
|
},
|
|
}
|
|
mLis, _ := proto.Marshal(lis)
|
|
return mLis
|
|
}(),
|
|
},
|
|
},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}},
|
|
wantMD: errMD,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "wrong type in apiListener",
|
|
resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
ApiListener: &v3listenerpb.ApiListener{
|
|
ApiListener: testutils.MarshalAny(&v2xdspb.Listener{}),
|
|
},
|
|
})},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}},
|
|
wantMD: errMD,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "empty httpConnMgr in apiListener",
|
|
resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
ApiListener: &v3listenerpb.ApiListener{
|
|
ApiListener: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
|
|
RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{
|
|
Rds: &v3httppb.Rds{},
|
|
},
|
|
}),
|
|
},
|
|
})},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}},
|
|
wantMD: errMD,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "scopedRoutes routeConfig in apiListener",
|
|
resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
ApiListener: &v3listenerpb.ApiListener{
|
|
ApiListener: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
|
|
RouteSpecifier: &v3httppb.HttpConnectionManager_ScopedRoutes{},
|
|
}),
|
|
},
|
|
})},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}},
|
|
wantMD: errMD,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "rds.ConfigSource in apiListener is not ADS",
|
|
resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
ApiListener: &v3listenerpb.ApiListener{
|
|
ApiListener: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
|
|
RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{
|
|
Rds: &v3httppb.Rds{
|
|
ConfigSource: &v3corepb.ConfigSource{
|
|
ConfigSourceSpecifier: &v3corepb.ConfigSource_Path{
|
|
Path: "/some/path",
|
|
},
|
|
},
|
|
RouteConfigName: v3RouteConfigName,
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
})},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}},
|
|
wantMD: errMD,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "empty resource list",
|
|
wantMD: UpdateMetadata{
|
|
Status: ServiceStatusACKed,
|
|
Version: testVersion,
|
|
},
|
|
},
|
|
{
|
|
name: "v3 with no filters",
|
|
resources: []*anypb.Any{v3LisWithFilters()},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{
|
|
v3LDSTarget: {Update: ListenerUpdate{RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second, HTTPFilters: routerFilterList, Raw: v3LisWithFilters()}},
|
|
},
|
|
wantMD: UpdateMetadata{
|
|
Status: ServiceStatusACKed,
|
|
Version: testVersion,
|
|
},
|
|
},
|
|
{
|
|
name: "v3 no terminal filter",
|
|
resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
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: v3RouteConfigName,
|
|
},
|
|
},
|
|
CommonHttpProtocolOptions: &v3corepb.HttpProtocolOptions{
|
|
MaxStreamDuration: durationpb.New(time.Second),
|
|
},
|
|
}),
|
|
},
|
|
})},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}},
|
|
wantMD: errMD,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "v3 with custom filter",
|
|
resources: []*anypb.Any{v3LisWithFilters(customFilter)},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{
|
|
v3LDSTarget: {Update: ListenerUpdate{
|
|
RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second,
|
|
HTTPFilters: []HTTPFilter{
|
|
{
|
|
Name: "customFilter",
|
|
Filter: httpFilter{},
|
|
Config: filterConfig{Cfg: customFilterConfig},
|
|
},
|
|
routerFilter,
|
|
},
|
|
Raw: v3LisWithFilters(customFilter),
|
|
}},
|
|
},
|
|
wantMD: UpdateMetadata{
|
|
Status: ServiceStatusACKed,
|
|
Version: testVersion,
|
|
},
|
|
},
|
|
{
|
|
name: "v3 with custom filter in typed struct",
|
|
resources: []*anypb.Any{v3LisWithFilters(typedStructFilter)},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{
|
|
v3LDSTarget: {Update: ListenerUpdate{
|
|
RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second,
|
|
HTTPFilters: []HTTPFilter{
|
|
{
|
|
Name: "customFilter",
|
|
Filter: httpFilter{},
|
|
Config: filterConfig{Cfg: customFilterTypedStructConfig},
|
|
},
|
|
routerFilter,
|
|
},
|
|
Raw: v3LisWithFilters(typedStructFilter),
|
|
}},
|
|
},
|
|
wantMD: UpdateMetadata{
|
|
Status: ServiceStatusACKed,
|
|
Version: testVersion,
|
|
},
|
|
},
|
|
{
|
|
name: "v3 with optional custom filter",
|
|
resources: []*anypb.Any{v3LisWithFilters(customOptionalFilter)},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{
|
|
v3LDSTarget: {Update: ListenerUpdate{
|
|
RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second,
|
|
HTTPFilters: []HTTPFilter{
|
|
{
|
|
Name: "customFilter",
|
|
Filter: httpFilter{},
|
|
Config: filterConfig{Cfg: customFilterConfig},
|
|
},
|
|
routerFilter,
|
|
},
|
|
Raw: v3LisWithFilters(customOptionalFilter),
|
|
}},
|
|
},
|
|
wantMD: UpdateMetadata{
|
|
Status: ServiceStatusACKed,
|
|
Version: testVersion,
|
|
},
|
|
},
|
|
{
|
|
name: "v3 with two filters with same name",
|
|
resources: []*anypb.Any{v3LisWithFilters(customFilter, customFilter)},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}},
|
|
wantMD: errMD,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "v3 with two filters - same type different name",
|
|
resources: []*anypb.Any{v3LisWithFilters(customFilter, customFilter2)},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{
|
|
v3LDSTarget: {Update: ListenerUpdate{
|
|
RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second,
|
|
HTTPFilters: []HTTPFilter{{
|
|
Name: "customFilter",
|
|
Filter: httpFilter{},
|
|
Config: filterConfig{Cfg: customFilterConfig},
|
|
}, {
|
|
Name: "customFilter2",
|
|
Filter: httpFilter{},
|
|
Config: filterConfig{Cfg: customFilterConfig},
|
|
},
|
|
routerFilter,
|
|
},
|
|
Raw: v3LisWithFilters(customFilter, customFilter2),
|
|
}},
|
|
},
|
|
wantMD: UpdateMetadata{
|
|
Status: ServiceStatusACKed,
|
|
Version: testVersion,
|
|
},
|
|
},
|
|
{
|
|
name: "v3 with server-only filter",
|
|
resources: []*anypb.Any{v3LisWithFilters(serverOnlyCustomFilter)},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}},
|
|
wantMD: errMD,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "v3 with optional server-only filter",
|
|
resources: []*anypb.Any{v3LisWithFilters(serverOnlyOptionalCustomFilter)},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{
|
|
v3LDSTarget: {Update: ListenerUpdate{
|
|
RouteConfigName: v3RouteConfigName,
|
|
MaxStreamDuration: time.Second,
|
|
Raw: v3LisWithFilters(serverOnlyOptionalCustomFilter),
|
|
HTTPFilters: routerFilterList,
|
|
}},
|
|
},
|
|
wantMD: UpdateMetadata{
|
|
Status: ServiceStatusACKed,
|
|
Version: testVersion,
|
|
},
|
|
},
|
|
{
|
|
name: "v3 with client-only filter",
|
|
resources: []*anypb.Any{v3LisWithFilters(clientOnlyCustomFilter)},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{
|
|
v3LDSTarget: {Update: ListenerUpdate{
|
|
RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second,
|
|
HTTPFilters: []HTTPFilter{
|
|
{
|
|
Name: "clientOnlyCustomFilter",
|
|
Filter: clientOnlyHTTPFilter{},
|
|
Config: filterConfig{Cfg: clientOnlyCustomFilterConfig},
|
|
},
|
|
routerFilter},
|
|
Raw: v3LisWithFilters(clientOnlyCustomFilter),
|
|
}},
|
|
},
|
|
wantMD: UpdateMetadata{
|
|
Status: ServiceStatusACKed,
|
|
Version: testVersion,
|
|
},
|
|
},
|
|
{
|
|
name: "v3 with err filter",
|
|
resources: []*anypb.Any{v3LisWithFilters(errFilter)},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}},
|
|
wantMD: errMD,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "v3 with optional err filter",
|
|
resources: []*anypb.Any{v3LisWithFilters(errOptionalFilter)},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}},
|
|
wantMD: errMD,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "v3 with unknown filter",
|
|
resources: []*anypb.Any{v3LisWithFilters(unknownFilter)},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}},
|
|
wantMD: errMD,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "v3 with unknown filter (optional)",
|
|
resources: []*anypb.Any{v3LisWithFilters(unknownOptionalFilter)},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{
|
|
v3LDSTarget: {Update: ListenerUpdate{
|
|
RouteConfigName: v3RouteConfigName,
|
|
MaxStreamDuration: time.Second,
|
|
HTTPFilters: routerFilterList,
|
|
Raw: v3LisWithFilters(unknownOptionalFilter),
|
|
}},
|
|
},
|
|
wantMD: UpdateMetadata{
|
|
Status: ServiceStatusACKed,
|
|
Version: testVersion,
|
|
},
|
|
},
|
|
{
|
|
name: "v2 listener resource",
|
|
resources: []*anypb.Any{v2Lis},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{
|
|
v2LDSTarget: {Update: ListenerUpdate{RouteConfigName: v2RouteConfigName, Raw: v2Lis}},
|
|
},
|
|
wantMD: UpdateMetadata{
|
|
Status: ServiceStatusACKed,
|
|
Version: testVersion,
|
|
},
|
|
},
|
|
{
|
|
name: "v3 listener resource",
|
|
resources: []*anypb.Any{v3LisWithFilters()},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{
|
|
v3LDSTarget: {Update: ListenerUpdate{RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second, HTTPFilters: routerFilterList, Raw: v3LisWithFilters()}},
|
|
},
|
|
wantMD: UpdateMetadata{
|
|
Status: ServiceStatusACKed,
|
|
Version: testVersion,
|
|
},
|
|
},
|
|
{
|
|
name: "v3 listener with inline route configuration",
|
|
resources: []*anypb.Any{v3LisWithInlineRoute},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{
|
|
v3LDSTarget: {Update: ListenerUpdate{
|
|
InlineRouteConfig: &RouteConfigUpdate{
|
|
VirtualHosts: []*VirtualHost{{
|
|
Domains: []string{v3LDSTarget},
|
|
Routes: []*Route{{Prefix: newStringP("/"), WeightedClusters: map[string]WeightedCluster{clusterName: {Weight: 1}}, RouteAction: RouteActionRoute}},
|
|
}}},
|
|
MaxStreamDuration: time.Second,
|
|
Raw: v3LisWithInlineRoute,
|
|
HTTPFilters: routerFilterList,
|
|
}},
|
|
},
|
|
wantMD: UpdateMetadata{
|
|
Status: ServiceStatusACKed,
|
|
Version: testVersion,
|
|
},
|
|
},
|
|
{
|
|
name: "multiple listener resources",
|
|
resources: []*anypb.Any{v2Lis, v3LisWithFilters()},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{
|
|
v2LDSTarget: {Update: ListenerUpdate{RouteConfigName: v2RouteConfigName, Raw: v2Lis}},
|
|
v3LDSTarget: {Update: ListenerUpdate{RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second, Raw: v3LisWithFilters(), HTTPFilters: routerFilterList}},
|
|
},
|
|
wantMD: UpdateMetadata{
|
|
Status: ServiceStatusACKed,
|
|
Version: testVersion,
|
|
},
|
|
},
|
|
{
|
|
// To test that unmarshal keeps processing on errors.
|
|
name: "good and bad listener resources",
|
|
resources: []*anypb.Any{
|
|
v2Lis,
|
|
testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: "bad",
|
|
ApiListener: &v3listenerpb.ApiListener{
|
|
ApiListener: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
|
|
RouteSpecifier: &v3httppb.HttpConnectionManager_ScopedRoutes{},
|
|
}),
|
|
}}),
|
|
v3LisWithFilters(),
|
|
},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{
|
|
v2LDSTarget: {Update: ListenerUpdate{RouteConfigName: v2RouteConfigName, Raw: v2Lis}},
|
|
v3LDSTarget: {Update: ListenerUpdate{RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second, Raw: v3LisWithFilters(), HTTPFilters: routerFilterList}},
|
|
"bad": {Err: cmpopts.AnyError},
|
|
},
|
|
wantMD: errMD,
|
|
wantErr: true,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
update, md, err := UnmarshalListener(testVersion, test.resources, nil)
|
|
if (err != nil) != test.wantErr {
|
|
t.Fatalf("UnmarshalListener(), got err: %v, wantErr: %v", err, test.wantErr)
|
|
}
|
|
if diff := cmp.Diff(update, test.wantUpdate, cmpOpts); diff != "" {
|
|
t.Errorf("got unexpected update, diff (-got +want): %v", diff)
|
|
}
|
|
if diff := cmp.Diff(md, test.wantMD, cmpOptsIgnoreDetails); diff != "" {
|
|
t.Errorf("got unexpected metadata, diff (-got +want): %v", diff)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func (s) TestUnmarshalListener_ServerSide(t *testing.T) {
|
|
oldRBAC := env.RBACSupport
|
|
env.RBACSupport = true
|
|
defer func() {
|
|
env.RBACSupport = oldRBAC
|
|
}()
|
|
const (
|
|
v3LDSTarget = "grpc/server?xds.resource.listening_address=0.0.0.0:9999"
|
|
testVersion = "test-version-lds-server"
|
|
)
|
|
|
|
var (
|
|
serverOnlyCustomFilter = &v3httppb.HttpFilter{
|
|
Name: "serverOnlyCustomFilter",
|
|
ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: serverOnlyCustomFilterConfig},
|
|
}
|
|
routeConfig = &v3routepb.RouteConfiguration{
|
|
Name: "routeName",
|
|
VirtualHosts: []*v3routepb.VirtualHost{{
|
|
Domains: []string{"lds.target.good:3333"},
|
|
Routes: []*v3routepb.Route{{
|
|
Match: &v3routepb.RouteMatch{
|
|
PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/"},
|
|
},
|
|
Action: &v3routepb.Route_NonForwardingAction{},
|
|
}}}}}
|
|
inlineRouteConfig = &RouteConfigUpdate{
|
|
VirtualHosts: []*VirtualHost{{
|
|
Domains: []string{"lds.target.good:3333"},
|
|
Routes: []*Route{{Prefix: newStringP("/"), RouteAction: RouteActionNonForwardingAction}},
|
|
}}}
|
|
emptyValidNetworkFilters = []*v3listenerpb.Filter{
|
|
{
|
|
Name: "filter-1",
|
|
ConfigType: &v3listenerpb.Filter_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
|
|
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
|
|
RouteConfig: routeConfig,
|
|
},
|
|
HttpFilters: []*v3httppb.HttpFilter{e2e.RouterHTTPFilter},
|
|
}),
|
|
},
|
|
},
|
|
}
|
|
localSocketAddress = &v3corepb.Address{
|
|
Address: &v3corepb.Address_SocketAddress{
|
|
SocketAddress: &v3corepb.SocketAddress{
|
|
Address: "0.0.0.0",
|
|
PortSpecifier: &v3corepb.SocketAddress_PortValue{
|
|
PortValue: 9999,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
listenerEmptyTransportSocket = testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: emptyValidNetworkFilters,
|
|
},
|
|
},
|
|
})
|
|
listenerNoValidationContextDeprecatedFields = testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: emptyValidNetworkFilters,
|
|
TransportSocket: &v3corepb.TransportSocket{
|
|
Name: "envoy.transport_sockets.tls",
|
|
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{
|
|
CommonTlsContext: &v3tlspb.CommonTlsContext{
|
|
TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
|
|
InstanceName: "identityPluginInstance",
|
|
CertificateName: "identityCertName",
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
DefaultFilterChain: &v3listenerpb.FilterChain{
|
|
Name: "default-filter-chain-1",
|
|
Filters: emptyValidNetworkFilters,
|
|
TransportSocket: &v3corepb.TransportSocket{
|
|
Name: "envoy.transport_sockets.tls",
|
|
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{
|
|
CommonTlsContext: &v3tlspb.CommonTlsContext{
|
|
TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
|
|
InstanceName: "defaultIdentityPluginInstance",
|
|
CertificateName: "defaultIdentityCertName",
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
})
|
|
listenerNoValidationContextNewFields = testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: emptyValidNetworkFilters,
|
|
TransportSocket: &v3corepb.TransportSocket{
|
|
Name: "envoy.transport_sockets.tls",
|
|
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{
|
|
CommonTlsContext: &v3tlspb.CommonTlsContext{
|
|
TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
|
|
InstanceName: "identityPluginInstance",
|
|
CertificateName: "identityCertName",
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
DefaultFilterChain: &v3listenerpb.FilterChain{
|
|
Name: "default-filter-chain-1",
|
|
Filters: emptyValidNetworkFilters,
|
|
TransportSocket: &v3corepb.TransportSocket{
|
|
Name: "envoy.transport_sockets.tls",
|
|
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{
|
|
CommonTlsContext: &v3tlspb.CommonTlsContext{
|
|
TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
|
|
InstanceName: "defaultIdentityPluginInstance",
|
|
CertificateName: "defaultIdentityCertName",
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
})
|
|
listenerWithValidationContextDeprecatedFields = testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: emptyValidNetworkFilters,
|
|
TransportSocket: &v3corepb.TransportSocket{
|
|
Name: "envoy.transport_sockets.tls",
|
|
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{
|
|
RequireClientCertificate: &wrapperspb.BoolValue{Value: true},
|
|
CommonTlsContext: &v3tlspb.CommonTlsContext{
|
|
TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
|
|
InstanceName: "identityPluginInstance",
|
|
CertificateName: "identityCertName",
|
|
},
|
|
ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{
|
|
ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
|
|
InstanceName: "rootPluginInstance",
|
|
CertificateName: "rootCertName",
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
DefaultFilterChain: &v3listenerpb.FilterChain{
|
|
Name: "default-filter-chain-1",
|
|
Filters: emptyValidNetworkFilters,
|
|
TransportSocket: &v3corepb.TransportSocket{
|
|
Name: "envoy.transport_sockets.tls",
|
|
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{
|
|
RequireClientCertificate: &wrapperspb.BoolValue{Value: true},
|
|
CommonTlsContext: &v3tlspb.CommonTlsContext{
|
|
TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
|
|
InstanceName: "defaultIdentityPluginInstance",
|
|
CertificateName: "defaultIdentityCertName",
|
|
},
|
|
ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{
|
|
ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
|
|
InstanceName: "defaultRootPluginInstance",
|
|
CertificateName: "defaultRootCertName",
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
})
|
|
listenerWithValidationContextNewFields = testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: emptyValidNetworkFilters,
|
|
TransportSocket: &v3corepb.TransportSocket{
|
|
Name: "envoy.transport_sockets.tls",
|
|
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{
|
|
RequireClientCertificate: &wrapperspb.BoolValue{Value: true},
|
|
CommonTlsContext: &v3tlspb.CommonTlsContext{
|
|
TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
|
|
InstanceName: "identityPluginInstance",
|
|
CertificateName: "identityCertName",
|
|
},
|
|
ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{
|
|
ValidationContext: &v3tlspb.CertificateValidationContext{
|
|
CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
|
|
InstanceName: "rootPluginInstance",
|
|
CertificateName: "rootCertName",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
DefaultFilterChain: &v3listenerpb.FilterChain{
|
|
Name: "default-filter-chain-1",
|
|
Filters: emptyValidNetworkFilters,
|
|
TransportSocket: &v3corepb.TransportSocket{
|
|
Name: "envoy.transport_sockets.tls",
|
|
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{
|
|
RequireClientCertificate: &wrapperspb.BoolValue{Value: true},
|
|
CommonTlsContext: &v3tlspb.CommonTlsContext{
|
|
TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
|
|
InstanceName: "defaultIdentityPluginInstance",
|
|
CertificateName: "defaultIdentityCertName",
|
|
},
|
|
ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{
|
|
CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{
|
|
DefaultValidationContext: &v3tlspb.CertificateValidationContext{
|
|
CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
|
|
InstanceName: "defaultRootPluginInstance",
|
|
CertificateName: "defaultRootCertName",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
})
|
|
errMD = UpdateMetadata{
|
|
Status: ServiceStatusNACKed,
|
|
Version: testVersion,
|
|
ErrState: &UpdateErrorMetadata{
|
|
Version: testVersion,
|
|
Err: cmpopts.AnyError,
|
|
},
|
|
}
|
|
)
|
|
|
|
tests := []struct {
|
|
name string
|
|
resources []*anypb.Any
|
|
wantUpdate map[string]ListenerUpdateErrTuple
|
|
wantMD UpdateMetadata
|
|
wantErr string
|
|
}{
|
|
{
|
|
name: "non-empty listener filters",
|
|
resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
ListenerFilters: []*v3listenerpb.ListenerFilter{
|
|
{Name: "listener-filter-1"},
|
|
},
|
|
})},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}},
|
|
wantMD: errMD,
|
|
wantErr: "unsupported field 'listener_filters'",
|
|
},
|
|
{
|
|
name: "use_original_dst is set",
|
|
resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
UseOriginalDst: &wrapperspb.BoolValue{Value: true},
|
|
})},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}},
|
|
wantMD: errMD,
|
|
wantErr: "unsupported field 'use_original_dst'",
|
|
},
|
|
{
|
|
name: "no address field",
|
|
resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{Name: v3LDSTarget})},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}},
|
|
wantMD: errMD,
|
|
wantErr: "no address field in LDS response",
|
|
},
|
|
{
|
|
name: "no socket address field",
|
|
resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: &v3corepb.Address{},
|
|
})},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}},
|
|
wantMD: errMD,
|
|
wantErr: "no socket_address field in LDS response",
|
|
},
|
|
{
|
|
name: "no filter chains and no default filter chain",
|
|
resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
FilterChainMatch: &v3listenerpb.FilterChainMatch{DestinationPort: &wrapperspb.UInt32Value{Value: 666}},
|
|
Filters: emptyValidNetworkFilters,
|
|
},
|
|
},
|
|
})},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}},
|
|
wantMD: errMD,
|
|
wantErr: "no supported filter chains and no default filter chain",
|
|
},
|
|
{
|
|
name: "missing http connection manager network filter",
|
|
resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
},
|
|
},
|
|
})},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}},
|
|
wantMD: errMD,
|
|
wantErr: "missing HttpConnectionManager filter",
|
|
},
|
|
{
|
|
name: "missing filter name in http filter",
|
|
resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: []*v3listenerpb.Filter{
|
|
{
|
|
ConfigType: &v3listenerpb.Filter_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}},
|
|
wantMD: errMD,
|
|
wantErr: "missing name field in filter",
|
|
},
|
|
{
|
|
name: "duplicate filter names in http filter",
|
|
resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: []*v3listenerpb.Filter{
|
|
{
|
|
Name: "name",
|
|
ConfigType: &v3listenerpb.Filter_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
|
|
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
|
|
RouteConfig: routeConfig,
|
|
},
|
|
HttpFilters: []*v3httppb.HttpFilter{emptyRouterFilter},
|
|
}),
|
|
},
|
|
},
|
|
{
|
|
Name: "name",
|
|
ConfigType: &v3listenerpb.Filter_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
|
|
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
|
|
RouteConfig: routeConfig,
|
|
},
|
|
HttpFilters: []*v3httppb.HttpFilter{emptyRouterFilter},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}},
|
|
wantMD: errMD,
|
|
wantErr: "duplicate filter name",
|
|
},
|
|
{
|
|
name: "no terminal filter",
|
|
resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: []*v3listenerpb.Filter{
|
|
{
|
|
Name: "name",
|
|
ConfigType: &v3listenerpb.Filter_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
|
|
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
|
|
RouteConfig: routeConfig,
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}},
|
|
wantMD: errMD,
|
|
wantErr: "http filters list is empty",
|
|
},
|
|
{
|
|
name: "terminal filter not last",
|
|
resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: []*v3listenerpb.Filter{
|
|
{
|
|
Name: "name",
|
|
ConfigType: &v3listenerpb.Filter_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
|
|
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
|
|
RouteConfig: routeConfig,
|
|
},
|
|
HttpFilters: []*v3httppb.HttpFilter{emptyRouterFilter, serverOnlyCustomFilter},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}},
|
|
wantMD: errMD,
|
|
wantErr: "is a terminal filter but it is not last in the filter chain",
|
|
},
|
|
{
|
|
name: "last not terminal filter",
|
|
resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: []*v3listenerpb.Filter{
|
|
{
|
|
Name: "name",
|
|
ConfigType: &v3listenerpb.Filter_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
|
|
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
|
|
RouteConfig: routeConfig,
|
|
},
|
|
HttpFilters: []*v3httppb.HttpFilter{serverOnlyCustomFilter},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}},
|
|
wantMD: errMD,
|
|
wantErr: "is not a terminal filter",
|
|
},
|
|
{
|
|
name: "unsupported oneof in typed config of http filter",
|
|
resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: []*v3listenerpb.Filter{
|
|
{
|
|
Name: "name",
|
|
ConfigType: &v3listenerpb.Filter_ConfigDiscovery{},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}},
|
|
wantMD: errMD,
|
|
wantErr: "unsupported config_type",
|
|
},
|
|
{
|
|
name: "overlapping filter chain match criteria",
|
|
resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
FilterChainMatch: &v3listenerpb.FilterChainMatch{SourcePorts: []uint32{1, 2, 3, 4, 5}},
|
|
Filters: emptyValidNetworkFilters,
|
|
},
|
|
{
|
|
FilterChainMatch: &v3listenerpb.FilterChainMatch{},
|
|
Filters: emptyValidNetworkFilters,
|
|
},
|
|
{
|
|
FilterChainMatch: &v3listenerpb.FilterChainMatch{SourcePorts: []uint32{5, 6, 7}},
|
|
Filters: emptyValidNetworkFilters,
|
|
},
|
|
},
|
|
})},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}},
|
|
wantMD: errMD,
|
|
wantErr: "multiple filter chains with overlapping matching rules are defined",
|
|
},
|
|
{
|
|
name: "unsupported network filter",
|
|
resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: []*v3listenerpb.Filter{
|
|
{
|
|
Name: "name",
|
|
ConfigType: &v3listenerpb.Filter_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(&v3httppb.LocalReplyConfig{}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}},
|
|
wantMD: errMD,
|
|
wantErr: "unsupported network filter",
|
|
},
|
|
{
|
|
name: "badly marshaled network filter",
|
|
resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: []*v3listenerpb.Filter{
|
|
{
|
|
Name: "name",
|
|
ConfigType: &v3listenerpb.Filter_TypedConfig{
|
|
TypedConfig: &anypb.Any{
|
|
TypeUrl: version.V3HTTPConnManagerURL,
|
|
Value: []byte{1, 2, 3, 4},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}},
|
|
wantMD: errMD,
|
|
wantErr: "failed unmarshaling of network filter",
|
|
},
|
|
{
|
|
name: "unexpected transport socket name",
|
|
resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: emptyValidNetworkFilters,
|
|
TransportSocket: &v3corepb.TransportSocket{
|
|
Name: "unsupported-transport-socket-name",
|
|
},
|
|
},
|
|
},
|
|
})},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}},
|
|
wantMD: errMD,
|
|
wantErr: "transport_socket field has unexpected name",
|
|
},
|
|
{
|
|
name: "unexpected transport socket typedConfig URL",
|
|
resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: emptyValidNetworkFilters,
|
|
TransportSocket: &v3corepb.TransportSocket{
|
|
Name: "envoy.transport_sockets.tls",
|
|
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(&v3tlspb.UpstreamTlsContext{}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}},
|
|
wantMD: errMD,
|
|
wantErr: "transport_socket field has unexpected typeURL",
|
|
},
|
|
{
|
|
name: "badly marshaled transport socket",
|
|
resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: emptyValidNetworkFilters,
|
|
TransportSocket: &v3corepb.TransportSocket{
|
|
Name: "envoy.transport_sockets.tls",
|
|
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
|
TypedConfig: &anypb.Any{
|
|
TypeUrl: version.V3DownstreamTLSContextURL,
|
|
Value: []byte{1, 2, 3, 4},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}},
|
|
wantMD: errMD,
|
|
wantErr: "failed to unmarshal DownstreamTlsContext in LDS response",
|
|
},
|
|
{
|
|
name: "missing CommonTlsContext",
|
|
resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: emptyValidNetworkFilters,
|
|
TransportSocket: &v3corepb.TransportSocket{
|
|
Name: "envoy.transport_sockets.tls",
|
|
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}},
|
|
wantMD: errMD,
|
|
wantErr: "DownstreamTlsContext in LDS response does not contain a CommonTlsContext",
|
|
},
|
|
{
|
|
name: "unsupported validation context in transport socket",
|
|
resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: emptyValidNetworkFilters,
|
|
TransportSocket: &v3corepb.TransportSocket{
|
|
Name: "envoy.transport_sockets.tls",
|
|
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{
|
|
CommonTlsContext: &v3tlspb.CommonTlsContext{
|
|
ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextSdsSecretConfig{
|
|
ValidationContextSdsSecretConfig: &v3tlspb.SdsSecretConfig{
|
|
Name: "foo-sds-secret",
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}},
|
|
wantMD: errMD,
|
|
wantErr: "validation context contains unexpected type",
|
|
},
|
|
{
|
|
name: "empty transport socket",
|
|
resources: []*anypb.Any{listenerEmptyTransportSocket},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{
|
|
v3LDSTarget: {Update: ListenerUpdate{
|
|
InboundListenerCfg: &InboundListenerConfig{
|
|
Address: "0.0.0.0",
|
|
Port: "9999",
|
|
FilterChains: &FilterChainManager{
|
|
dstPrefixMap: map[string]*destPrefixEntry{
|
|
unspecifiedPrefixMapKey: {
|
|
srcTypeArr: [3]*sourcePrefixes{
|
|
{
|
|
srcPrefixMap: map[string]*sourcePrefixEntry{
|
|
unspecifiedPrefixMapKey: {
|
|
srcPortMap: map[int]*FilterChain{
|
|
0: {
|
|
InlineRouteConfig: inlineRouteConfig,
|
|
HTTPFilters: routerFilterList,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Raw: listenerEmptyTransportSocket,
|
|
}},
|
|
},
|
|
wantMD: UpdateMetadata{
|
|
Status: ServiceStatusACKed,
|
|
Version: testVersion,
|
|
},
|
|
},
|
|
{
|
|
name: "no identity and root certificate providers using deprecated fields",
|
|
resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: emptyValidNetworkFilters,
|
|
TransportSocket: &v3corepb.TransportSocket{
|
|
Name: "envoy.transport_sockets.tls",
|
|
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{
|
|
RequireClientCertificate: &wrapperspb.BoolValue{Value: true},
|
|
CommonTlsContext: &v3tlspb.CommonTlsContext{
|
|
TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
|
|
InstanceName: "identityPluginInstance",
|
|
CertificateName: "identityCertName",
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}},
|
|
wantMD: errMD,
|
|
wantErr: "security configuration on the server-side does not contain root certificate provider instance name, but require_client_cert field is set",
|
|
},
|
|
{
|
|
name: "no identity and root certificate providers using new fields",
|
|
resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: emptyValidNetworkFilters,
|
|
TransportSocket: &v3corepb.TransportSocket{
|
|
Name: "envoy.transport_sockets.tls",
|
|
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{
|
|
RequireClientCertificate: &wrapperspb.BoolValue{Value: true},
|
|
CommonTlsContext: &v3tlspb.CommonTlsContext{
|
|
TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
|
|
InstanceName: "identityPluginInstance",
|
|
CertificateName: "identityCertName",
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}},
|
|
wantMD: errMD,
|
|
wantErr: "security configuration on the server-side does not contain root certificate provider instance name, but require_client_cert field is set",
|
|
},
|
|
{
|
|
name: "no identity certificate provider with require_client_cert",
|
|
resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: emptyValidNetworkFilters,
|
|
TransportSocket: &v3corepb.TransportSocket{
|
|
Name: "envoy.transport_sockets.tls",
|
|
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{
|
|
CommonTlsContext: &v3tlspb.CommonTlsContext{},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}},
|
|
wantMD: errMD,
|
|
wantErr: "security configuration on the server-side does not contain identity certificate provider instance name",
|
|
},
|
|
{
|
|
name: "happy case with no validation context using deprecated fields",
|
|
resources: []*anypb.Any{listenerNoValidationContextDeprecatedFields},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{
|
|
v3LDSTarget: {Update: ListenerUpdate{
|
|
InboundListenerCfg: &InboundListenerConfig{
|
|
Address: "0.0.0.0",
|
|
Port: "9999",
|
|
FilterChains: &FilterChainManager{
|
|
dstPrefixMap: map[string]*destPrefixEntry{
|
|
unspecifiedPrefixMapKey: {
|
|
srcTypeArr: [3]*sourcePrefixes{
|
|
{
|
|
srcPrefixMap: map[string]*sourcePrefixEntry{
|
|
unspecifiedPrefixMapKey: {
|
|
srcPortMap: map[int]*FilterChain{
|
|
0: {
|
|
SecurityCfg: &SecurityConfig{
|
|
IdentityInstanceName: "identityPluginInstance",
|
|
IdentityCertName: "identityCertName",
|
|
},
|
|
InlineRouteConfig: inlineRouteConfig,
|
|
HTTPFilters: routerFilterList,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
def: &FilterChain{
|
|
SecurityCfg: &SecurityConfig{
|
|
IdentityInstanceName: "defaultIdentityPluginInstance",
|
|
IdentityCertName: "defaultIdentityCertName",
|
|
},
|
|
InlineRouteConfig: inlineRouteConfig,
|
|
HTTPFilters: routerFilterList,
|
|
},
|
|
},
|
|
},
|
|
Raw: listenerNoValidationContextDeprecatedFields,
|
|
}},
|
|
},
|
|
wantMD: UpdateMetadata{
|
|
Status: ServiceStatusACKed,
|
|
Version: testVersion,
|
|
},
|
|
},
|
|
{
|
|
name: "happy case with no validation context using new fields",
|
|
resources: []*anypb.Any{listenerNoValidationContextNewFields},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{
|
|
v3LDSTarget: {Update: ListenerUpdate{
|
|
InboundListenerCfg: &InboundListenerConfig{
|
|
Address: "0.0.0.0",
|
|
Port: "9999",
|
|
FilterChains: &FilterChainManager{
|
|
dstPrefixMap: map[string]*destPrefixEntry{
|
|
unspecifiedPrefixMapKey: {
|
|
srcTypeArr: [3]*sourcePrefixes{
|
|
{
|
|
srcPrefixMap: map[string]*sourcePrefixEntry{
|
|
unspecifiedPrefixMapKey: {
|
|
srcPortMap: map[int]*FilterChain{
|
|
0: {
|
|
SecurityCfg: &SecurityConfig{
|
|
IdentityInstanceName: "identityPluginInstance",
|
|
IdentityCertName: "identityCertName",
|
|
},
|
|
InlineRouteConfig: inlineRouteConfig,
|
|
HTTPFilters: routerFilterList,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
def: &FilterChain{
|
|
SecurityCfg: &SecurityConfig{
|
|
IdentityInstanceName: "defaultIdentityPluginInstance",
|
|
IdentityCertName: "defaultIdentityCertName",
|
|
},
|
|
InlineRouteConfig: inlineRouteConfig,
|
|
HTTPFilters: routerFilterList,
|
|
},
|
|
},
|
|
},
|
|
Raw: listenerNoValidationContextNewFields,
|
|
}},
|
|
},
|
|
wantMD: UpdateMetadata{
|
|
Status: ServiceStatusACKed,
|
|
Version: testVersion,
|
|
},
|
|
},
|
|
{
|
|
name: "happy case with validation context provider instance with deprecated fields",
|
|
resources: []*anypb.Any{listenerWithValidationContextDeprecatedFields},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{
|
|
v3LDSTarget: {Update: ListenerUpdate{
|
|
InboundListenerCfg: &InboundListenerConfig{
|
|
Address: "0.0.0.0",
|
|
Port: "9999",
|
|
FilterChains: &FilterChainManager{
|
|
dstPrefixMap: map[string]*destPrefixEntry{
|
|
unspecifiedPrefixMapKey: {
|
|
srcTypeArr: [3]*sourcePrefixes{
|
|
{
|
|
srcPrefixMap: map[string]*sourcePrefixEntry{
|
|
unspecifiedPrefixMapKey: {
|
|
srcPortMap: map[int]*FilterChain{
|
|
0: {
|
|
SecurityCfg: &SecurityConfig{
|
|
RootInstanceName: "rootPluginInstance",
|
|
RootCertName: "rootCertName",
|
|
IdentityInstanceName: "identityPluginInstance",
|
|
IdentityCertName: "identityCertName",
|
|
RequireClientCert: true,
|
|
},
|
|
InlineRouteConfig: inlineRouteConfig,
|
|
HTTPFilters: routerFilterList,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
def: &FilterChain{
|
|
SecurityCfg: &SecurityConfig{
|
|
RootInstanceName: "defaultRootPluginInstance",
|
|
RootCertName: "defaultRootCertName",
|
|
IdentityInstanceName: "defaultIdentityPluginInstance",
|
|
IdentityCertName: "defaultIdentityCertName",
|
|
RequireClientCert: true,
|
|
},
|
|
InlineRouteConfig: inlineRouteConfig,
|
|
HTTPFilters: routerFilterList,
|
|
},
|
|
},
|
|
},
|
|
Raw: listenerWithValidationContextDeprecatedFields,
|
|
}},
|
|
},
|
|
wantMD: UpdateMetadata{
|
|
Status: ServiceStatusACKed,
|
|
Version: testVersion,
|
|
},
|
|
},
|
|
{
|
|
name: "happy case with validation context provider instance with new fields",
|
|
resources: []*anypb.Any{listenerWithValidationContextNewFields},
|
|
wantUpdate: map[string]ListenerUpdateErrTuple{
|
|
v3LDSTarget: {Update: ListenerUpdate{
|
|
InboundListenerCfg: &InboundListenerConfig{
|
|
Address: "0.0.0.0",
|
|
Port: "9999",
|
|
FilterChains: &FilterChainManager{
|
|
dstPrefixMap: map[string]*destPrefixEntry{
|
|
unspecifiedPrefixMapKey: {
|
|
srcTypeArr: [3]*sourcePrefixes{
|
|
{
|
|
srcPrefixMap: map[string]*sourcePrefixEntry{
|
|
unspecifiedPrefixMapKey: {
|
|
srcPortMap: map[int]*FilterChain{
|
|
0: {
|
|
SecurityCfg: &SecurityConfig{
|
|
RootInstanceName: "rootPluginInstance",
|
|
RootCertName: "rootCertName",
|
|
IdentityInstanceName: "identityPluginInstance",
|
|
IdentityCertName: "identityCertName",
|
|
RequireClientCert: true,
|
|
},
|
|
InlineRouteConfig: inlineRouteConfig,
|
|
HTTPFilters: routerFilterList,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
def: &FilterChain{
|
|
SecurityCfg: &SecurityConfig{
|
|
RootInstanceName: "defaultRootPluginInstance",
|
|
RootCertName: "defaultRootCertName",
|
|
IdentityInstanceName: "defaultIdentityPluginInstance",
|
|
IdentityCertName: "defaultIdentityCertName",
|
|
RequireClientCert: true,
|
|
},
|
|
InlineRouteConfig: inlineRouteConfig,
|
|
HTTPFilters: routerFilterList,
|
|
},
|
|
},
|
|
},
|
|
Raw: listenerWithValidationContextNewFields,
|
|
}},
|
|
},
|
|
wantMD: UpdateMetadata{
|
|
Status: ServiceStatusACKed,
|
|
Version: testVersion,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
gotUpdate, md, err := UnmarshalListener(testVersion, test.resources, nil)
|
|
if (err != nil) != (test.wantErr != "") {
|
|
t.Fatalf("UnmarshalListener(), got err: %v, wantErr: %v", err, test.wantErr)
|
|
}
|
|
if err != nil && !strings.Contains(err.Error(), test.wantErr) {
|
|
t.Fatalf("UnmarshalListener() = %v wantErr: %q", err, test.wantErr)
|
|
}
|
|
if diff := cmp.Diff(gotUpdate, test.wantUpdate, cmpOpts); diff != "" {
|
|
t.Errorf("got unexpected update, diff (-got +want): %v", diff)
|
|
}
|
|
if diff := cmp.Diff(md, test.wantMD, cmpOptsIgnoreDetails); diff != "" {
|
|
t.Errorf("got unexpected metadata, diff (-got +want): %v", diff)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
type filterConfig struct {
|
|
httpfilter.FilterConfig
|
|
Cfg proto.Message
|
|
Override proto.Message
|
|
}
|
|
|
|
// httpFilter allows testing the http filter registry and parsing functionality.
|
|
type httpFilter struct {
|
|
httpfilter.ClientInterceptorBuilder
|
|
httpfilter.ServerInterceptorBuilder
|
|
}
|
|
|
|
func (httpFilter) TypeURLs() []string { return []string{"custom.filter"} }
|
|
|
|
func (httpFilter) ParseFilterConfig(cfg proto.Message) (httpfilter.FilterConfig, error) {
|
|
return filterConfig{Cfg: cfg}, nil
|
|
}
|
|
|
|
func (httpFilter) ParseFilterConfigOverride(override proto.Message) (httpfilter.FilterConfig, error) {
|
|
return filterConfig{Override: override}, nil
|
|
}
|
|
|
|
func (httpFilter) IsTerminal() bool {
|
|
return false
|
|
}
|
|
|
|
// errHTTPFilter returns errors no matter what is passed to ParseFilterConfig.
|
|
type errHTTPFilter struct {
|
|
httpfilter.ClientInterceptorBuilder
|
|
}
|
|
|
|
func (errHTTPFilter) TypeURLs() []string { return []string{"err.custom.filter"} }
|
|
|
|
func (errHTTPFilter) ParseFilterConfig(cfg proto.Message) (httpfilter.FilterConfig, error) {
|
|
return nil, fmt.Errorf("error from ParseFilterConfig")
|
|
}
|
|
|
|
func (errHTTPFilter) ParseFilterConfigOverride(override proto.Message) (httpfilter.FilterConfig, error) {
|
|
return nil, fmt.Errorf("error from ParseFilterConfigOverride")
|
|
}
|
|
|
|
func (errHTTPFilter) IsTerminal() bool {
|
|
return false
|
|
}
|
|
|
|
func init() {
|
|
httpfilter.Register(httpFilter{})
|
|
httpfilter.Register(errHTTPFilter{})
|
|
httpfilter.Register(serverOnlyHTTPFilter{})
|
|
httpfilter.Register(clientOnlyHTTPFilter{})
|
|
}
|
|
|
|
// serverOnlyHTTPFilter does not implement ClientInterceptorBuilder
|
|
type serverOnlyHTTPFilter struct {
|
|
httpfilter.ServerInterceptorBuilder
|
|
}
|
|
|
|
func (serverOnlyHTTPFilter) TypeURLs() []string { return []string{"serverOnly.custom.filter"} }
|
|
|
|
func (serverOnlyHTTPFilter) ParseFilterConfig(cfg proto.Message) (httpfilter.FilterConfig, error) {
|
|
return filterConfig{Cfg: cfg}, nil
|
|
}
|
|
|
|
func (serverOnlyHTTPFilter) ParseFilterConfigOverride(override proto.Message) (httpfilter.FilterConfig, error) {
|
|
return filterConfig{Override: override}, nil
|
|
}
|
|
|
|
func (serverOnlyHTTPFilter) IsTerminal() bool {
|
|
return false
|
|
}
|
|
|
|
// clientOnlyHTTPFilter does not implement ServerInterceptorBuilder
|
|
type clientOnlyHTTPFilter struct {
|
|
httpfilter.ClientInterceptorBuilder
|
|
}
|
|
|
|
func (clientOnlyHTTPFilter) TypeURLs() []string { return []string{"clientOnly.custom.filter"} }
|
|
|
|
func (clientOnlyHTTPFilter) ParseFilterConfig(cfg proto.Message) (httpfilter.FilterConfig, error) {
|
|
return filterConfig{Cfg: cfg}, nil
|
|
}
|
|
|
|
func (clientOnlyHTTPFilter) ParseFilterConfigOverride(override proto.Message) (httpfilter.FilterConfig, error) {
|
|
return filterConfig{Override: override}, nil
|
|
}
|
|
|
|
func (clientOnlyHTTPFilter) IsTerminal() bool {
|
|
return false
|
|
}
|
|
|
|
var customFilterConfig = &anypb.Any{
|
|
TypeUrl: "custom.filter",
|
|
Value: []byte{1, 2, 3},
|
|
}
|
|
|
|
var errFilterConfig = &anypb.Any{
|
|
TypeUrl: "err.custom.filter",
|
|
Value: []byte{1, 2, 3},
|
|
}
|
|
|
|
var serverOnlyCustomFilterConfig = &anypb.Any{
|
|
TypeUrl: "serverOnly.custom.filter",
|
|
Value: []byte{1, 2, 3},
|
|
}
|
|
|
|
var clientOnlyCustomFilterConfig = &anypb.Any{
|
|
TypeUrl: "clientOnly.custom.filter",
|
|
Value: []byte{1, 2, 3},
|
|
}
|
|
|
|
var customFilterTypedStructConfig = &v1typepb.TypedStruct{
|
|
TypeUrl: "custom.filter",
|
|
Value: &spb.Struct{
|
|
Fields: map[string]*spb.Value{
|
|
"foo": {Kind: &spb.Value_StringValue{StringValue: "bar"}},
|
|
},
|
|
},
|
|
}
|
|
var wrappedCustomFilterTypedStructConfig *anypb.Any
|
|
|
|
func init() {
|
|
wrappedCustomFilterTypedStructConfig = testutils.MarshalAny(customFilterTypedStructConfig)
|
|
}
|
|
|
|
var unknownFilterConfig = &anypb.Any{
|
|
TypeUrl: "unknown.custom.filter",
|
|
Value: []byte{1, 2, 3},
|
|
}
|
|
|
|
func wrappedOptionalFilter(name string) *anypb.Any {
|
|
return testutils.MarshalAny(&v3routepb.FilterConfig{
|
|
IsOptional: true,
|
|
Config: &anypb.Any{
|
|
TypeUrl: name,
|
|
Value: []byte{1, 2, 3},
|
|
},
|
|
})
|
|
}
|