blob: 5f248b5d138213aad1787009ea6b8cb6dec6e799 [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 xdstest
import (
"fmt"
"reflect"
"sort"
)
import (
cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
endpoint "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"
listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
tcpproxy "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/tcp_proxy/v3"
tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
"github.com/envoyproxy/go-control-plane/pkg/wellknown"
"google.golang.org/protobuf/proto"
any "google.golang.org/protobuf/types/known/anypb"
)
import (
"github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util"
v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3"
"github.com/apache/dubbo-go-pixiu/pkg/test"
"github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal"
"github.com/apache/dubbo-go-pixiu/pkg/util/sets"
)
func ExtractRoutesFromListeners(ll []*listener.Listener) []string {
routes := []string{}
for _, l := range ll {
for _, fc := range l.FilterChains {
for _, filter := range fc.Filters {
if filter.Name == wellknown.HTTPConnectionManager {
hcon := &hcm.HttpConnectionManager{}
if err := filter.GetTypedConfig().UnmarshalTo(hcon); err != nil {
continue
}
switch r := hcon.GetRouteSpecifier().(type) {
case *hcm.HttpConnectionManager_Rds:
routes = append(routes, r.Rds.RouteConfigName)
}
}
}
}
}
return routes
}
// ExtractSecretResources fetches all referenced SDS resource names from a list of clusters and listeners
func ExtractSecretResources(t test.Failer, rs []*any.Any) []string {
resourceNames := sets.New()
for _, r := range rs {
switch r.TypeUrl {
case v3.ClusterType:
c := &cluster.Cluster{}
if err := r.UnmarshalTo(c); err != nil {
t.Fatal(err)
}
sockets := []*core.TransportSocket{}
if c.TransportSocket != nil {
sockets = append(sockets, c.TransportSocket)
}
for _, ts := range c.TransportSocketMatches {
sockets = append(sockets, ts.TransportSocket)
}
for _, s := range sockets {
tl := &tls.UpstreamTlsContext{}
if err := s.GetTypedConfig().UnmarshalTo(tl); err != nil {
t.Fatal(err)
}
resourceNames.Insert(tl.GetCommonTlsContext().GetCombinedValidationContext().GetValidationContextSdsSecretConfig().GetName())
for _, s := range tl.GetCommonTlsContext().GetTlsCertificateSdsSecretConfigs() {
resourceNames.Insert(s.GetName())
}
}
case v3.ListenerType:
l := &listener.Listener{}
if err := r.UnmarshalTo(l); err != nil {
t.Fatal(err)
}
sockets := []*core.TransportSocket{}
for _, fc := range l.GetFilterChains() {
if fc.GetTransportSocket() != nil {
sockets = append(sockets, fc.GetTransportSocket())
}
}
if ts := l.GetDefaultFilterChain().GetTransportSocket(); ts != nil {
sockets = append(sockets, ts)
}
for _, s := range sockets {
tl := &tls.DownstreamTlsContext{}
if err := s.GetTypedConfig().UnmarshalTo(tl); err != nil {
t.Fatal(err)
}
resourceNames.Insert(tl.GetCommonTlsContext().GetCombinedValidationContext().GetValidationContextSdsSecretConfig().GetName())
for _, s := range tl.GetCommonTlsContext().GetTlsCertificateSdsSecretConfigs() {
resourceNames.Insert(s.GetName())
}
}
}
}
resourceNames.Delete("")
ls := resourceNames.UnsortedList()
sort.Sort(sort.Reverse(sort.StringSlice(ls)))
return ls
}
func ExtractListenerNames(ll []*listener.Listener) []string {
res := []string{}
for _, l := range ll {
res = append(res, l.Name)
}
return res
}
func ExtractListener(name string, ll []*listener.Listener) *listener.Listener {
for _, l := range ll {
if l.Name == name {
return l
}
}
return nil
}
func ExtractVirtualHosts(rc *route.RouteConfiguration) map[string][]string {
res := map[string][]string{}
for _, vh := range rc.GetVirtualHosts() {
var dests []string
for _, r := range vh.Routes {
if dc := r.GetRoute().GetCluster(); dc != "" {
dests = append(dests, dc)
}
}
sort.Strings(dests)
for _, d := range vh.Domains {
res[d] = dests
}
}
return res
}
func ExtractRouteConfigurations(rc []*route.RouteConfiguration) map[string]*route.RouteConfiguration {
res := map[string]*route.RouteConfiguration{}
for _, l := range rc {
res[l.Name] = l
}
return res
}
func ExtractListenerFilters(l *listener.Listener) map[string]*listener.ListenerFilter {
res := map[string]*listener.ListenerFilter{}
for _, lf := range l.ListenerFilters {
res[lf.Name] = lf
}
return res
}
func ExtractFilterChain(name string, l *listener.Listener) *listener.FilterChain {
for _, f := range l.GetFilterChains() {
if f.GetName() == name {
return f
}
}
return nil
}
func ExtractFilterChainNames(l *listener.Listener) []string {
res := []string{}
for _, f := range l.GetFilterChains() {
res = append(res, f.GetName())
}
return res
}
func ExtractFilterNames(t test.Failer, fcs *listener.FilterChain) ([]string, []string) {
nwFilters := []string{}
httpFilters := []string{}
for _, fc := range fcs.Filters {
if fc.Name == wellknown.HTTPConnectionManager {
h := &hcm.HttpConnectionManager{}
if fc.GetTypedConfig() != nil {
if err := fc.GetTypedConfig().UnmarshalTo(h); err != nil {
t.Fatalf("failed to unmarshal hcm: %v", err)
}
}
for _, hf := range h.HttpFilters {
httpFilters = append(httpFilters, hf.Name)
}
}
nwFilters = append(nwFilters, fc.Name)
}
return nwFilters, httpFilters
}
func ExtractTCPProxy(t test.Failer, fcs *listener.FilterChain) *tcpproxy.TcpProxy {
for _, fc := range fcs.Filters {
if fc.Name == wellknown.TCPProxy {
tcpProxy := &tcpproxy.TcpProxy{}
if fc.GetTypedConfig() != nil {
if err := fc.GetTypedConfig().UnmarshalTo(tcpProxy); err != nil {
t.Fatalf("failed to unmarshal tcp proxy: %v", err)
}
}
return tcpProxy
}
}
return nil
}
func ExtractHTTPConnectionManager(t test.Failer, fcs *listener.FilterChain) *hcm.HttpConnectionManager {
for _, fc := range fcs.Filters {
if fc.Name == wellknown.HTTPConnectionManager {
h := &hcm.HttpConnectionManager{}
if fc.GetTypedConfig() != nil {
if err := fc.GetTypedConfig().UnmarshalTo(h); err != nil {
t.Fatalf("failed to unmarshal hcm: %v", err)
}
}
return h
}
}
return nil
}
func ExtractLoadAssignments(cla []*endpoint.ClusterLoadAssignment) map[string][]string {
got := map[string][]string{}
for _, cla := range cla {
if cla == nil {
continue
}
got[cla.ClusterName] = append(got[cla.ClusterName], ExtractEndpoints(cla)...)
}
return got
}
// ExtractHealthEndpoints returns all health and unhealth endpoints
func ExtractHealthEndpoints(cla *endpoint.ClusterLoadAssignment) ([]string, []string) {
if cla == nil {
return nil, nil
}
healthy := []string{}
unhealthy := []string{}
for _, ep := range cla.Endpoints {
for _, lb := range ep.LbEndpoints {
if lb.HealthStatus == core.HealthStatus_HEALTHY {
if lb.GetEndpoint().Address.GetSocketAddress() != nil {
healthy = append(healthy, fmt.Sprintf("%s:%d",
lb.GetEndpoint().Address.GetSocketAddress().Address, lb.GetEndpoint().Address.GetSocketAddress().GetPortValue()))
} else {
healthy = append(healthy, lb.GetEndpoint().Address.GetPipe().Path)
}
} else {
if lb.GetEndpoint().Address.GetSocketAddress() != nil {
unhealthy = append(unhealthy, fmt.Sprintf("%s:%d",
lb.GetEndpoint().Address.GetSocketAddress().Address, lb.GetEndpoint().Address.GetSocketAddress().GetPortValue()))
} else {
unhealthy = append(unhealthy, lb.GetEndpoint().Address.GetPipe().Path)
}
}
}
}
return healthy, unhealthy
}
// ExtractEndpoints returns all endpoints in the load assignment (including unhealthy endpoints)
func ExtractEndpoints(cla *endpoint.ClusterLoadAssignment) []string {
h, uh := ExtractHealthEndpoints(cla)
h = append(h, uh...)
return h
}
func ExtractClusters(cc []*cluster.Cluster) map[string]*cluster.Cluster {
res := map[string]*cluster.Cluster{}
for _, c := range cc {
res[c.Name] = c
}
return res
}
func ExtractCluster(name string, cc []*cluster.Cluster) *cluster.Cluster {
return ExtractClusters(cc)[name]
}
func ExtractClusterEndpoints(clusters []*cluster.Cluster) map[string][]string {
cla := []*endpoint.ClusterLoadAssignment{}
for _, c := range clusters {
cla = append(cla, c.LoadAssignment)
}
return ExtractLoadAssignments(cla)
}
func ExtractEdsClusterNames(cl []*cluster.Cluster) []string {
res := []string{}
for _, c := range cl {
switch v := c.ClusterDiscoveryType.(type) {
case *cluster.Cluster_Type:
if v.Type != cluster.Cluster_EDS {
continue
}
}
res = append(res, c.Name)
}
return res
}
func ExtractTLSSecrets(t test.Failer, secrets []*any.Any) map[string]*tls.Secret {
res := map[string]*tls.Secret{}
for _, a := range secrets {
scrt := &tls.Secret{}
if err := a.UnmarshalTo(scrt); err != nil {
t.Fatal(err)
}
res[scrt.Name] = scrt
}
return res
}
func UnmarshalRouteConfiguration(t test.Failer, resp []*any.Any) []*route.RouteConfiguration {
un := make([]*route.RouteConfiguration, 0, len(resp))
for _, r := range resp {
u := &route.RouteConfiguration{}
if err := r.UnmarshalTo(u); err != nil {
t.Fatal(err)
}
un = append(un, u)
}
return un
}
func UnmarshalClusterLoadAssignment(t test.Failer, resp []*any.Any) []*endpoint.ClusterLoadAssignment {
un := make([]*endpoint.ClusterLoadAssignment, 0, len(resp))
for _, r := range resp {
u := &endpoint.ClusterLoadAssignment{}
if err := r.UnmarshalTo(u); err != nil {
t.Fatal(err)
}
un = append(un, u)
}
return un
}
func FilterClusters(cl []*cluster.Cluster, f func(c *cluster.Cluster) bool) []*cluster.Cluster {
res := make([]*cluster.Cluster, 0, len(cl))
for _, c := range cl {
if f(c) {
res = append(res, c)
}
}
return res
}
func ToDiscoveryResponse(p interface{}) *discovery.DiscoveryResponse {
slice := InterfaceSlice(p)
if len(slice) == 0 {
return &discovery.DiscoveryResponse{}
}
resources := make([]*any.Any, 0, len(slice))
for _, v := range slice {
resources = append(resources, util.MessageToAny(v.(proto.Message)))
}
return &discovery.DiscoveryResponse{
Resources: resources,
TypeUrl: resources[0].TypeUrl,
}
}
func InterfaceSlice(slice interface{}) []interface{} {
s := reflect.ValueOf(slice)
if s.Kind() != reflect.Slice {
panic("InterfaceSlice() given a non-slice type")
}
ret := make([]interface{}, s.Len())
for i := 0; i < s.Len(); i++ {
ret[i] = s.Index(i).Interface()
}
return ret
}
// DumpList will dump a list of protos. To workaround go type issues, call DumpList(t, InterfaceSlice([]proto.Message))
func DumpList(t test.Failer, protoList []interface{}) []string {
res := []string{}
for _, i := range protoList {
p, ok := i.(proto.Message)
if !ok {
t.Fatalf("expected proto, got %T", i)
}
res = append(res, Dump(t, p))
}
return res
}
func Dump(t test.Failer, p proto.Message) string {
v := reflect.ValueOf(p)
if p == nil || (v.Kind() == reflect.Ptr && v.IsNil()) {
return "nil"
}
s, err := protomarshal.ToJSONWithIndent(p, " ")
if err != nil {
t.Fatal(err)
}
return s
}
func MapKeys(mp interface{}) []string {
keys := reflect.ValueOf(mp).MapKeys()
res := []string{}
for _, k := range keys {
res = append(res, k.String())
}
sort.Strings(res)
return res
}