From 7ceec94a87bc05bc661b4644d0bf8527b0ee83c7 Mon Sep 17 00:00:00 2001 From: charlie Date: Thu, 2 Feb 2023 15:22:11 +0800 Subject: [PATCH] unit-test: add unit test for pkg/ingress/kube/annotations (#156) Signed-off-by: charlie --- pkg/ingress/kube/annotations/canary_test.go | 103 ++++++++++++ pkg/ingress/kube/annotations/destination.go | 5 +- .../kube/annotations/destination_test.go | 98 +++++++++++ pkg/ingress/kube/annotations/redirect_test.go | 89 ++++++++++ .../kube/annotations/upstreamtls_test.go | 152 ++++++++++++++++++ pkg/ingress/kube/annotations/util_test.go | 33 ++++ 6 files changed, 479 insertions(+), 1 deletion(-) create mode 100644 pkg/ingress/kube/annotations/destination_test.go create mode 100644 pkg/ingress/kube/annotations/redirect_test.go create mode 100644 pkg/ingress/kube/annotations/upstreamtls_test.go diff --git a/pkg/ingress/kube/annotations/canary_test.go b/pkg/ingress/kube/annotations/canary_test.go index 0376e09ed..3fa6e095c 100644 --- a/pkg/ingress/kube/annotations/canary_test.go +++ b/pkg/ingress/kube/annotations/canary_test.go @@ -18,9 +18,112 @@ import ( "reflect" "testing" + "github.com/google/go-cmp/cmp" + networking "istio.io/api/networking/v1alpha3" ) +func TestCanaryParse(t *testing.T) { + parser := canary{} + + testCases := []struct { + name string + input Annotations + expect *CanaryConfig + }{ + { + name: "Don't contain the 'enableCanary' key", + input: Annotations{}, + expect: nil, + }, + { + name: "the 'enableCanary' is false", + input: Annotations{ + buildNginxAnnotationKey(enableCanary): "false", + }, + expect: &CanaryConfig{ + Enabled: false, + WeightTotal: defaultCanaryWeightTotal, + }, + }, + { + name: "By header", + input: Annotations{ + buildNginxAnnotationKey(enableCanary): "true", + buildNginxAnnotationKey(canaryByHeader): "header", + }, + expect: &CanaryConfig{ + Enabled: true, + Header: "header", + WeightTotal: defaultCanaryWeightTotal, + }, + }, + { + name: "By headerValue", + input: Annotations{ + buildNginxAnnotationKey(enableCanary): "true", + buildNginxAnnotationKey(canaryByHeader): "header", + buildNginxAnnotationKey(canaryByHeaderValue): "headerValue", + }, + expect: &CanaryConfig{ + Enabled: true, + Header: "header", + HeaderValue: "headerValue", + WeightTotal: defaultCanaryWeightTotal, + }, + }, + { + name: "By headerPattern", + input: Annotations{ + buildNginxAnnotationKey(enableCanary): "true", + buildNginxAnnotationKey(canaryByHeader): "header", + buildNginxAnnotationKey(canaryByHeaderPattern): "headerPattern", + }, + expect: &CanaryConfig{ + Enabled: true, + Header: "header", + HeaderPattern: "headerPattern", + WeightTotal: defaultCanaryWeightTotal, + }, + }, + { + name: "By cookie", + input: Annotations{ + buildNginxAnnotationKey(enableCanary): "true", + buildNginxAnnotationKey(canaryByCookie): "cookie", + }, + expect: &CanaryConfig{ + Enabled: true, + Cookie: "cookie", + WeightTotal: defaultCanaryWeightTotal, + }, + }, + { + name: "By weight", + input: Annotations{ + buildNginxAnnotationKey(enableCanary): "true", + buildNginxAnnotationKey(canaryWeight): "50", + buildNginxAnnotationKey(canaryWeightTotal): "100", + }, + expect: &CanaryConfig{ + Enabled: true, + Weight: 50, + WeightTotal: 100, + }, + }, + } + + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + config := &Ingress{} + _ = parser.Parse(tt.input, config, nil) + if diff := cmp.Diff(tt.expect, config.Canary); diff != "" { + t.Fatalf("TestCanaryParse() mismatch (-want +got):\n%s", diff) + } + }) + } +} + func TestApplyWeight(t *testing.T) { route := &networking.HTTPRoute{ Headers: &networking.Headers{ diff --git a/pkg/ingress/kube/annotations/destination.go b/pkg/ingress/kube/annotations/destination.go index e69d9c5e9..1cddbf145 100644 --- a/pkg/ingress/kube/annotations/destination.go +++ b/pkg/ingress/kube/annotations/destination.go @@ -37,7 +37,7 @@ type DestinationConfig struct { type destination struct{} -func (a destination) Parse(annotations Annotations, config *Ingress, globalContext *GlobalContext) error { +func (a destination) Parse(annotations Annotations, config *Ingress, _ *GlobalContext) error { if !needDestinationConfig(annotations) { return nil } @@ -55,6 +55,9 @@ func (a destination) Parse(annotations Annotations, config *Ingress, globalConte pairs := strings.Fields(line) var weight int64 = 100 var addrIndex int + if len(pairs) == 0 { + continue + } if strings.HasSuffix(pairs[0], "%") { weight, err = strconv.ParseInt(strings.TrimSuffix(pairs[0], "%"), 10, 32) if err != nil { diff --git a/pkg/ingress/kube/annotations/destination_test.go b/pkg/ingress/kube/annotations/destination_test.go new file mode 100644 index 000000000..aead0606e --- /dev/null +++ b/pkg/ingress/kube/annotations/destination_test.go @@ -0,0 +1,98 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// 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 annotations + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + + networking "istio.io/api/networking/v1alpha3" +) + +func TestDestinationParse(t *testing.T) { + parser := destination{} + + testCases := []struct { + input Annotations + expect *DestinationConfig + }{ + { + input: Annotations{}, + expect: nil, + }, + { + input: Annotations{ + buildHigressAnnotationKey(destinationKey): "", + }, + expect: nil, + }, + { + input: Annotations{ + buildHigressAnnotationKey(destinationKey): "100% my-svc.DEFAULT-GROUP.xxxx.nacos:8080 v1", + }, + expect: &DestinationConfig{ + McpDestination: []*networking.HTTPRouteDestination{ + { + Destination: &networking.Destination{ + Host: "my-svc.DEFAULT-GROUP.xxxx.nacos", + Subset: "v1", + Port: &networking.PortSelector{Number: 8080}, + }, + Weight: 100, + }, + }, + WeightSum: 100, + }, + }, + { + input: Annotations{ + buildHigressAnnotationKey(destinationKey): "50% my-svc.DEFAULT-GROUP.xxxx.nacos:8080 v1\n\n" + + "50% my-svc.DEFAULT-GROUP.xxxx.nacos:8080 v2", + }, + expect: &DestinationConfig{ + McpDestination: []*networking.HTTPRouteDestination{ + { + Destination: &networking.Destination{ + Host: "my-svc.DEFAULT-GROUP.xxxx.nacos", + Subset: "v1", + Port: &networking.PortSelector{Number: 8080}, + }, + Weight: 50, + }, + { + Destination: &networking.Destination{ + Host: "my-svc.DEFAULT-GROUP.xxxx.nacos", + Subset: "v2", + Port: &networking.PortSelector{Number: 8080}, + }, + Weight: 50, + }, + }, + WeightSum: 100, + }, + }, + } + + for _, testCase := range testCases { + t.Run("", func(t *testing.T) { + config := &Ingress{} + _ = parser.Parse(testCase.input, config, nil) + if diff := cmp.Diff(config.Destination, testCase.expect); diff != "" { + t.Fatalf("TestDestinationParse() mismatch: (-want +got)\n%s", diff) + } + }) + } +} diff --git a/pkg/ingress/kube/annotations/redirect_test.go b/pkg/ingress/kube/annotations/redirect_test.go new file mode 100644 index 000000000..a1f39bcf3 --- /dev/null +++ b/pkg/ingress/kube/annotations/redirect_test.go @@ -0,0 +1,89 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// 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 annotations + +import ( + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestRedirectParse(t *testing.T) { + parser := redirect{} + + testCases := []struct { + name string + input Annotations + expect *RedirectConfig + }{ + { + name: "Don't contain any redirect keys", + input: Annotations{}, + expect: nil, + }, + { + name: "By appRoot", + input: Annotations{ + buildHigressAnnotationKey(appRoot): "/root", + buildHigressAnnotationKey(sslRedirect): "true", + buildHigressAnnotationKey(forceSSLRedirect): "true", + }, + expect: &RedirectConfig{ + AppRoot: "/root", + httpsRedirect: true, + Code: defaultPermanentRedirectCode, + }, + }, + { + name: "By temporalRedirect", + input: Annotations{ + buildHigressAnnotationKey(temporalRedirect): "http://www.xxx.org", + }, + expect: &RedirectConfig{ + URL: "http://www.xxx.org", + Code: defaultTemporalRedirectCode, + }, + }, + { + name: "By temporalRedirect with invalid url", + input: Annotations{ + buildHigressAnnotationKey(temporalRedirect): "tcp://www.xxx.org", + }, + expect: &RedirectConfig{ + Code: defaultPermanentRedirectCode, + }, + }, + { + name: "By permanentRedirect", + input: Annotations{ + buildHigressAnnotationKey(permanentRedirect): "http://www.xxx.org", + }, + expect: &RedirectConfig{ + URL: "http://www.xxx.org", + Code: defaultPermanentRedirectCode, + }, + }, + } + + for _, tt := range testCases { + t.Run("", func(t *testing.T) { + config := &Ingress{} + _ = parser.Parse(tt.input, config, nil) + if diff := cmp.Diff(tt.expect, config.Redirect, cmp.AllowUnexported(RedirectConfig{})); diff != "" { + t.Fatalf("TestRedirectParse() mismatch (-want +got):\n%s", diff) + } + }) + } +} diff --git a/pkg/ingress/kube/annotations/upstreamtls_test.go b/pkg/ingress/kube/annotations/upstreamtls_test.go new file mode 100644 index 000000000..6cff32647 --- /dev/null +++ b/pkg/ingress/kube/annotations/upstreamtls_test.go @@ -0,0 +1,152 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// 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 annotations + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + + networking "istio.io/api/networking/v1alpha3" +) + +func TestUpstreamTLSParse(t *testing.T) { + parser := upstreamTLS{} + + testCases := []struct { + input Annotations + expect *UpstreamTLSConfig + }{ + { + input: Annotations{}, + expect: nil, + }, + { + input: Annotations{ + buildNginxAnnotationKey(proxySSLSecret): "", + buildNginxAnnotationKey(backendProtocol): "HTTP2", + buildNginxAnnotationKey(proxySSLSecret): "namespace/SSLSecret", + buildNginxAnnotationKey(proxySSLVerify): "on", + buildNginxAnnotationKey(proxySSLName): "SSLName", + buildNginxAnnotationKey(proxySSLServerName): "on", + }, + expect: &UpstreamTLSConfig{ + BackendProtocol: "HTTP2", + SSLVerify: true, + SNI: "SSLName", + SecretName: "namespace/SSLSecret", + }, + }, + { + input: Annotations{ + buildNginxAnnotationKey(proxySSLSecret): "", + buildNginxAnnotationKey(backendProtocol): "HTTP2", + buildNginxAnnotationKey(proxySSLSecret): "", // if there is no ssl secret, it will be return directly + buildNginxAnnotationKey(proxySSLVerify): "on", + buildNginxAnnotationKey(proxySSLName): "SSLName", + buildNginxAnnotationKey(proxySSLServerName): "on", + }, + expect: &UpstreamTLSConfig{ + BackendProtocol: "HTTP2", + SSLVerify: false, + SNI: "", + SecretName: "", + }, + }, + } + + for _, testCase := range testCases { + t.Run("", func(t *testing.T) { + config := &Ingress{} + _ = parser.Parse(testCase.input, config, nil) + if diff := cmp.Diff(testCase.expect, config.UpstreamTLS); diff != "" { + t.Fatalf("TestUpstreamTLSParse() mismatch: \n%s", diff) + } + }) + } +} + +func TestApplyTrafficPolicy(t *testing.T) { + parser := upstreamTLS{} + + testCases := []struct { + input *networking.TrafficPolicy_PortTrafficPolicy + config *Ingress + expect *networking.TrafficPolicy_PortTrafficPolicy + }{ + { + input: &networking.TrafficPolicy_PortTrafficPolicy{}, + config: &Ingress{ + UpstreamTLS: &UpstreamTLSConfig{}, + }, + expect: &networking.TrafficPolicy_PortTrafficPolicy{}, + }, + { + input: &networking.TrafficPolicy_PortTrafficPolicy{}, + config: &Ingress{ + UpstreamTLS: &UpstreamTLSConfig{ + BackendProtocol: "HTTP2", + }, + }, + expect: &networking.TrafficPolicy_PortTrafficPolicy{ + ConnectionPool: &networking.ConnectionPoolSettings{ + Http: &networking.ConnectionPoolSettings_HTTPSettings{ + H2UpgradePolicy: networking.ConnectionPoolSettings_HTTPSettings_UPGRADE, + }, + }, + }, + }, + { + input: &networking.TrafficPolicy_PortTrafficPolicy{}, + config: &Ingress{ + UpstreamTLS: &UpstreamTLSConfig{ + BackendProtocol: "HTTPS", + EnableSNI: true, + SNI: "SNI", + }, + }, + expect: &networking.TrafficPolicy_PortTrafficPolicy{ + Tls: &networking.ClientTLSSettings{ + Mode: networking.ClientTLSSettings_SIMPLE, + Sni: "SNI", + }, + }, + }, + { + input: &networking.TrafficPolicy_PortTrafficPolicy{}, + config: &Ingress{ + UpstreamTLS: &UpstreamTLSConfig{ + SecretName: "namespace/secretName", + SSLVerify: true, + }, + }, + expect: &networking.TrafficPolicy_PortTrafficPolicy{ + Tls: &networking.ClientTLSSettings{ + Mode: networking.ClientTLSSettings_MUTUAL, + CredentialName: "kubernetes-ingress://Kubernetes/namespace/secretName", + }, + }, + }, + } + + for _, testCase := range testCases { + t.Run("", func(t *testing.T) { + parser.ApplyTrafficPolicy(testCase.input, testCase.config) + if diff := cmp.Diff(testCase.expect, testCase.input); diff != "" { + t.Fatalf("TestApplyTrafficPolicy() mismatch (-want +got): \n%s", diff) + } + }) + } +} diff --git a/pkg/ingress/kube/annotations/util_test.go b/pkg/ingress/kube/annotations/util_test.go index a862cab59..4f9b1bdb7 100644 --- a/pkg/ingress/kube/annotations/util_test.go +++ b/pkg/ingress/kube/annotations/util_test.go @@ -18,6 +18,7 @@ import ( "reflect" "testing" + "github.com/google/go-cmp/cmp" "istio.io/istio/pilot/pkg/model" ) @@ -51,3 +52,35 @@ func TestExtraSecret(t *testing.T) { }) } } + +func TestSplitBySeparator(t *testing.T) { + testCases := []struct { + input string + sep string + expect []string + }{ + { + input: "a b c d", + sep: " ", + expect: []string{"a", "b", "c", "d"}, + }, + { + input: ".1.2.3.4.", + sep: ".", + expect: []string{"1", "2", "3", "4"}, + }, + { + input: "1....2....3....4", + sep: ".", + expect: []string{"1", "2", "3", "4"}, + }, + } + + for _, tt := range testCases { + got := splitBySeparator(tt.input, tt.sep) + if diff := cmp.Diff(tt.expect, got); diff != "" { + t.Errorf("TestSplitBySeparator() mismatch (-want +got):\n%s", diff) + } + } + +}