1// Copyright 2019 Istio Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package v2
16
17import (
18	"fmt"
19	"reflect"
20	"strconv"
21	"testing"
22
23	model "istio.io/istio/pilot/pkg/model"
24	"istio.io/istio/pkg/config/schema/collections"
25	"istio.io/istio/pkg/config/schema/resource"
26)
27
28func TestProxyNeedsPush(t *testing.T) {
29	const (
30		invalidKind = "INVALID_KIND"
31		svcName     = "svc1.com"
32		drName      = "dr1"
33		vsName      = "vs1"
34		nsName      = "ns1"
35		generalName = "name1"
36
37		invalidNameSuffix = "invalid"
38	)
39
40	type Case struct {
41		name    string
42		proxy   *model.Proxy
43		configs map[model.ConfigKey]struct{}
44		want    bool
45	}
46
47	sidecar := &model.Proxy{
48		Type: model.SidecarProxy, IPAddresses: []string{"127.0.0.1"}, Metadata: &model.NodeMetadata{},
49		SidecarScope: &model.SidecarScope{}}
50	gateway := &model.Proxy{Type: model.Router}
51
52	sidecarScopeKindNames := map[resource.GroupVersionKind]string{
53		model.ServiceEntryKind: svcName, model.VirtualServiceKind: vsName, model.DestinationRuleKind: drName}
54	for kind, name := range sidecarScopeKindNames {
55		sidecar.SidecarScope.AddConfigDependencies(model.ConfigKey{Kind: kind, Name: name, Namespace: nsName})
56	}
57	for kind, types := range configKindAffectedProxyTypes {
58		for _, nodeType := range types {
59			if nodeType == model.SidecarProxy {
60				sidecar.SidecarScope.AddConfigDependencies(model.ConfigKey{
61					Kind:      kind,
62					Name:      generalName,
63					Namespace: nsName,
64				})
65			}
66		}
67	}
68
69	cases := []Case{
70		{"no namespace or configs", sidecar, nil, true},
71		{"gateway config for sidecar", sidecar, map[model.ConfigKey]struct{}{
72			{
73				Kind: collections.IstioNetworkingV1Alpha3Gateways.Resource().GroupVersionKind(),
74				Name: generalName, Namespace: nsName}: {}}, false},
75		{"gateway config for gateway", gateway, map[model.ConfigKey]struct{}{
76			{
77				Kind: collections.IstioNetworkingV1Alpha3Gateways.Resource().GroupVersionKind(),
78				Name: generalName, Namespace: nsName}: {}}, true},
79		{"quotaspec config for sidecar", sidecar, map[model.ConfigKey]struct{}{
80			{
81				Kind: collections.IstioMixerV1ConfigClientQuotaspecs.Resource().GroupVersionKind(),
82				Name: generalName, Namespace: nsName}: {}}, true},
83		{"quotaspec config for gateway", gateway, map[model.ConfigKey]struct{}{
84			{
85				Kind: collections.IstioMixerV1ConfigClientQuotaspecs.Resource().GroupVersionKind(),
86				Name: generalName, Namespace: nsName}: {}}, false},
87		{"invalid config for sidecar", sidecar, map[model.ConfigKey]struct{}{
88			{
89				Kind: resource.GroupVersionKind{Kind: invalidKind}, Name: generalName, Namespace: nsName}: {}},
90			true},
91		{"mixture matched and unmatched config for sidecar", sidecar, map[model.ConfigKey]struct{}{
92			{Kind: model.DestinationRuleKind, Name: drName, Namespace: nsName}:                   {},
93			{Kind: model.ServiceEntryKind, Name: svcName + invalidNameSuffix, Namespace: nsName}: {},
94		}, true},
95		{"mixture unmatched and unmatched config for sidecar", sidecar, map[model.ConfigKey]struct{}{
96			{Kind: model.DestinationRuleKind, Name: drName + invalidNameSuffix, Namespace: nsName}: {},
97			{Kind: model.ServiceEntryKind, Name: svcName + invalidNameSuffix, Namespace: nsName}:   {},
98		}, false},
99		{"empty configsUpdated for sidecar", sidecar, nil, true},
100	}
101
102	for kind, name := range sidecarScopeKindNames {
103		cases = append(cases, Case{ // valid name
104			name:    fmt.Sprintf("%s config for sidecar", kind.Kind),
105			proxy:   sidecar,
106			configs: map[model.ConfigKey]struct{}{{Kind: kind, Name: name, Namespace: nsName}: {}},
107			want:    true,
108		}, Case{ // invalid name
109			name:    fmt.Sprintf("%s unmatched config for sidecar", kind.Kind),
110			proxy:   sidecar,
111			configs: map[model.ConfigKey]struct{}{{Kind: kind, Name: name + invalidNameSuffix, Namespace: nsName}: {}},
112			want:    false,
113		})
114	}
115
116	// tests for kind-affect-proxy.
117	for kind, types := range configKindAffectedProxyTypes {
118		for _, nodeType := range types {
119			proxy := gateway
120			if nodeType == model.SidecarProxy {
121				proxy = sidecar
122			}
123			cases = append(cases, Case{
124				name:  fmt.Sprintf("kind %s affect %s", kind, nodeType),
125				proxy: proxy,
126				configs: map[model.ConfigKey]struct{}{
127					{Kind: kind, Name: generalName + invalidNameSuffix, Namespace: nsName}: {}},
128				want: true,
129			})
130		}
131	}
132
133	for _, tt := range cases {
134		t.Run(tt.name, func(t *testing.T) {
135			pushEv := &XdsEvent{configsUpdated: tt.configs}
136			got := ProxyNeedsPush(tt.proxy, pushEv)
137			if got != tt.want {
138				t.Fatalf("Got needs push = %v, expected %v", got, tt.want)
139			}
140		})
141	}
142}
143
144func TestPushTypeFor(t *testing.T) {
145	t.Parallel()
146
147	sidecar := &model.Proxy{Type: model.SidecarProxy}
148	gateway := &model.Proxy{Type: model.Router}
149
150	tests := []struct {
151		name        string
152		proxy       *model.Proxy
153		configTypes []resource.GroupVersionKind
154		expect      map[XdsType]bool
155	}{
156		{
157			name:        "configTypes is empty",
158			proxy:       sidecar,
159			configTypes: nil,
160			expect:      map[XdsType]bool{CDS: true, EDS: true, LDS: true, RDS: true},
161		},
162		{
163			name:        "configTypes is empty",
164			proxy:       gateway,
165			configTypes: nil,
166			expect:      map[XdsType]bool{CDS: true, EDS: true, LDS: true, RDS: true},
167		},
168		{
169			name:        "sidecar updated for sidecar proxy",
170			proxy:       sidecar,
171			configTypes: []resource.GroupVersionKind{collections.IstioNetworkingV1Alpha3Sidecars.Resource().GroupVersionKind()},
172			expect:      map[XdsType]bool{CDS: true, EDS: true, LDS: true, RDS: true},
173		},
174		{
175			name:        "sidecar updated for gateway proxy",
176			proxy:       gateway,
177			configTypes: []resource.GroupVersionKind{collections.IstioNetworkingV1Alpha3Sidecars.Resource().GroupVersionKind()},
178			expect:      map[XdsType]bool{},
179		},
180		{
181			name:        "quotaSpec updated for sidecar proxy",
182			proxy:       sidecar,
183			configTypes: []resource.GroupVersionKind{collections.IstioMixerV1ConfigClientQuotaspecs.Resource().GroupVersionKind()},
184			expect:      map[XdsType]bool{LDS: true, RDS: true},
185		},
186		{
187			name:        "quotaSpec updated for gateway",
188			proxy:       gateway,
189			configTypes: []resource.GroupVersionKind{collections.IstioMixerV1ConfigClientQuotaspecs.Resource().GroupVersionKind()},
190			expect:      map[XdsType]bool{},
191		},
192		{
193			name:        "authorizationpolicy updated",
194			proxy:       sidecar,
195			configTypes: []resource.GroupVersionKind{collections.IstioSecurityV1Beta1Authorizationpolicies.Resource().GroupVersionKind()},
196			expect:      map[XdsType]bool{LDS: true},
197		},
198		{
199			name:        "authorizationpolicy updated",
200			proxy:       gateway,
201			configTypes: []resource.GroupVersionKind{collections.IstioSecurityV1Beta1Authorizationpolicies.Resource().GroupVersionKind()},
202			expect:      map[XdsType]bool{LDS: true},
203		},
204		{
205			name:        "unknown type updated",
206			proxy:       sidecar,
207			configTypes: []resource.GroupVersionKind{{Kind: "unknown"}},
208			expect:      map[XdsType]bool{CDS: true, EDS: true, LDS: true, RDS: true},
209		},
210		{
211			name:        "unknown type updated",
212			proxy:       gateway,
213			configTypes: []resource.GroupVersionKind{},
214			expect:      map[XdsType]bool{CDS: true, EDS: true, LDS: true, RDS: true},
215		},
216		{
217			name:  "gateway and virtualservice updated for gateway proxy",
218			proxy: gateway,
219			configTypes: []resource.GroupVersionKind{collections.IstioNetworkingV1Alpha3Gateways.Resource().GroupVersionKind(),
220				collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind()},
221			expect: map[XdsType]bool{LDS: true, RDS: true},
222		},
223		{
224			name:  "virtualservice and destinationrule updated",
225			proxy: sidecar,
226			configTypes: []resource.GroupVersionKind{collections.IstioNetworkingV1Alpha3Destinationrules.Resource().GroupVersionKind(),
227				collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind()},
228			expect: map[XdsType]bool{CDS: true, EDS: true, LDS: true, RDS: true},
229		},
230		{
231			name:        "requestauthentication updated",
232			proxy:       sidecar,
233			configTypes: []resource.GroupVersionKind{collections.IstioSecurityV1Beta1Requestauthentications.Resource().GroupVersionKind()},
234			expect:      map[XdsType]bool{LDS: true},
235		},
236		{
237			name:        "requestauthentication updated",
238			proxy:       gateway,
239			configTypes: []resource.GroupVersionKind{collections.IstioSecurityV1Beta1Requestauthentications.Resource().GroupVersionKind()},
240			expect:      map[XdsType]bool{LDS: true},
241		},
242		{
243			name:        "peerauthentication updated",
244			proxy:       sidecar,
245			configTypes: []resource.GroupVersionKind{collections.IstioSecurityV1Beta1Peerauthentications.Resource().GroupVersionKind()},
246			expect:      map[XdsType]bool{CDS: true, EDS: true, LDS: true},
247		},
248		{
249			name:        "peerauthentication updated",
250			proxy:       gateway,
251			configTypes: []resource.GroupVersionKind{collections.IstioSecurityV1Beta1Peerauthentications.Resource().GroupVersionKind()},
252			expect:      map[XdsType]bool{CDS: true, EDS: true, LDS: true},
253		},
254	}
255
256	for _, tt := range tests {
257		t.Run(tt.name, func(t *testing.T) {
258			cfgs := map[model.ConfigKey]struct{}{}
259			for _, kind := range tt.configTypes {
260				cfgs[model.ConfigKey{
261					Kind:      kind,
262					Name:      "name",
263					Namespace: "ns",
264				}] = struct{}{}
265			}
266			pushEv := &XdsEvent{configsUpdated: cfgs}
267			out := PushTypeFor(tt.proxy, pushEv)
268			if !reflect.DeepEqual(out, tt.expect) {
269				t.Errorf("expected: %v, but got %v", tt.expect, out)
270			}
271		})
272	}
273}
274
275func BenchmarkListEquals(b *testing.B) {
276	size := 100
277	var l []string
278	for i := 0; i < size; i++ {
279		l = append(l, strconv.Itoa(i))
280	}
281	var equal []string
282	for i := 0; i < size; i++ {
283		equal = append(equal, strconv.Itoa(i))
284	}
285	var notEqual []string
286	for i := 0; i < size; i++ {
287		notEqual = append(notEqual, strconv.Itoa(i))
288	}
289	notEqual[size-1] = "z"
290
291	for n := 0; n < b.N; n++ {
292		listEqualUnordered(l, equal)
293		listEqualUnordered(l, notEqual)
294	}
295}
296