blob: 0839cfa271df10c3e538810cca1a9d9f92743c42 [file] [log] [blame]
// Copyright Istio 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 listenertest
import (
"fmt"
)
import (
listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
)
import (
xdsfilters "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/filters"
"github.com/apache/dubbo-go-pixiu/pilot/test/xdstest"
"github.com/apache/dubbo-go-pixiu/pkg/test"
"github.com/apache/dubbo-go-pixiu/pkg/test/util/assert"
"github.com/apache/dubbo-go-pixiu/pkg/util/sets"
)
type ListenersTest struct {
// Match listener by name
Name string
// Match listener by port
Port uint32
// Listener assertions
Listener ListenerTest
}
// ListenerTest provides a struct for defining expectations for a listener
type ListenerTest struct {
// Assert the listener contains these filter chains (in order, if TotalMatch)
FilterChains []FilterChainTest
// Assert the listener contains these ListenerFilters (in order, if TotalMatch)
Filters []string
// TotalMatch will require that the all elements exactly match (eg, if I have 3 elements in the
// check, the listener must as well). Otherwise, we only validate the assertions we provided are
// present.
TotalMatch bool
}
type FilterChainTest struct {
// Match a filter chain by name
Name string
// Match filter chain by 'type'. This can be important since Name is currently not unique
Type FilterChainType
// Port the filter chain matches
Port uint32
NetworkFilters []string
HTTPFilters []string
ValidateHCM func(t test.Failer, hcm *hcm.HttpConnectionManager)
TotalMatch bool
}
type FilterChainType string
const (
PlainTCP FilterChainType = "plaintext TCP"
PlainHTTP FilterChainType = "plaintext HTTP"
StandardTLS FilterChainType = "TLS"
MTLSTCP FilterChainType = "mTLS TCP"
MTLSHTTP FilterChainType = "mTLS HTTP"
Unknown FilterChainType = "unknown"
)
func classifyFilterChain(have *listener.FilterChain) FilterChainType {
fcm := have.GetFilterChainMatch()
alpn := sets.New(fcm.GetApplicationProtocols()...)
switch fcm.GetTransportProtocol() {
case xdsfilters.TLSTransportProtocol:
if alpn.Contains("istio-http/1.1") {
return MTLSHTTP
}
if alpn.Contains("istio") {
return MTLSTCP
}
return StandardTLS
case xdsfilters.RawBufferTransportProtocol:
if alpn.Contains("http/1.1") {
return PlainHTTP
}
return PlainTCP
default:
return Unknown
}
}
func VerifyListeners(t test.Failer, listeners []*listener.Listener, lt ListenersTest) {
t.Helper()
for _, l := range listeners {
if lt.Name != "" && lt.Name != l.Name {
continue
}
if lt.Port != 0 && lt.Port != l.Address.GetSocketAddress().GetPortValue() {
continue
}
// It was a match, run assertions
VerifyListener(t, l, lt.Listener)
}
}
func VerifyListener(t test.Failer, l *listener.Listener, lt ListenerTest) {
t.Helper()
haveFilters := []string{}
for _, lf := range l.ListenerFilters {
haveFilters = append(haveFilters, lf.Name)
}
// Check ListenerFilters
if lt.Filters != nil {
if lt.TotalMatch {
assert.Equal(t, lt.Filters, haveFilters, l.Name+": listener filters should be equal")
} else {
if missing := sets.New(lt.Filters...).Difference(sets.New(haveFilters...)).SortedList(); len(missing) > 0 {
t.Fatalf("%v: missing listener filters: %v", l.Name, missing)
}
}
}
// Check FilterChains
if lt.FilterChains != nil {
if lt.TotalMatch {
// First check they are the same size
if len(lt.FilterChains) != len(l.FilterChains) {
want := []string{}
for _, n := range lt.FilterChains {
want = append(want, n.Name)
}
t.Fatalf("didn't match filter chains, have names %v, expected %v", xdstest.ExtractFilterChainNames(l), want)
}
// Now check they are equivalent
for i := range lt.FilterChains {
have := l.FilterChains[i]
want := lt.FilterChains[i]
VerifyFilterChain(t, have, want)
}
} else {
for _, want := range lt.FilterChains {
found := 0
for _, have := range l.FilterChains {
if want.Name != "" && want.Name != have.Name {
continue
}
haveType := classifyFilterChain(have)
if want.Type != "" && want.Type != haveType {
continue
}
if want.Port != 0 && want.Port != have.GetFilterChainMatch().GetDestinationPort().GetValue() {
continue
}
found++
VerifyFilterChain(t, have, want)
}
if found == 0 {
t.Fatalf("No matching chain found for %+v", want)
}
if found > 1 {
t.Logf("warning: multiple matching chains found for %+v", want)
}
}
}
}
}
func VerifyFilterChain(t test.Failer, have *listener.FilterChain, want FilterChainTest) {
t.Helper()
haveType := classifyFilterChain(have)
context := func(s string) string {
return fmt.Sprintf("%v/%v: %v", have.Name, haveType, s)
}
if want.Name != "" {
assert.Equal(t, want.Name, have.Name, context("name should be equal"))
}
if want.Type != "" {
assert.Equal(t, want.Type, haveType, context("type should be equal"))
}
if want.Port != 0 {
assert.Equal(t, want.Port, have.GetFilterChainMatch().GetDestinationPort().GetValue(), context("port should be equal"))
}
haveNetwork, haveHTTP := xdstest.ExtractFilterNames(t, have)
if want.TotalMatch {
if want.NetworkFilters != nil {
assert.Equal(t, want.NetworkFilters, haveNetwork, context("network filters should be equal"))
}
if want.HTTPFilters != nil {
assert.Equal(t, want.HTTPFilters, haveHTTP, context("http should be equal"))
}
} else {
if missing := sets.New(want.NetworkFilters...).Difference(sets.New(haveNetwork...)).SortedList(); len(missing) > 0 {
t.Fatalf("%v/%v: missing network filters: %v, have %v", have.Name, haveType, missing, haveNetwork)
}
if missing := sets.New(want.HTTPFilters...).Difference(sets.New(haveHTTP...)).SortedList(); len(missing) > 0 {
t.Fatalf("%v/%v: missing network filters: %v, have %v", have.Name, haveType, missing, haveHTTP)
}
}
if want.ValidateHCM != nil {
want.ValidateHCM(t, xdstest.ExtractHTTPConnectionManager(t, have))
}
}