xds: add HTTP filter is_optional support (gRFC A39) (#4221)

This commit is contained in:
Doug Fawley 2021-03-02 09:11:35 -08:00 коммит произвёл GitHub
Родитель fc8f38cccf
Коммит 29bf29e0ed
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
8 изменённых файлов: 168 добавлений и 18 удалений

Просмотреть файл

@ -5,8 +5,8 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 h1:cqQfy1jclcSy/FwLjemeg3SR1yaINm74aQyupQ0Bl8M=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad h1:EmNYJhPYy0pOFjCx2PrgtaBXmee0iUX9hLlxE1xHOJE=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d h1:QyzYnTnPE15SQyUeqU6qLbWxMkwyAyu+vGksa0b7j00=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=

2
go.mod
Просмотреть файл

@ -4,7 +4,7 @@ go 1.11
require (
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
github.com/golang/protobuf v1.4.2
github.com/google/go-cmp v0.5.0

4
go.sum
Просмотреть файл

@ -12,8 +12,8 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad h1:EmNYJhPYy0pOFjCx2PrgtaBXmee0iUX9hLlxE1xHOJE=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d h1:QyzYnTnPE15SQyUeqU6qLbWxMkwyAyu+vGksa0b7j00=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=

Просмотреть файл

@ -2,7 +2,7 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=

Просмотреть файл

@ -2,7 +2,7 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=

Просмотреть файл

@ -41,6 +41,7 @@ import (
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"
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"
v3tlspb "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
anypb "github.com/golang/protobuf/ptypes/any"
@ -92,6 +93,11 @@ func (s) TestUnmarshalListener_ClientSide(t *testing.T) {
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},
@ -100,6 +106,11 @@ func (s) TestUnmarshalListener_ClientSide(t *testing.T) {
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},
@ -108,6 +119,20 @@ func (s) TestUnmarshalListener_ClientSide(t *testing.T) {
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,
}
v3LisWithFilters = func(fs ...*v3httppb.HttpFilter) *anypb.Any {
hcm := &v3httppb.HttpConnectionManager{
RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{
@ -342,6 +367,20 @@ func (s) TestUnmarshalListener_ClientSide(t *testing.T) {
},
},
},
{
name: "v3 with optional custom filter",
resources: []*anypb.Any{v3LisWithFilters(customOptionalFilter)},
wantUpdate: map[string]ListenerUpdate{
v3LDSTarget: {
RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second,
HTTPFilters: []HTTPFilter{{
Name: "customFilter",
Filter: httpFilter{},
Config: filterConfig{Cfg: customFilterConfig},
}},
},
},
},
{
name: "v3 with custom filter, fault injection disabled",
resources: []*anypb.Any{v3LisWithFilters(customFilter)},
@ -378,6 +417,15 @@ func (s) TestUnmarshalListener_ClientSide(t *testing.T) {
resources: []*anypb.Any{v3LisWithFilters(serverOnlyCustomFilter)},
wantErr: true,
},
{
name: "v3 with optional server-only filter",
resources: []*anypb.Any{v3LisWithFilters(serverOnlyOptionalCustomFilter)},
wantUpdate: map[string]ListenerUpdate{
v3LDSTarget: {
RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second,
},
},
},
{
name: "v3 with client-only filter",
resources: []*anypb.Any{v3LisWithFilters(clientOnlyCustomFilter)},
@ -397,6 +445,23 @@ func (s) TestUnmarshalListener_ClientSide(t *testing.T) {
resources: []*anypb.Any{v3LisWithFilters(errFilter)},
wantErr: true,
},
{
name: "v3 with optional err filter",
resources: []*anypb.Any{v3LisWithFilters(errOptionalFilter)},
wantErr: true,
},
{
name: "v3 with unknown filter",
resources: []*anypb.Any{v3LisWithFilters(unknownFilter)},
wantErr: true,
},
{
name: "v3 with unknown filter (optional)",
resources: []*anypb.Any{v3LisWithFilters(unknownOptionalFilter)},
wantUpdate: map[string]ListenerUpdate{
v3LDSTarget: {RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second},
},
},
{
name: "v3 with error filter, fault injection disabled",
resources: []*anypb.Any{v3LisWithFilters(errFilter)},
@ -1190,3 +1255,23 @@ func init() {
panic(err.Error())
}
}
var unknownFilterConfig = &anypb.Any{
TypeUrl: "unknown.custom.filter",
Value: []byte{1, 2, 3},
}
func wrappedOptionalFilter(name string) *anypb.Any {
filter := &v3routepb.FilterConfig{
IsOptional: true,
Config: &anypb.Any{
TypeUrl: name,
Value: []byte{1, 2, 3},
},
}
w, err := ptypes.MarshalAny(filter)
if err != nil {
panic("error marshalling any: " + err.Error())
}
return w
}

Просмотреть файл

@ -445,11 +445,31 @@ func (s) TestRDSGenerateRDSUpdateFromRouteConfiguration(t *testing.T) {
rc: goodRouteConfigWithFilterConfigs(map[string]*anypb.Any{"foo": wrappedCustomFilterTypedStructConfig}),
wantUpdate: goodUpdateWithFilterConfigs(map[string]httpfilter.FilterConfig{"foo": filterConfig{Override: customFilterTypedStructConfig}}),
},
{
name: "good-route-config-with-optional-http-filter-config",
rc: goodRouteConfigWithFilterConfigs(map[string]*anypb.Any{"foo": wrappedOptionalFilter("custom.filter")}),
wantUpdate: goodUpdateWithFilterConfigs(map[string]httpfilter.FilterConfig{"foo": filterConfig{Override: customFilterConfig}}),
},
{
name: "good-route-config-with-http-err-filter-config",
rc: goodRouteConfigWithFilterConfigs(map[string]*anypb.Any{"foo": errFilterConfig}),
wantError: true,
},
{
name: "good-route-config-with-http-optional-err-filter-config",
rc: goodRouteConfigWithFilterConfigs(map[string]*anypb.Any{"foo": wrappedOptionalFilter("err.custom.filter")}),
wantError: true,
},
{
name: "good-route-config-with-http-unknown-filter-config",
rc: goodRouteConfigWithFilterConfigs(map[string]*anypb.Any{"foo": unknownFilterConfig}),
wantError: true,
},
{
name: "good-route-config-with-http-optional-unknown-filter-config",
rc: goodRouteConfigWithFilterConfigs(map[string]*anypb.Any{"foo": wrappedOptionalFilter("unknown.custom.filter")}),
wantUpdate: goodUpdateWithFilterConfigs(nil),
},
{
name: "good-route-config-with-http-err-filter-config-fi-disabled",
disableFI: true,
@ -897,6 +917,11 @@ func (s) TestRoutesProtoToSlice(t *testing.T) {
routes: goodRouteWithFilterConfigs(map[string]*anypb.Any{"foo": wrappedCustomFilterTypedStructConfig}),
wantRoutes: goodUpdateWithFilterConfigs(map[string]httpfilter.FilterConfig{"foo": filterConfig{Override: customFilterTypedStructConfig}}),
},
{
name: "with optional custom HTTP filter config",
routes: goodRouteWithFilterConfigs(map[string]*anypb.Any{"foo": wrappedOptionalFilter("custom.filter")}),
wantRoutes: goodUpdateWithFilterConfigs(map[string]httpfilter.FilterConfig{"foo": filterConfig{Override: customFilterConfig}}),
},
{
name: "with custom HTTP filter config, FI disabled",
disableFI: true,
@ -908,12 +933,27 @@ func (s) TestRoutesProtoToSlice(t *testing.T) {
routes: goodRouteWithFilterConfigs(map[string]*anypb.Any{"foo": errFilterConfig}),
wantErr: true,
},
{
name: "with optional erroring custom HTTP filter config",
routes: goodRouteWithFilterConfigs(map[string]*anypb.Any{"foo": wrappedOptionalFilter("err.custom.filter")}),
wantErr: true,
},
{
name: "with erroring custom HTTP filter config, FI disabled",
disableFI: true,
routes: goodRouteWithFilterConfigs(map[string]*anypb.Any{"foo": errFilterConfig}),
wantRoutes: goodUpdateWithFilterConfigs(nil),
},
{
name: "with unknown custom HTTP filter config",
routes: goodRouteWithFilterConfigs(map[string]*anypb.Any{"foo": unknownFilterConfig}),
wantErr: true,
},
{
name: "with optional unknown custom HTTP filter config",
routes: goodRouteWithFilterConfigs(map[string]*anypb.Any{"foo": wrappedOptionalFilter("unknown.custom.filter")}),
wantRoutes: goodUpdateWithFilterConfigs(nil),
},
}
cmpOpts := []cmp.Option{

Просмотреть файл

@ -133,24 +133,27 @@ func processClientSideListener(lis *v3listenerpb.Listener, v2 bool) (*ListenerUp
}
func unwrapHTTPFilterConfig(config *anypb.Any) (proto.Message, string, error) {
if typeURL := config.GetTypeUrl(); typeURL != "type.googleapis.com/udpa.type.v1.TypedStruct" {
return config, typeURL, nil
}
// The real type name is inside the TypedStruct.
s := new(v1typepb.TypedStruct)
if !ptypes.Is(config, s) {
return config, config.GetTypeUrl(), nil
}
if err := ptypes.UnmarshalAny(config, s); err != nil {
return nil, "", fmt.Errorf("error unmarshalling TypedStruct filter config: %v", err)
}
return s, s.GetTypeUrl(), nil
}
func validateHTTPFilterConfig(cfg *anypb.Any, lds bool) (httpfilter.Filter, httpfilter.FilterConfig, error) {
func validateHTTPFilterConfig(cfg *anypb.Any, lds, optional bool) (httpfilter.Filter, httpfilter.FilterConfig, error) {
config, typeURL, err := unwrapHTTPFilterConfig(cfg)
if err != nil {
return nil, nil, err
}
filterBuilder := httpfilter.Get(typeURL)
if filterBuilder == nil {
if optional {
return nil, nil, nil
}
return nil, nil, fmt.Errorf("no filter implementation found for %q", typeURL)
}
parseFunc := filterBuilder.ParseFilterConfig
@ -170,9 +173,23 @@ func processHTTPFilterOverrides(cfgs map[string]*anypb.Any) (map[string]httpfilt
}
m := make(map[string]httpfilter.FilterConfig)
for name, cfg := range cfgs {
_, config, err := validateHTTPFilterConfig(cfg, false)
optional := false
s := new(v3routepb.FilterConfig)
if ptypes.Is(cfg, s) {
if err := ptypes.UnmarshalAny(cfg, s); err != nil {
return nil, fmt.Errorf("filter override %q: error unmarshalling FilterConfig: %v", name, err)
}
cfg = s.GetConfig()
optional = s.GetIsOptional()
}
httpFilter, config, err := validateHTTPFilterConfig(cfg, false, optional)
if err != nil {
return nil, err
return nil, fmt.Errorf("filter override %q: %v", name, err)
}
if httpFilter == nil {
// Optional configs are ignored.
continue
}
m[name] = config
}
@ -196,18 +213,26 @@ func processHTTPFilters(filters []*v3httppb.HttpFilter, server bool) ([]HTTPFilt
}
seenNames[name] = true
httpFilter, config, err := validateHTTPFilterConfig(filter.GetTypedConfig(), true)
httpFilter, config, err := validateHTTPFilterConfig(filter.GetTypedConfig(), true, filter.GetIsOptional())
if err != nil {
return nil, err
}
if httpFilter == nil {
// Optional configs are ignored.
continue
}
if server {
if _, ok := httpFilter.(httpfilter.ServerInterceptorBuilder); !ok {
return nil, fmt.Errorf("httpFilter %q not supported server-side", name)
if filter.GetIsOptional() {
continue
}
} else {
if _, ok := httpFilter.(httpfilter.ClientInterceptorBuilder); !ok {
return nil, fmt.Errorf("httpFilter %q not supported client-side", name)
return nil, fmt.Errorf("HTTP filter %q not supported server-side", name)
}
} else if _, ok := httpFilter.(httpfilter.ClientInterceptorBuilder); !ok {
if filter.GetIsOptional() {
continue
}
return nil, fmt.Errorf("HTTP filter %q not supported client-side", name)
}
// Save name/config