blob: cd272d185fa42fe92eec7c2b4732fd5641316c3d [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 model
import (
"fmt"
"testing"
"time"
)
import (
"github.com/golang/protobuf/ptypes/wrappers"
fuzz "github.com/google/gofuzz"
"google.golang.org/protobuf/types/known/durationpb"
networking "istio.io/api/networking/v1alpha3"
)
import (
"github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider"
"github.com/apache/dubbo-go-pixiu/pkg/config"
"github.com/apache/dubbo-go-pixiu/pkg/config/host"
"github.com/apache/dubbo-go-pixiu/pkg/config/protocol"
"github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections"
"github.com/apache/dubbo-go-pixiu/pkg/config/visibility"
"github.com/apache/dubbo-go-pixiu/pkg/test/util/assert"
)
const wildcardIP = "0.0.0.0"
func TestMergeVirtualServices(t *testing.T) {
independentVs := config.Config{
Meta: config.Meta{
GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(),
Name: "virtual-service",
Namespace: "default",
},
Spec: &networking.VirtualService{
Hosts: []string{"example.org"},
Gateways: []string{"gateway"},
Http: []*networking.HTTPRoute{
{
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "example.org",
Port: &networking.PortSelector{
Number: 80,
},
},
},
},
},
},
},
}
rootVs := config.Config{
Meta: config.Meta{
GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(),
Name: "root-vs",
Namespace: "dubbo-system",
},
Spec: &networking.VirtualService{
Hosts: []string{"*.org"},
Gateways: []string{"gateway"},
Http: []*networking.HTTPRoute{
{
Match: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"},
},
},
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Exact{Exact: "/login"},
},
},
},
Delegate: &networking.Delegate{
Name: "productpage-vs",
Namespace: "default",
},
},
{
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "example.org",
Port: &networking.PortSelector{
Number: 80,
},
},
},
},
},
},
},
}
defaultVs := config.Config{
Meta: config.Meta{
GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(),
Name: "default-vs",
Namespace: "default",
},
Spec: &networking.VirtualService{
Hosts: []string{"*.org"},
Gateways: []string{"gateway"},
Http: []*networking.HTTPRoute{
{
Match: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"},
},
},
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Exact{Exact: "/login"},
},
},
},
Delegate: &networking.Delegate{
Name: "productpage-vs",
},
},
{
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "example.org",
Port: &networking.PortSelector{
Number: 80,
},
},
},
},
},
},
},
}
oneRoot := config.Config{
Meta: config.Meta{
GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(),
Name: "root-vs",
Namespace: "dubbo-system",
},
Spec: &networking.VirtualService{
Hosts: []string{"*.org"},
Gateways: []string{"gateway"},
Http: []*networking.HTTPRoute{
{
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "example.org",
Port: &networking.PortSelector{
Number: 80,
},
},
},
},
},
},
},
}
createDelegateVs := func(name, namespace string, exportTo []string) config.Config {
return config.Config{
Meta: config.Meta{
GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(),
Name: name,
Namespace: namespace,
},
Spec: &networking.VirtualService{
Hosts: []string{},
Gateways: []string{"gateway"},
ExportTo: exportTo,
Http: []*networking.HTTPRoute{
{
Match: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v1"},
},
},
},
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "productpage.org",
Port: &networking.PortSelector{
Number: 80,
},
Subset: "v1",
},
},
},
},
{
Match: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v2"},
},
},
},
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "productpage.org",
Port: &networking.PortSelector{
Number: 80,
},
Subset: "v2",
},
},
},
},
{
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "productpage.org",
Port: &networking.PortSelector{
Number: 80,
},
Subset: "v3",
},
},
},
},
},
},
}
}
delegateVs := createDelegateVs("productpage-vs", "default", []string{"dubbo-system"})
delegateVsExportedToAll := createDelegateVs("productpage-vs", "default", []string{})
delegateVsNotExported := config.Config{
Meta: config.Meta{
GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(),
Name: "productpage-vs",
Namespace: "default2",
},
Spec: &networking.VirtualService{
Hosts: []string{},
Gateways: []string{"gateway"},
ExportTo: []string{"."},
},
}
mergedVs := config.Config{
Meta: config.Meta{
GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(),
Name: "root-vs",
Namespace: "dubbo-system",
},
Spec: &networking.VirtualService{
Hosts: []string{"*.org"},
Gateways: []string{"gateway"},
Http: []*networking.HTTPRoute{
{
Match: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v1"},
},
},
},
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "productpage.org",
Port: &networking.PortSelector{
Number: 80,
},
Subset: "v1",
},
},
},
},
{
Match: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v2"},
},
},
},
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "productpage.org",
Port: &networking.PortSelector{
Number: 80,
},
Subset: "v2",
},
},
},
},
{
Match: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"},
},
},
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Exact{Exact: "/login"},
},
},
},
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "productpage.org",
Port: &networking.PortSelector{
Number: 80,
},
Subset: "v3",
},
},
},
},
{
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "example.org",
Port: &networking.PortSelector{
Number: 80,
},
},
},
},
},
},
},
}
mergedVsInDefault := mergedVs.DeepCopy()
mergedVsInDefault.Name = "default-vs"
mergedVsInDefault.Namespace = "default"
// invalid delegate, match condition conflicts with root
delegateVs2 := config.Config{
Meta: config.Meta{
GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(),
Name: "productpage-vs",
Namespace: "default",
},
Spec: &networking.VirtualService{
Hosts: []string{},
Gateways: []string{"gateway"},
Http: []*networking.HTTPRoute{
{
Match: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v1"},
},
},
},
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "productpage.org",
Port: &networking.PortSelector{
Number: 80,
},
Subset: "v1",
},
},
},
},
{
Match: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v2"},
},
},
},
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "productpage.org",
Port: &networking.PortSelector{
Number: 80,
},
Subset: "v2",
},
},
},
},
{
// mismatch, this route will be ignored
Match: []*networking.HTTPMatchRequest{
{
Name: "mismatch",
Uri: &networking.StringMatch{
// conflicts with root's HTTPMatchRequest
MatchType: &networking.StringMatch_Prefix{Prefix: "/mis-match/path"},
},
},
},
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "productpage.org",
Port: &networking.PortSelector{
Number: 80,
},
Subset: "v3",
},
},
},
},
},
},
}
mergedVs2 := config.Config{
Meta: config.Meta{
GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(),
Name: "root-vs",
Namespace: "dubbo-system",
},
Spec: &networking.VirtualService{
Hosts: []string{"*.org"},
Gateways: []string{"gateway"},
Http: []*networking.HTTPRoute{
{
Match: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v1"},
},
},
},
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "productpage.org",
Port: &networking.PortSelector{
Number: 80,
},
Subset: "v1",
},
},
},
},
{
Match: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v2"},
},
},
},
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "productpage.org",
Port: &networking.PortSelector{
Number: 80,
},
Subset: "v2",
},
},
},
},
{
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "example.org",
Port: &networking.PortSelector{
Number: 80,
},
},
},
},
},
},
},
}
// multiple routes delegate to one single sub VS
multiRoutes := config.Config{
Meta: config.Meta{
GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(),
Name: "root-vs",
Namespace: "dubbo-system",
},
Spec: &networking.VirtualService{
Hosts: []string{"*.org"},
Gateways: []string{"gateway"},
Http: []*networking.HTTPRoute{
{
Match: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"},
},
},
},
Delegate: &networking.Delegate{
Name: "productpage-vs",
Namespace: "default",
},
},
{
Match: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/legacy/path"},
},
},
},
Rewrite: &networking.HTTPRewrite{
Uri: "/productpage",
},
Delegate: &networking.Delegate{
Name: "productpage-vs",
Namespace: "default",
},
},
},
},
}
singleDelegate := config.Config{
Meta: config.Meta{
GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(),
Name: "productpage-vs",
Namespace: "default",
},
Spec: &networking.VirtualService{
Hosts: []string{},
Gateways: []string{"gateway"},
Http: []*networking.HTTPRoute{
{
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "productpage.org",
Port: &networking.PortSelector{
Number: 80,
},
Subset: "v1",
},
},
},
},
},
},
}
mergedVs3 := config.Config{
Meta: config.Meta{
GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(),
Name: "root-vs",
Namespace: "dubbo-system",
},
Spec: &networking.VirtualService{
Hosts: []string{"*.org"},
Gateways: []string{"gateway"},
Http: []*networking.HTTPRoute{
{
Match: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"},
},
},
},
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "productpage.org",
Port: &networking.PortSelector{
Number: 80,
},
Subset: "v1",
},
},
},
},
{
Match: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/legacy/path"},
},
},
},
Rewrite: &networking.HTTPRewrite{
Uri: "/productpage",
},
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "productpage.org",
Port: &networking.PortSelector{
Number: 80,
},
Subset: "v1",
},
},
},
},
},
},
}
cases := []struct {
name string
virtualServices []config.Config
expectedVirtualServices []config.Config
}{
{
name: "one independent vs",
virtualServices: []config.Config{independentVs},
expectedVirtualServices: []config.Config{independentVs},
},
{
name: "one root vs",
virtualServices: []config.Config{rootVs},
expectedVirtualServices: []config.Config{oneRoot},
},
{
name: "one delegate vs",
virtualServices: []config.Config{delegateVs},
expectedVirtualServices: []config.Config{},
},
{
name: "root and delegate vs",
virtualServices: []config.Config{rootVs.DeepCopy(), delegateVs},
expectedVirtualServices: []config.Config{mergedVs},
},
{
name: "root and conflicted delegate vs",
virtualServices: []config.Config{rootVs.DeepCopy(), delegateVs2},
expectedVirtualServices: []config.Config{mergedVs2},
},
{
name: "multiple routes delegate to one",
virtualServices: []config.Config{multiRoutes.DeepCopy(), singleDelegate},
expectedVirtualServices: []config.Config{mergedVs3},
},
{
name: "root not specify delegate namespace",
virtualServices: []config.Config{defaultVs.DeepCopy(), delegateVsExportedToAll},
expectedVirtualServices: []config.Config{mergedVsInDefault},
},
{
name: "delegate not exported to root vs namespace",
virtualServices: []config.Config{rootVs, delegateVsNotExported},
expectedVirtualServices: []config.Config{oneRoot},
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got, _ := mergeVirtualServicesIfNeeded(tc.virtualServices, map[visibility.Instance]bool{visibility.Public: true})
assert.Equal(t, got, tc.expectedVirtualServices)
})
}
}
func TestMergeHttpRoutes(t *testing.T) {
cases := []struct {
name string
root *networking.HTTPRoute
delegate []*networking.HTTPRoute
expected []*networking.HTTPRoute
}{
{
name: "root catch all",
root: &networking.HTTPRoute{
Match: nil, // catch all
Timeout: &durationpb.Duration{Seconds: 10},
Headers: &networking.Headers{
Request: &networking.Headers_HeaderOperations{
Add: map[string]string{
"istio": "test",
},
Remove: []string{"trace-id"},
},
},
Delegate: &networking.Delegate{
Name: "delegate",
Namespace: "default",
},
},
delegate: []*networking.HTTPRoute{
{
Match: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v1"},
},
Headers: map[string]*networking.StringMatch{
"version": {
MatchType: &networking.StringMatch_Exact{Exact: "v1"},
},
},
},
},
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "productpage.org",
Port: &networking.PortSelector{
Number: 80,
},
Subset: "v1",
},
},
},
},
{
Match: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v2"},
},
Headers: map[string]*networking.StringMatch{
"version": {
MatchType: &networking.StringMatch_Exact{Exact: "v2"},
},
},
},
{
Headers: map[string]*networking.StringMatch{
"version": {
MatchType: &networking.StringMatch_Exact{Exact: "v2"},
},
},
QueryParams: map[string]*networking.StringMatch{
"version": {
MatchType: &networking.StringMatch_Exact{Exact: "v2"},
},
},
},
},
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "productpage.org",
Port: &networking.PortSelector{
Number: 80,
},
Subset: "v2",
},
},
},
},
},
expected: []*networking.HTTPRoute{
{
Match: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v1"},
},
Headers: map[string]*networking.StringMatch{
"version": {
MatchType: &networking.StringMatch_Exact{Exact: "v1"},
},
},
},
},
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "productpage.org",
Port: &networking.PortSelector{
Number: 80,
},
Subset: "v1",
},
},
},
Timeout: &durationpb.Duration{Seconds: 10},
Headers: &networking.Headers{
Request: &networking.Headers_HeaderOperations{
Add: map[string]string{
"istio": "test",
},
Remove: []string{"trace-id"},
},
},
},
{
Match: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v2"},
},
Headers: map[string]*networking.StringMatch{
"version": {
MatchType: &networking.StringMatch_Exact{Exact: "v2"},
},
},
},
{
Headers: map[string]*networking.StringMatch{
"version": {
MatchType: &networking.StringMatch_Exact{Exact: "v2"},
},
},
QueryParams: map[string]*networking.StringMatch{
"version": {
MatchType: &networking.StringMatch_Exact{Exact: "v2"},
},
},
},
},
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "productpage.org",
Port: &networking.PortSelector{
Number: 80,
},
Subset: "v2",
},
},
},
Timeout: &durationpb.Duration{Seconds: 10},
Headers: &networking.Headers{
Request: &networking.Headers_HeaderOperations{
Add: map[string]string{
"istio": "test",
},
Remove: []string{"trace-id"},
},
},
},
},
},
{
name: "delegate with empty match",
root: &networking.HTTPRoute{
Match: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"},
},
Port: 8080,
},
},
Delegate: &networking.Delegate{
Name: "delegate",
Namespace: "default",
},
},
delegate: []*networking.HTTPRoute{
{
Match: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v1"},
},
Headers: map[string]*networking.StringMatch{
"version": {
MatchType: &networking.StringMatch_Exact{Exact: "v1"},
},
},
},
},
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "productpage.org",
Port: &networking.PortSelector{
Number: 80,
},
Subset: "v1",
},
},
},
},
{
Match: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v2"},
},
Headers: map[string]*networking.StringMatch{
"version": {
MatchType: &networking.StringMatch_Exact{Exact: "v2"},
},
},
},
{
Headers: map[string]*networking.StringMatch{
"version": {
MatchType: &networking.StringMatch_Exact{Exact: "v2"},
},
},
QueryParams: map[string]*networking.StringMatch{
"version": {
MatchType: &networking.StringMatch_Exact{Exact: "v2"},
},
},
},
},
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "productpage.org",
Port: &networking.PortSelector{
Number: 80,
},
Subset: "v2",
},
},
},
},
{
// default route to v3
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "productpage.org",
Port: &networking.PortSelector{
Number: 80,
},
Subset: "v3",
},
},
},
},
},
expected: []*networking.HTTPRoute{
{
Match: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v1"},
},
Headers: map[string]*networking.StringMatch{
"version": {
MatchType: &networking.StringMatch_Exact{Exact: "v1"},
},
},
Port: 8080,
},
},
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "productpage.org",
Port: &networking.PortSelector{
Number: 80,
},
Subset: "v1",
},
},
},
},
{
Match: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v2"},
},
Headers: map[string]*networking.StringMatch{
"version": {
MatchType: &networking.StringMatch_Exact{Exact: "v2"},
},
},
Port: 8080,
},
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"},
},
Headers: map[string]*networking.StringMatch{
"version": {
MatchType: &networking.StringMatch_Exact{Exact: "v2"},
},
},
QueryParams: map[string]*networking.StringMatch{
"version": {
MatchType: &networking.StringMatch_Exact{Exact: "v2"},
},
},
Port: 8080,
},
},
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "productpage.org",
Port: &networking.PortSelector{
Number: 80,
},
Subset: "v2",
},
},
},
},
{
Match: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"},
},
Port: 8080,
},
},
// default route to v3
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "productpage.org",
Port: &networking.PortSelector{
Number: 80,
},
Subset: "v3",
},
},
},
},
},
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got := mergeHTTPRoutes(tc.root, tc.delegate)
assert.Equal(t, got, tc.expected)
})
}
}
func TestMergeHTTPMatchRequests(t *testing.T) {
cases := []struct {
name string
root []*networking.HTTPMatchRequest
delegate []*networking.HTTPMatchRequest
expected []*networking.HTTPMatchRequest
}{
{
name: "url match",
root: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"},
},
},
},
delegate: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v1"},
},
},
},
expected: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v1"},
},
},
},
},
{
name: "multi url match",
root: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"},
},
},
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/reviews"},
},
},
},
delegate: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v1"},
},
},
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Exact{Exact: "/reviews/v1"},
},
},
},
expected: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v1"},
},
},
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Exact{Exact: "/reviews/v1"},
},
},
},
},
{
name: "url mismatch",
root: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"},
},
},
},
delegate: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Exact{Exact: "/reviews"},
},
},
},
expected: nil,
},
{
name: "url match",
root: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"},
},
},
},
delegate: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v1"},
},
},
{
Uri: &networking.StringMatch{
// conflicts
MatchType: &networking.StringMatch_Exact{Exact: "/reviews"},
},
},
},
expected: nil,
},
{
name: "headers",
root: []*networking.HTTPMatchRequest{
{
Headers: map[string]*networking.StringMatch{
"header": {
MatchType: &networking.StringMatch_Exact{Exact: "h1"},
},
},
},
},
delegate: []*networking.HTTPMatchRequest{},
expected: []*networking.HTTPMatchRequest{
{
Headers: map[string]*networking.StringMatch{
"header": {
MatchType: &networking.StringMatch_Exact{Exact: "h1"},
},
},
},
},
},
{
name: "headers",
root: []*networking.HTTPMatchRequest{
{
Headers: map[string]*networking.StringMatch{
"header": {
MatchType: &networking.StringMatch_Exact{Exact: "h1"},
},
},
},
},
delegate: []*networking.HTTPMatchRequest{
{
Headers: map[string]*networking.StringMatch{
"header": {
MatchType: &networking.StringMatch_Exact{Exact: "h2"},
},
},
},
},
expected: nil,
},
{
name: "headers",
root: []*networking.HTTPMatchRequest{
{
Headers: map[string]*networking.StringMatch{
"header": {
MatchType: &networking.StringMatch_Exact{Exact: "h1"},
},
},
},
},
delegate: []*networking.HTTPMatchRequest{
{
Headers: map[string]*networking.StringMatch{
"header-2": {
MatchType: &networking.StringMatch_Exact{Exact: "h2"},
},
},
},
},
expected: []*networking.HTTPMatchRequest{
{
Headers: map[string]*networking.StringMatch{
"header": {
MatchType: &networking.StringMatch_Exact{Exact: "h1"},
},
"header-2": {
MatchType: &networking.StringMatch_Exact{Exact: "h2"},
},
},
},
},
},
{
name: "complicated merge",
root: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"},
},
Headers: map[string]*networking.StringMatch{
"header": {
MatchType: &networking.StringMatch_Exact{Exact: "h1"},
},
},
Port: 8080,
Authority: &networking.StringMatch{
MatchType: &networking.StringMatch_Exact{Exact: "productpage.com"},
},
},
},
delegate: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v1"},
},
},
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v2"},
},
},
},
expected: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v1"},
},
Headers: map[string]*networking.StringMatch{
"header": {
MatchType: &networking.StringMatch_Exact{Exact: "h1"},
},
},
Port: 8080,
Authority: &networking.StringMatch{
MatchType: &networking.StringMatch_Exact{Exact: "productpage.com"},
},
},
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v2"},
},
Headers: map[string]*networking.StringMatch{
"header": {
MatchType: &networking.StringMatch_Exact{Exact: "h1"},
},
},
Port: 8080,
Authority: &networking.StringMatch{
MatchType: &networking.StringMatch_Exact{Exact: "productpage.com"},
},
},
},
},
{
name: "conflicted merge",
root: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"},
},
Headers: map[string]*networking.StringMatch{
"header": {
MatchType: &networking.StringMatch_Exact{Exact: "h1"},
},
},
Port: 8080,
Authority: &networking.StringMatch{
MatchType: &networking.StringMatch_Exact{Exact: "productpage.com"},
},
},
},
delegate: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v1"},
},
},
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v2"},
},
Port: 9090, // conflicts
},
},
expected: nil,
},
{
name: "gateway merge",
root: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"},
},
Gateways: []string{"ingress-gateway", "mesh"},
},
},
delegate: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v1"},
},
Gateways: []string{"ingress-gateway"},
},
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v2"},
},
Gateways: []string{"mesh"},
},
},
expected: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v1"},
},
Gateways: []string{"ingress-gateway"},
},
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v2"},
},
Gateways: []string{"mesh"},
},
},
},
{
name: "gateway conflicted merge",
root: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"},
},
Gateways: []string{"ingress-gateway"},
},
},
delegate: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v1"},
},
Gateways: []string{"ingress-gateway"},
},
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v2"},
},
Gateways: []string{"mesh"},
},
},
expected: nil,
},
{
name: "source labels merge",
root: []*networking.HTTPMatchRequest{
{
SourceLabels: map[string]string{"app": "test"},
},
},
delegate: []*networking.HTTPMatchRequest{
{
SourceLabels: map[string]string{"version": "v1"},
},
},
expected: []*networking.HTTPMatchRequest{
{
SourceLabels: map[string]string{"app": "test", "version": "v1"},
},
},
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got, _ := mergeHTTPMatchRequests(tc.root, tc.delegate)
assert.Equal(t, got, tc.expected)
})
}
}
func TestHasConflict(t *testing.T) {
cases := []struct {
name string
root *networking.HTTPMatchRequest
leaf *networking.HTTPMatchRequest
expected bool
}{
{
name: "regex uri",
root: &networking.HTTPMatchRequest{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Regex{Regex: "^/productpage"},
},
},
leaf: &networking.HTTPMatchRequest{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v2"},
},
},
expected: true,
},
{
name: "match uri",
root: &networking.HTTPMatchRequest{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"},
},
},
leaf: &networking.HTTPMatchRequest{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v2"},
},
},
expected: false,
},
{
name: "mismatch uri",
root: &networking.HTTPMatchRequest{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v2"},
},
},
leaf: &networking.HTTPMatchRequest{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"},
},
},
expected: true,
},
{
name: "match uri",
root: &networking.HTTPMatchRequest{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v2"},
},
},
leaf: &networking.HTTPMatchRequest{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v2"},
},
},
expected: false,
},
{
name: "headers not equal",
root: &networking.HTTPMatchRequest{
Headers: map[string]*networking.StringMatch{
"header": {
MatchType: &networking.StringMatch_Exact{Exact: "h1"},
},
},
},
leaf: &networking.HTTPMatchRequest{
Headers: map[string]*networking.StringMatch{
"header": {
MatchType: &networking.StringMatch_Exact{Exact: "h2"},
},
},
},
expected: true,
},
{
name: "headers equal",
root: &networking.HTTPMatchRequest{
Headers: map[string]*networking.StringMatch{
"header": {
MatchType: &networking.StringMatch_Exact{Exact: "h1"},
},
},
},
leaf: &networking.HTTPMatchRequest{
Headers: map[string]*networking.StringMatch{
"header": {
MatchType: &networking.StringMatch_Exact{Exact: "h1"},
},
},
},
expected: false,
},
{
name: "headers match",
root: &networking.HTTPMatchRequest{
Headers: map[string]*networking.StringMatch{
"header": {
MatchType: &networking.StringMatch_Prefix{Prefix: "h1"},
},
},
},
leaf: &networking.HTTPMatchRequest{
Headers: map[string]*networking.StringMatch{
"header": {
MatchType: &networking.StringMatch_Exact{Exact: "h1-v1"},
},
},
},
expected: false,
},
{
name: "headers mismatch",
root: &networking.HTTPMatchRequest{
Headers: map[string]*networking.StringMatch{
"header": {
MatchType: &networking.StringMatch_Prefix{Prefix: "h1"},
},
},
},
leaf: &networking.HTTPMatchRequest{
Headers: map[string]*networking.StringMatch{
"header": {
MatchType: &networking.StringMatch_Exact{Exact: "h2"},
},
},
},
expected: true,
},
{
name: "headers prefix mismatch",
root: &networking.HTTPMatchRequest{
Headers: map[string]*networking.StringMatch{
"header": {
MatchType: &networking.StringMatch_Prefix{Prefix: "h1"},
},
},
},
leaf: &networking.HTTPMatchRequest{
Headers: map[string]*networking.StringMatch{
"header": {
MatchType: &networking.StringMatch_Prefix{Prefix: "h2"},
},
},
},
expected: true,
},
{
name: "diff headers",
root: &networking.HTTPMatchRequest{
Headers: map[string]*networking.StringMatch{
"header": {
MatchType: &networking.StringMatch_Exact{Exact: "h1"},
},
},
},
leaf: &networking.HTTPMatchRequest{
Headers: map[string]*networking.StringMatch{
"header-2": {
MatchType: &networking.StringMatch_Exact{Exact: "h2"},
},
},
},
expected: false,
},
{
name: "withoutHeaders mismatch",
root: &networking.HTTPMatchRequest{
WithoutHeaders: map[string]*networking.StringMatch{
"header": {
MatchType: &networking.StringMatch_Prefix{Prefix: "h1"},
},
},
},
leaf: &networking.HTTPMatchRequest{
WithoutHeaders: map[string]*networking.StringMatch{
"header": {
MatchType: &networking.StringMatch_Exact{Exact: "h2"},
},
},
},
expected: true,
},
{
name: "port",
root: &networking.HTTPMatchRequest{
Port: 0,
},
leaf: &networking.HTTPMatchRequest{
Port: 8080,
},
expected: false,
},
{
name: "port",
root: &networking.HTTPMatchRequest{
Port: 8080,
},
leaf: &networking.HTTPMatchRequest{
Port: 0,
},
expected: false,
},
{
name: "port",
root: &networking.HTTPMatchRequest{
Port: 8080,
},
leaf: &networking.HTTPMatchRequest{
Port: 8090,
},
expected: true,
},
{
name: "sourceLabels mismatch",
root: &networking.HTTPMatchRequest{
SourceLabels: map[string]string{"a": "b"},
},
leaf: &networking.HTTPMatchRequest{
SourceLabels: map[string]string{"a": "c"},
},
expected: true,
},
{
name: "sourceNamespace mismatch",
root: &networking.HTTPMatchRequest{
SourceNamespace: "test1",
},
leaf: &networking.HTTPMatchRequest{
SourceNamespace: "test2",
},
expected: true,
},
{
name: "root has less gateways than delegate",
root: &networking.HTTPMatchRequest{
Gateways: []string{"ingress-gateway"},
},
leaf: &networking.HTTPMatchRequest{
Gateways: []string{"ingress-gateway", "mesh"},
},
expected: true,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got := hasConflict(tc.root, tc.leaf)
if got != tc.expected {
t.Errorf("got %v, expected %v", got, tc.expected)
}
})
}
}
// Note: this is to prevent missing merge new added HTTPRoute fields
func TestFuzzMergeHttpRoute(t *testing.T) {
f := fuzz.New().NilChance(0.5).NumElements(0, 1).Funcs(
func(r *networking.HTTPRoute, c fuzz.Continue) {
c.FuzzNoCustom(r)
r.Match = []*networking.HTTPMatchRequest{{}}
r.Route = nil
r.Redirect = nil
r.Delegate = nil
},
func(r *networking.HTTPMatchRequest, c fuzz.Continue) {
*r = networking.HTTPMatchRequest{}
},
func(r *networking.HTTPRouteDestination, c fuzz.Continue) {
*r = networking.HTTPRouteDestination{}
},
func(r *networking.HTTPRedirect, c fuzz.Continue) {
*r = networking.HTTPRedirect{}
},
func(r *networking.Delegate, c fuzz.Continue) {
*r = networking.Delegate{}
},
func(r *networking.HTTPRewrite, c fuzz.Continue) {
*r = networking.HTTPRewrite{}
},
func(r *durationpb.Duration, c fuzz.Continue) {
*r = durationpb.Duration{}
},
func(r *networking.HTTPRetry, c fuzz.Continue) {
*r = networking.HTTPRetry{}
},
func(r *networking.HTTPFaultInjection, c fuzz.Continue) {
*r = networking.HTTPFaultInjection{}
},
func(r *networking.Destination, c fuzz.Continue) {
*r = networking.Destination{}
},
func(r *wrappers.UInt32Value, c fuzz.Continue) {
*r = wrappers.UInt32Value{}
},
func(r *networking.Percent, c fuzz.Continue) {
*r = networking.Percent{}
},
func(r *networking.CorsPolicy, c fuzz.Continue) {
*r = networking.CorsPolicy{}
},
func(r *networking.Headers, c fuzz.Continue) {
*r = networking.Headers{}
})
root := &networking.HTTPRoute{}
f.Fuzz(root)
delegate := &networking.HTTPRoute{}
expected := mergeHTTPRoute(root, delegate)
assert.Equal(t, expected, root)
}
// Note: this is to prevent missing merge new added HTTPMatchRequest fields
func TestFuzzMergeHttpMatchRequest(t *testing.T) {
f := fuzz.New().NilChance(0.5).NumElements(1, 1).Funcs(
func(r *networking.StringMatch, c fuzz.Continue) {
*r = networking.StringMatch{
MatchType: &networking.StringMatch_Exact{
Exact: "fuzz",
},
}
},
func(m *map[string]*networking.StringMatch, c fuzz.Continue) {
*m = map[string]*networking.StringMatch{
"test": nil,
}
},
func(m *map[string]string, c fuzz.Continue) {
*m = map[string]string{"test": "fuzz"}
})
root := &networking.HTTPMatchRequest{}
f.Fuzz(root)
root.SourceNamespace = ""
root.SourceLabels = nil
root.Gateways = nil
root.IgnoreUriCase = false
delegate := &networking.HTTPMatchRequest{}
merged := mergeHTTPMatchRequest(root, delegate)
assert.Equal(t, merged, root)
}
var gatewayNameTests = []struct {
gateway string
namespace string
resolved string
}{
{
"./gateway",
"default",
"default/gateway",
},
{
"gateway",
"default",
"default/gateway",
},
{
"default/gateway",
"foo",
"default/gateway",
},
{
"gateway.default",
"default",
"default/gateway",
},
{
"gateway.default",
"foo",
"default/gateway",
},
{
"private.ingress.svc.cluster.local",
"foo",
"ingress/private",
},
}
func TestResolveGatewayName(t *testing.T) {
for _, tt := range gatewayNameTests {
t.Run(fmt.Sprintf("%s-%s", tt.gateway, tt.namespace), func(t *testing.T) {
if got := resolveGatewayName(tt.gateway, config.Meta{Namespace: tt.namespace}); got != tt.resolved {
t.Fatalf("expected %q got %q", tt.resolved, got)
}
})
}
}
func BenchmarkResolveGatewayName(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, tt := range gatewayNameTests {
_ = resolveGatewayName(tt.gateway, config.Meta{Namespace: tt.namespace})
}
}
}
func TestSelectVirtualService(t *testing.T) {
services := []*Service{
buildHTTPService("bookinfo.com", visibility.Public, wildcardIP, "default", 9999, 70),
buildHTTPService("private.com", visibility.Private, wildcardIP, "default", 9999, 80),
buildHTTPService("test.com", visibility.Public, "8.8.8.8", "not-default", 8080),
buildHTTPService("test-private.com", visibility.Private, "9.9.9.9", "not-default", 80, 70),
buildHTTPService("test-private-2.com", visibility.Private, "9.9.9.10", "not-default", 60),
buildHTTPService("test-headless.com", visibility.Public, wildcardIP, "not-default", 8888),
buildHTTPService("test-headless-someother.com", visibility.Public, wildcardIP, "some-other-ns", 8888),
}
hostsByNamespace := make(map[string][]host.Name)
for _, svc := range services {
hostsByNamespace[svc.Attributes.Namespace] = append(hostsByNamespace[svc.Attributes.Namespace], svc.Hostname)
}
virtualServiceSpec1 := &networking.VirtualService{
Hosts: []string{"test-private-2.com"},
Gateways: []string{"mesh"},
Http: []*networking.HTTPRoute{
{
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
// Subset: "some-subset",
Host: "example.org",
Port: &networking.PortSelector{
Number: 61,
},
},
Weight: 100,
},
},
},
},
}
virtualServiceSpec2 := &networking.VirtualService{
Hosts: []string{"test-private-2.com"},
Gateways: []string{"mesh"},
Http: []*networking.HTTPRoute{
{
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "test.org",
Port: &networking.PortSelector{
Number: 62,
},
},
Weight: 100,
},
},
},
},
}
virtualServiceSpec3 := &networking.VirtualService{
Hosts: []string{"test-private-3.com"},
Gateways: []string{"mesh"},
Http: []*networking.HTTPRoute{
{
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "test.org",
Port: &networking.PortSelector{
Number: 63,
},
},
Weight: 100,
},
},
},
},
}
virtualServiceSpec4 := &networking.VirtualService{
Hosts: []string{"test-headless.com", "example.com"},
Gateways: []string{"mesh"},
Http: []*networking.HTTPRoute{
{
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "test.org",
Port: &networking.PortSelector{
Number: 64,
},
},
Weight: 100,
},
},
},
},
}
virtualServiceSpec5 := &networking.VirtualService{
Hosts: []string{"test-svc.testns.svc.cluster.local"},
Gateways: []string{"mesh"},
Http: []*networking.HTTPRoute{
{
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "test-svc.testn.svc.cluster.local",
},
Weight: 100,
},
},
},
},
}
virtualServiceSpec6 := &networking.VirtualService{
Hosts: []string{"match-no-service"},
Gateways: []string{"mesh"},
Http: []*networking.HTTPRoute{
{
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "non-exist-service",
},
Weight: 100,
},
},
},
},
}
virtualServiceSpec7 := &networking.VirtualService{
Hosts: []string{"test-headless-someother.com"},
Gateways: []string{"mesh"},
Http: []*networking.HTTPRoute{
{
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "test.org",
Port: &networking.PortSelector{
Number: 64,
},
},
Weight: 100,
},
},
},
},
}
virtualService1 := config.Config{
Meta: config.Meta{
GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(),
Name: "acme2-v1",
Namespace: "not-default",
},
Spec: virtualServiceSpec1,
}
virtualService2 := config.Config{
Meta: config.Meta{
GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(),
Name: "acme-v2",
Namespace: "not-default",
},
Spec: virtualServiceSpec2,
}
virtualService3 := config.Config{
Meta: config.Meta{
GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(),
Name: "acme-v3",
Namespace: "not-default",
},
Spec: virtualServiceSpec3,
}
virtualService4 := config.Config{
Meta: config.Meta{
GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(),
Name: "acme-v4",
Namespace: "not-default",
},
Spec: virtualServiceSpec4,
}
virtualService5 := config.Config{
Meta: config.Meta{
GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(),
Name: "acme-v3",
Namespace: "not-default",
},
Spec: virtualServiceSpec5,
}
virtualService6 := config.Config{
Meta: config.Meta{
GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(),
Name: "acme-v3",
Namespace: "not-default",
},
Spec: virtualServiceSpec6,
}
virtualService7 := config.Config{
Meta: config.Meta{
GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(),
Name: "acme2-v1",
Namespace: "some-other-ns",
},
Spec: virtualServiceSpec7,
}
configs := SelectVirtualServices(
[]config.Config{virtualService1, virtualService2, virtualService3, virtualService4, virtualService5, virtualService6, virtualService7},
hostsByNamespace)
expectedVS := []string{virtualService1.Name, virtualService2.Name, virtualService4.Name, virtualService7.Name}
if len(expectedVS) != len(configs) {
t.Fatalf("Unexpected virtualService, got %d, epxected %d", len(configs), len(expectedVS))
}
for i, config := range configs {
if config.Name != expectedVS[i] {
t.Fatalf("Unexpected virtualService, got %s, epxected %s", config.Name, expectedVS[i])
}
}
}
func buildHTTPService(hostname string, v visibility.Instance, ip, namespace string, ports ...int) *Service {
service := &Service{
CreationTime: time.Now(),
Hostname: host.Name(hostname),
DefaultAddress: ip,
Resolution: DNSLB,
Attributes: ServiceAttributes{
ServiceRegistry: provider.Kubernetes,
Namespace: namespace,
ExportTo: map[visibility.Instance]bool{v: true},
},
}
if ip == wildcardIP {
service.Resolution = Passthrough
}
Ports := make([]*Port, 0)
for _, p := range ports {
Ports = append(Ports, &Port{
Name: fmt.Sprintf("http-%d", p),
Port: p,
Protocol: protocol.HTTP,
})
}
service.Ports = Ports
return service
}