mirror of
https://github.com/alibaba/higress.git
synced 2026-02-26 21:50:53 +08:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b7723bac5 | ||
|
|
f402f86374 | ||
|
|
7726d5d138 | ||
|
|
50e7bfddee | ||
|
|
9400f7bf07 | ||
|
|
40f4d7845d | ||
|
|
d84c9e67c3 | ||
|
|
71f5dcd123 | ||
|
|
245c807b85 | ||
|
|
c88ee327ba | ||
|
|
ca0d62c91a | ||
|
|
b000bc6ce9 | ||
|
|
1b8ec8d204 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,6 +6,7 @@ out
|
||||
.DS_Store
|
||||
coverage.xml
|
||||
.idea/
|
||||
.vscode/
|
||||
bazel-bin
|
||||
bazel-out
|
||||
bazel-testlogs
|
||||
|
||||
12
CODEOWNERS
12
CODEOWNERS
@@ -1,4 +1,10 @@
|
||||
# Top level
|
||||
* @johnlanni @SpecialYang @Lynskylate @gengleilei @NameHaibinZhang
|
||||
/api @johnlanni
|
||||
/envoy @gengleilei @johnlanni @Lynskylate
|
||||
/istio @SpecialYang @johnlanni
|
||||
/pkg @SpecialYang @johnlanni
|
||||
/plugins @johnlanni
|
||||
/registry @NameHaibinZhang @johnlanni
|
||||
/test @Xunzhuo
|
||||
/tools @johnlanni @Xunzhuo
|
||||
|
||||
|
||||
# TODO Add code reviewers for subdirectory.
|
||||
|
||||
@@ -96,13 +96,13 @@ export PARENT_GIT_REVISION:=$(TAG)
|
||||
export ENVOY_TAR_PATH:=/home/package/envoy.tar.gz
|
||||
|
||||
build-istio: prebuild
|
||||
cd external/istio; GOOS_LOCAL=linux TARGET_OS=linux TARGET_ARCH=amd64 BUILD_WITH_CONTAINER=1 DOCKER_BUILD_VARIANTS=default DOCKER_TARGETS="docker.pilot" make docker
|
||||
cd external/istio; rm -rf out; GOOS_LOCAL=linux TARGET_OS=linux TARGET_ARCH=amd64 BUILD_WITH_CONTAINER=1 DOCKER_BUILD_VARIANTS=default DOCKER_TARGETS="docker.pilot" make docker
|
||||
|
||||
external/package/envoy.tar.gz:
|
||||
cd external/proxy; BUILD_WITH_CONTAINER=1 make test_release
|
||||
|
||||
build-gateway: prebuild external/package/envoy.tar.gz
|
||||
cd external/istio; GOOS_LOCAL=linux TARGET_OS=linux TARGET_ARCH=amd64 BUILD_WITH_CONTAINER=1 DOCKER_BUILD_VARIANTS=default DOCKER_TARGETS="docker.proxyv2" make docker
|
||||
cd external/istio; rm -rf out; GOOS_LOCAL=linux TARGET_OS=linux TARGET_ARCH=amd64 BUILD_WITH_CONTAINER=1 DOCKER_BUILD_VARIANTS=default DOCKER_TARGETS="docker.proxyv2" make docker
|
||||
|
||||
pre-install:
|
||||
cp api/kubernetes/customresourcedefinitions.gen.yaml helm/higress/crds
|
||||
@@ -182,7 +182,7 @@ gateway-conformance-test:
|
||||
|
||||
# ingress-conformance-test runs ingress api conformance tests.
|
||||
.PHONY: ingress-conformance-test
|
||||
ingress-conformance-test: $(tools/kind) delete-cluster create-cluster kube-load-image install-dev run-ingress-e2e-test delete-cluster
|
||||
ingress-conformance-test: $(tools/kind) delete-cluster create-cluster docker-build kube-load-image install-dev run-ingress-e2e-test delete-cluster
|
||||
|
||||
# create-cluster creates a kube cluster with kind.
|
||||
.PHONY: create-cluster
|
||||
@@ -196,7 +196,7 @@ delete-cluster: $(tools/kind) ## Delete kind cluster.
|
||||
|
||||
# kube-load-image loads a local built docker image into kube cluster.
|
||||
.PHONY: kube-load-image
|
||||
kube-load-image: docker-build $(tools/kind) ## Install the EG image to a kind cluster using the provided $IMAGE and $TAG.
|
||||
kube-load-image: $(tools/kind) ## Install the EG image to a kind cluster using the provided $IMAGE and $TAG.
|
||||
tools/hack/kind-load-image.sh higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/higress $(TAG)
|
||||
|
||||
# run-ingress-e2e-test starts to run ingress e2e tests.
|
||||
@@ -207,4 +207,4 @@ run-ingress-e2e-test:
|
||||
kubectl wait --timeout=5m -n higress-system deployment/higress-controller --for=condition=Available
|
||||
@echo -e "\n\033[36mWaiting higress-gateway to be ready...\033[0m\n"
|
||||
kubectl wait --timeout=5m -n higress-system deployment/higress-gateway --for=condition=Available
|
||||
go test -v -tags conformance ./test/ingress/e2e_test.go --ingress-class=higress --debug=true --use-unique-ports=true
|
||||
go test -v -tags conformance ./test/ingress/e2e_test.go --ingress-class=higress --debug=true
|
||||
|
||||
@@ -7,6 +7,7 @@ ARG BASE_VERSION=latest
|
||||
ARG HUB
|
||||
|
||||
# The following section is used as base image if BASE_DISTRIBUTION=debug
|
||||
# This base image is provided by istio, see: https://github.com/istio/istio/blob/master/docker/Dockerfile.base
|
||||
FROM ${HUB}/base:${BASE_VERSION}
|
||||
|
||||
ARG TARGETARCH
|
||||
|
||||
2
go.mod
2
go.mod
@@ -22,6 +22,7 @@ require (
|
||||
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1
|
||||
github.com/gogo/protobuf v1.3.2
|
||||
github.com/golang/protobuf v1.5.2
|
||||
github.com/google/go-cmp v0.5.8
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/nacos-group/nacos-sdk-go v1.0.8
|
||||
@@ -98,7 +99,6 @@ require (
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/google/go-cmp v0.5.8 // indirect
|
||||
github.com/google/go-containerregistry v0.6.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
apiVersion: v2
|
||||
appVersion: 0.6.1
|
||||
appVersion: 0.6.2
|
||||
description: Helm chart for deploying higress gateways
|
||||
icon: https://higress.io/img/higress_logo_small.png
|
||||
keywords:
|
||||
@@ -9,4 +9,4 @@ name: higress
|
||||
sources:
|
||||
- http://github.com/alibaba/higress
|
||||
type: application
|
||||
version: 0.6.1
|
||||
version: 0.6.2
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
revision: ""
|
||||
global:
|
||||
# whether to use autoscaling/v2 template for HPA settings
|
||||
# for internal usage only, not to be configured by users.
|
||||
@@ -364,7 +365,7 @@ gateway:
|
||||
name: "higress-gateway"
|
||||
replicas: 2
|
||||
image: gateway
|
||||
tag: "0.6.1"
|
||||
tag: "0.6.2"
|
||||
# revision declares which revision this gateway is a part of
|
||||
revision: ""
|
||||
|
||||
@@ -409,10 +410,6 @@ gateway:
|
||||
# Type of service. Set to "None" to disable the service entirely
|
||||
type: LoadBalancer
|
||||
ports:
|
||||
- name: status-port
|
||||
port: 15021
|
||||
protocol: TCP
|
||||
targetPort: 15021
|
||||
- name: http2
|
||||
port: 80
|
||||
protocol: TCP
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
apiVersion: v2
|
||||
appVersion: 0.6.1
|
||||
appVersion: 0.6.2
|
||||
description: Helm chart for deploying higress gateways
|
||||
icon: https://higress.io/img/higress_logo_small.png
|
||||
keywords:
|
||||
@@ -11,6 +11,6 @@ sources:
|
||||
dependencies:
|
||||
- name: higress
|
||||
repository: "file://../../higress"
|
||||
version: 0.6.1
|
||||
version: 0.6.2
|
||||
type: application
|
||||
version: 0.6.1
|
||||
version: 0.6.2
|
||||
|
||||
18
istio/1.12/patches/istio/20230208-waf-stats.patch
Normal file
18
istio/1.12/patches/istio/20230208-waf-stats.patch
Normal file
@@ -0,0 +1,18 @@
|
||||
diff -Naur istio/tools/packaging/common/envoy_bootstrap.json istio_new/tools/packaging/common/envoy_bootstrap.json
|
||||
--- istio/tools/packaging/common/envoy_bootstrap.json 2023-02-08 22:42:41.000000000 +0800
|
||||
+++ istio_new/tools/packaging/common/envoy_bootstrap.json 2023-02-08 22:19:04.000000000 +0800
|
||||
@@ -37,6 +37,14 @@
|
||||
"use_all_default_tags": false,
|
||||
"stats_tags": [
|
||||
{
|
||||
+ "tag_name": "phase",
|
||||
+ "regex": "(_phase=([a-z_]+))"
|
||||
+ },
|
||||
+ {
|
||||
+ "tag_name": "ruleid",
|
||||
+ "regex": "(_ruleid=([0-9]+))"
|
||||
+ },
|
||||
+ {
|
||||
"tag_name": "route",
|
||||
"regex": "^vhost\\..*?\\.route\\.([^\\.]+\\.)upstream"
|
||||
},
|
||||
14
samples/wasmplugin/waf.yaml
Normal file
14
samples/wasmplugin/waf.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
apiVersion: extensions.higress.io/v1alpha1
|
||||
kind: WasmPlugin
|
||||
metadata:
|
||||
name: waf
|
||||
namespace: higress-system
|
||||
spec:
|
||||
# build from https://github.com/corazawaf/coraza-proxy-wasm
|
||||
url: oci://higress-registry.cn-hangzhou.cr.aliyuncs.com/plugins/corazawaf:1.0.0
|
||||
defaultConfig:
|
||||
rules:
|
||||
- "Include @crs-setup-conf"
|
||||
- "Include @recommended-conf"
|
||||
- "Include @owasp_crs/*.conf"
|
||||
- "SecRuleEngine on"
|
||||
@@ -7,13 +7,22 @@ Higress e2e tests are mainly focusing on two parts for now:
|
||||
|
||||
## Ingress APIs Conformance Tests
|
||||
|
||||
Higress provides make target to run ingress api conformance tests: `make e2e-test`. It can be divided into below steps:
|
||||
### Architecture
|
||||
|
||||

|
||||
|
||||
### Workflow
|
||||
|
||||

|
||||
|
||||
Higress provides make target to run ingress api conformance tests: `make ingress-conformance-test`. It can be divided into below steps:
|
||||
|
||||
1. delete-cluster: checks if we have undeleted kind cluster.
|
||||
2. create-cluster: create a new kind cluster.
|
||||
3. kube-load-image: build a dev image of higress, and load it into kind cluster.
|
||||
4. install-dev: install higress-controller with dev image, and latest higress-gateway, istiod with helm.
|
||||
5. run-e2e-test:
|
||||
3. docker-build: build a dev image of higress.
|
||||
4. kube-load-image: load dev higress-controller image it into kind cluster.
|
||||
5. install-dev: install higress-controller with dev image, and latest higress-gateway, istiod with helm.
|
||||
6. run-e2e-test:
|
||||
1. Setup conformance suite, like define what conformance tests we want to run, in `e2e_test.go` / `higressTests Slice`. Each case we choose to open is difined in `test/ingress/conformance/tests`.
|
||||
2. Prepare resources and install them into cluster, like backend services/deployments.
|
||||
3. Load conformance tests we choose to open in `e2e_test.go` / `higressTests Slice`, and run them one by one, fail if it is not expected.
|
||||
|
||||
BIN
test/ingress/arch.png
Normal file
BIN
test/ingress/arch.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
76
test/ingress/conformance/tests/httproute-canary-header.go
Normal file
76
test/ingress/conformance/tests/httproute-canary-header.go
Normal file
@@ -0,0 +1,76 @@
|
||||
// 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 tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/alibaba/higress/test/ingress/conformance/utils/http"
|
||||
"github.com/alibaba/higress/test/ingress/conformance/utils/suite"
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteRewritePath)
|
||||
}
|
||||
|
||||
var HTTPRouteCanaryHeader = suite.ConformanceTest{
|
||||
ShortName: "HTTPRouteCanaryHeader",
|
||||
Description: "The Ingress in the higress-conformance-infra namespace uses the canary header traffic split",
|
||||
Manifests: []string{"tests/httproute-canary-header.yaml"},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testcases := []http.Assertion{
|
||||
{
|
||||
Meta: http.AssertionMeta{
|
||||
TargetBackend: "infra-backend-v1",
|
||||
TargetNamespace: "higress-conformance-infra",
|
||||
},
|
||||
Request: http.AssertionRequest{
|
||||
ActualRequest: http.Request{
|
||||
Path: "/echo",
|
||||
Host: "canary.higress.io",
|
||||
Headers: map[string]string{"traffic-split-higress": "true"},
|
||||
},
|
||||
},
|
||||
Response: http.AssertionResponse{
|
||||
ExpectedResponse: http.Response{
|
||||
StatusCode: 200,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Meta: http.AssertionMeta{
|
||||
TargetBackend: "infra-backend-v2",
|
||||
TargetNamespace: "higress-conformance-infra",
|
||||
},
|
||||
Request: http.AssertionRequest{
|
||||
ActualRequest: http.Request{
|
||||
Path: "/echo",
|
||||
Host: "canary.higress.io",
|
||||
},
|
||||
},
|
||||
Response: http.AssertionResponse{
|
||||
ExpectedResponse: http.Response{
|
||||
StatusCode: 200,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("Canary HTTPRoute Traffic Split", func(t *testing.T) {
|
||||
for _, testcase := range testcases {
|
||||
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, testcase)
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
55
test/ingress/conformance/tests/httproute-canary-header.yaml
Normal file
55
test/ingress/conformance/tests/httproute-canary-header.yaml
Normal file
@@ -0,0 +1,55 @@
|
||||
# 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.
|
||||
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/canary: "true"
|
||||
nginx.ingress.kubernetes.io/canary-by-header: "traffic-split-higress"
|
||||
nginx.ingress.kubernetes.io/canary-by-header-value: "true"
|
||||
name: ingress-echo-canary-value
|
||||
namespace: higress-conformance-infra
|
||||
spec:
|
||||
ingressClassName: higress
|
||||
rules:
|
||||
- host: canary.higress.io
|
||||
http:
|
||||
paths:
|
||||
- path: /echo
|
||||
pathType: Exact
|
||||
backend:
|
||||
service:
|
||||
name: infra-backend-v1
|
||||
port:
|
||||
number: 8080
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: ingress-echo
|
||||
namespace: higress-conformance-infra
|
||||
spec:
|
||||
ingressClassName: higress
|
||||
rules:
|
||||
- host: canary.higress.io
|
||||
http:
|
||||
paths:
|
||||
- path: /echo
|
||||
pathType: Exact
|
||||
backend:
|
||||
service:
|
||||
name: infra-backend-v2
|
||||
port:
|
||||
number: 8080
|
||||
@@ -0,0 +1,123 @@
|
||||
// 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 tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/alibaba/higress/test/ingress/conformance/utils/http"
|
||||
"github.com/alibaba/higress/test/ingress/conformance/utils/suite"
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteHostNameSameNamespace)
|
||||
}
|
||||
|
||||
var HTTPRouteHostNameSameNamespace = suite.ConformanceTest{
|
||||
ShortName: "HTTPRouteHostNameSameNamespace",
|
||||
Description: "A Ingress in the higress-conformance-infra namespace demonstrates host match ability",
|
||||
Manifests: []string{"tests/httproute-hostname-same-namespace.yaml"},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testcases := []http.Assertion{
|
||||
{
|
||||
Meta: http.AssertionMeta{
|
||||
TargetBackend: "infra-backend-v1",
|
||||
TargetNamespace: "higress-conformance-infra",
|
||||
},
|
||||
Request: http.AssertionRequest{
|
||||
ActualRequest: http.Request{
|
||||
Path: "/foo",
|
||||
Host: "foo.com",
|
||||
},
|
||||
},
|
||||
Response: http.AssertionResponse{
|
||||
ExpectedResponse: http.Response{
|
||||
StatusCode: 200,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Meta: http.AssertionMeta{
|
||||
TargetBackend: "infra-backend-v2",
|
||||
TargetNamespace: "higress-conformance-infra",
|
||||
},
|
||||
Request: http.AssertionRequest{
|
||||
ActualRequest: http.Request{
|
||||
Path: "/foo",
|
||||
Host: "bar.com",
|
||||
},
|
||||
},
|
||||
Response: http.AssertionResponse{
|
||||
ExpectedResponse: http.Response{
|
||||
StatusCode: 200,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Meta: http.AssertionMeta{
|
||||
TargetBackend: "infra-backend-v2",
|
||||
TargetNamespace: "higress-conformance-infra",
|
||||
},
|
||||
Request: http.AssertionRequest{
|
||||
ActualRequest: http.Request{
|
||||
Path: "/bar",
|
||||
Host: "foo.com",
|
||||
},
|
||||
},
|
||||
Response: http.AssertionResponse{
|
||||
ExpectedResponse: http.Response{
|
||||
StatusCode: 200,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Meta: http.AssertionMeta{
|
||||
TargetBackend: "infra-backend-v3",
|
||||
TargetNamespace: "higress-conformance-infra",
|
||||
},
|
||||
Request: http.AssertionRequest{
|
||||
ActualRequest: http.Request{
|
||||
Path: "/bar",
|
||||
Host: "bar.com",
|
||||
},
|
||||
},
|
||||
Response: http.AssertionResponse{
|
||||
ExpectedResponse: http.Response{
|
||||
StatusCode: 200,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Meta: http.AssertionMeta{
|
||||
TargetBackend: "infra-backend-v1",
|
||||
TargetNamespace: "higress-conformance-infra",
|
||||
},
|
||||
Request: http.AssertionRequest{
|
||||
ActualRequest: http.Request{
|
||||
Path: "/any",
|
||||
Host: "any.bar.com",
|
||||
},
|
||||
},
|
||||
Response: http.AssertionResponse{
|
||||
ExpectedResponse: http.Response{
|
||||
StatusCode: 200,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("HTTP request should reach infra-backend with different hostname", func(t *testing.T) {
|
||||
for _, testcase := range testcases {
|
||||
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, testcase)
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
# 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.
|
||||
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: httproute-hostname-same-namespace
|
||||
namespace: higress-conformance-infra
|
||||
spec:
|
||||
ingressClassName: higress
|
||||
rules:
|
||||
- host: "foo.com"
|
||||
http:
|
||||
paths:
|
||||
- pathType: Prefix
|
||||
path: "/foo"
|
||||
backend:
|
||||
service:
|
||||
name: infra-backend-v1
|
||||
port:
|
||||
number: 8080
|
||||
- host: "bar.com"
|
||||
http:
|
||||
paths:
|
||||
- pathType: Prefix
|
||||
path: "/foo"
|
||||
backend:
|
||||
service:
|
||||
name: infra-backend-v2
|
||||
port:
|
||||
number: 8080
|
||||
- host: "foo.com"
|
||||
http:
|
||||
paths:
|
||||
- pathType: Prefix
|
||||
path: "/bar"
|
||||
backend:
|
||||
service:
|
||||
name: infra-backend-v2
|
||||
port:
|
||||
number: 8080
|
||||
- host: "bar.com"
|
||||
http:
|
||||
paths:
|
||||
- pathType: Prefix
|
||||
path: "/bar"
|
||||
backend:
|
||||
service:
|
||||
name: infra-backend-v3
|
||||
port:
|
||||
number: 8080
|
||||
- host: "*.bar.com"
|
||||
http:
|
||||
paths:
|
||||
- pathType: Prefix
|
||||
path: "/any"
|
||||
backend:
|
||||
service:
|
||||
name: infra-backend-v1
|
||||
port:
|
||||
number: 8080
|
||||
91
test/ingress/conformance/tests/httproute-rewrite-host.go
Normal file
91
test/ingress/conformance/tests/httproute-rewrite-host.go
Normal file
@@ -0,0 +1,91 @@
|
||||
// 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 tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/alibaba/higress/test/ingress/conformance/utils/http"
|
||||
"github.com/alibaba/higress/test/ingress/conformance/utils/suite"
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteRewritePath)
|
||||
}
|
||||
|
||||
var HTTPRouteRewriteHost = suite.ConformanceTest{
|
||||
ShortName: "HTTPRouteRewriteHost",
|
||||
Description: "A single Ingress in the higress-conformance-infra namespace uses the rewrite host",
|
||||
Manifests: []string{"tests/httproute-rewrite-host.yaml"},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testcases := []http.Assertion{
|
||||
{
|
||||
Request: http.AssertionRequest{
|
||||
ActualRequest: http.Request{
|
||||
Path: "/foo",
|
||||
Host: "foo.com",
|
||||
},
|
||||
ExpectedRequest: &http.ExpectedRequest{
|
||||
Request: http.Request{
|
||||
Host: "higress.io",
|
||||
Path: "/foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Response: http.AssertionResponse{
|
||||
ExpectedResponse: http.Response{
|
||||
StatusCode: 200,
|
||||
},
|
||||
},
|
||||
|
||||
Meta: http.AssertionMeta{
|
||||
TargetBackend: "infra-backend-v1",
|
||||
TargetNamespace: "higress-conformance-infra",
|
||||
},
|
||||
}, {
|
||||
Request: http.AssertionRequest{
|
||||
ActualRequest: http.Request{
|
||||
Path: "/foo",
|
||||
Host: "bar.com",
|
||||
},
|
||||
ExpectedRequest: &http.ExpectedRequest{
|
||||
Request: http.Request{
|
||||
Host: "higress.io",
|
||||
Path: "/foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Response: http.AssertionResponse{
|
||||
ExpectedResponse: http.Response{
|
||||
StatusCode: 200,
|
||||
},
|
||||
},
|
||||
|
||||
Meta: http.AssertionMeta{
|
||||
TargetBackend: "infra-backend-v2",
|
||||
TargetNamespace: "higress-conformance-infra",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("Rewrite HTTPRoute Host", func(t *testing.T) {
|
||||
for _, testcase := range testcases {
|
||||
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, testcase)
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
44
test/ingress/conformance/tests/httproute-rewrite-host.yaml
Normal file
44
test/ingress/conformance/tests/httproute-rewrite-host.yaml
Normal file
@@ -0,0 +1,44 @@
|
||||
# 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.
|
||||
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/upstream-vhost: "higress.io"
|
||||
name: httproute-rewrite-host
|
||||
namespace: higress-conformance-infra
|
||||
spec:
|
||||
ingressClassName: higress
|
||||
rules:
|
||||
- host: "foo.com"
|
||||
http:
|
||||
paths:
|
||||
- pathType: Prefix
|
||||
path: "/foo"
|
||||
backend:
|
||||
service:
|
||||
name: infra-backend-v1
|
||||
port:
|
||||
number: 8080
|
||||
- host: "bar.com"
|
||||
http:
|
||||
paths:
|
||||
- pathType: Prefix
|
||||
path: "/foo"
|
||||
backend:
|
||||
service:
|
||||
name: infra-backend-v2
|
||||
port:
|
||||
number: 8080
|
||||
56
test/ingress/conformance/tests/httproute-rewrite-path.go
Normal file
56
test/ingress/conformance/tests/httproute-rewrite-path.go
Normal file
@@ -0,0 +1,56 @@
|
||||
// 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 tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/alibaba/higress/test/ingress/conformance/utils/http"
|
||||
"github.com/alibaba/higress/test/ingress/conformance/utils/suite"
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteRewritePath)
|
||||
}
|
||||
|
||||
var HTTPRouteRewritePath = suite.ConformanceTest{
|
||||
ShortName: "HTTPRouteRewritePath",
|
||||
Description: "A single Ingress in the higress-conformance-infra namespace uses the rewrite path",
|
||||
Manifests: []string{"tests/httproute-rewrite-path.yaml"},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
|
||||
t.Run("Rewrite HTTPRoute Path", func(t *testing.T) {
|
||||
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, http.Assertion{
|
||||
Request: http.AssertionRequest{
|
||||
ActualRequest: http.Request{Path: "/svc/foo"},
|
||||
ExpectedRequest: &http.ExpectedRequest{
|
||||
Request: http.Request{Path: "/foo"},
|
||||
},
|
||||
},
|
||||
|
||||
Response: http.AssertionResponse{
|
||||
ExpectedResponse: http.Response{
|
||||
StatusCode: 200,
|
||||
},
|
||||
},
|
||||
|
||||
Meta: http.AssertionMeta{
|
||||
TargetBackend: "infra-backend-v1",
|
||||
TargetNamespace: "higress-conformance-infra",
|
||||
},
|
||||
})
|
||||
})
|
||||
},
|
||||
}
|
||||
33
test/ingress/conformance/tests/httproute-rewrite-path.yaml
Normal file
33
test/ingress/conformance/tests/httproute-rewrite-path.yaml
Normal file
@@ -0,0 +1,33 @@
|
||||
# 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.
|
||||
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/rewrite-target: "/$1"
|
||||
name: httproute-rewrite-path
|
||||
namespace: higress-conformance-infra
|
||||
spec:
|
||||
ingressClassName: higress
|
||||
rules:
|
||||
- http:
|
||||
paths:
|
||||
- pathType: ImplementationSpecific
|
||||
path: "/svc/(.*)"
|
||||
backend:
|
||||
service:
|
||||
name: infra-backend-v1
|
||||
port:
|
||||
number: 8080
|
||||
@@ -27,16 +27,25 @@ func init() {
|
||||
|
||||
var HTTPRouteSimpleSameNamespace = suite.ConformanceTest{
|
||||
ShortName: "HTTPRouteSimpleSameNamespace",
|
||||
Description: "A single Ingress in the higress-conformance-infra namespace attaches to a Gateway in the same namespace",
|
||||
Description: "A single Ingress in the higress-conformance-infra namespace demonstrates basic routing ability",
|
||||
Manifests: []string{"tests/httproute-simple-same-namespace.yaml"},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
|
||||
t.Run("Simple HTTP request should reach infra-backend", func(t *testing.T) {
|
||||
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, http.ExpectedResponse{
|
||||
Request: http.Request{Path: "/hello-world"},
|
||||
Response: http.Response{StatusCode: 200},
|
||||
Backend: "infra-backend-v1",
|
||||
Namespace: "higress-conformance-infra",
|
||||
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, http.Assertion{
|
||||
Meta: http.AssertionMeta{
|
||||
TargetBackend: "infra-backend-v1",
|
||||
TargetNamespace: "higress-conformance-infra",
|
||||
},
|
||||
Request: http.AssertionRequest{
|
||||
ActualRequest: http.Request{
|
||||
Path: "/hello-world",
|
||||
},
|
||||
},
|
||||
Response: http.AssertionResponse{
|
||||
ExpectedResponse: http.Response{
|
||||
StatusCode: 200,
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
@@ -25,32 +25,40 @@ import (
|
||||
"github.com/alibaba/higress/test/ingress/conformance/utils/roundtripper"
|
||||
)
|
||||
|
||||
// ExpectedResponse defines the response expected for a given request.
|
||||
type ExpectedResponse struct {
|
||||
// Request defines the request to make.
|
||||
Request Request
|
||||
type Assertion struct {
|
||||
Meta AssertionMeta
|
||||
Request AssertionRequest
|
||||
Response AssertionResponse
|
||||
}
|
||||
|
||||
type AssertionMeta struct {
|
||||
// TestCaseName is the User Given TestCase name
|
||||
TestCaseName string
|
||||
// TargetBackend defines the target backend service
|
||||
TargetBackend string
|
||||
// TargetNamespace defines the target backend namespace
|
||||
TargetNamespace string
|
||||
}
|
||||
|
||||
type AssertionRequest struct {
|
||||
// ActualRequest defines the request to make.
|
||||
ActualRequest Request
|
||||
|
||||
// ExpectedRequest defines the request that
|
||||
// is expected to arrive at the backend. If
|
||||
// not specified, the backend request will be
|
||||
// expected to match Request.
|
||||
ExpectedRequest *ExpectedRequest
|
||||
|
||||
RedirectRequest *roundtripper.RedirectRequest
|
||||
}
|
||||
|
||||
// BackendSetResponseHeaders is a set of headers
|
||||
// the echoserver should set in its response.
|
||||
BackendSetResponseHeaders map[string]string
|
||||
|
||||
// Response defines what response the test case
|
||||
type AssertionResponse struct {
|
||||
// ExpectedResponse defines what response the test case
|
||||
// should receive.
|
||||
Response Response
|
||||
|
||||
Backend string
|
||||
Namespace string
|
||||
|
||||
// User Given TestCase name
|
||||
TestCaseName string
|
||||
ExpectedResponse Response
|
||||
// AdditionalResponseHeaders is a set of headers
|
||||
// the echoserver should set in its response.
|
||||
AdditionalResponseHeaders map[string]string
|
||||
}
|
||||
|
||||
// Request can be used as both the request to make and a means to verify
|
||||
@@ -91,38 +99,38 @@ const requiredConsecutiveSuccesses = 3
|
||||
//
|
||||
// Once the request succeeds consistently with the response having the expected status code, make
|
||||
// additional assertions on the response body using the provided ExpectedResponse.
|
||||
func MakeRequestAndExpectEventuallyConsistentResponse(t *testing.T, r roundtripper.RoundTripper, timeoutConfig config.TimeoutConfig, gwAddr string, expected ExpectedResponse) {
|
||||
func MakeRequestAndExpectEventuallyConsistentResponse(t *testing.T, r roundtripper.RoundTripper, timeoutConfig config.TimeoutConfig, gwAddr string, expected Assertion) {
|
||||
t.Helper()
|
||||
|
||||
if expected.Request.Method == "" {
|
||||
expected.Request.Method = "GET"
|
||||
if expected.Request.ActualRequest.Method == "" {
|
||||
expected.Request.ActualRequest.Method = "GET"
|
||||
}
|
||||
|
||||
if expected.Response.StatusCode == 0 {
|
||||
expected.Response.StatusCode = 200
|
||||
if expected.Response.ExpectedResponse.StatusCode == 0 {
|
||||
expected.Response.ExpectedResponse.StatusCode = 200
|
||||
}
|
||||
|
||||
t.Logf("Making %s request to http://%s%s", expected.Request.Method, gwAddr, expected.Request.Path)
|
||||
t.Logf("Making %s request to http://%s%s", expected.Request.ActualRequest.Method, gwAddr, expected.Request.ActualRequest.Path)
|
||||
|
||||
path, query, _ := strings.Cut(expected.Request.Path, "?")
|
||||
path, query, _ := strings.Cut(expected.Request.ActualRequest.Path, "?")
|
||||
|
||||
req := roundtripper.Request{
|
||||
Method: expected.Request.Method,
|
||||
Host: expected.Request.Host,
|
||||
Method: expected.Request.ActualRequest.Method,
|
||||
Host: expected.Request.ActualRequest.Host,
|
||||
URL: url.URL{Scheme: "http", Host: gwAddr, Path: path, RawQuery: query},
|
||||
Protocol: "HTTP",
|
||||
Headers: map[string][]string{},
|
||||
UnfollowRedirect: expected.Request.UnfollowRedirect,
|
||||
UnfollowRedirect: expected.Request.ActualRequest.UnfollowRedirect,
|
||||
}
|
||||
|
||||
if expected.Request.Headers != nil {
|
||||
for name, value := range expected.Request.Headers {
|
||||
if expected.Request.ActualRequest.Headers != nil {
|
||||
for name, value := range expected.Request.ActualRequest.Headers {
|
||||
req.Headers[name] = []string{value}
|
||||
}
|
||||
}
|
||||
|
||||
backendSetHeaders := []string{}
|
||||
for name, val := range expected.BackendSetResponseHeaders {
|
||||
for name, val := range expected.Response.AdditionalResponseHeaders {
|
||||
backendSetHeaders = append(backendSetHeaders, name+":"+val)
|
||||
}
|
||||
req.Headers["X-Echo-Set-Header"] = []string{strings.Join(backendSetHeaders, ",")}
|
||||
@@ -170,7 +178,7 @@ func awaitConvergence(t *testing.T, threshold int, maxTimeToConsistency time.Dur
|
||||
// WaitForConsistentResponse repeats the provided request until it completes with a response having
|
||||
// the expected response consistently. The provided threshold determines how many times in
|
||||
// a row this must occur to be considered "consistent".
|
||||
func WaitForConsistentResponse(t *testing.T, r roundtripper.RoundTripper, req roundtripper.Request, expected ExpectedResponse, threshold int, maxTimeToConsistency time.Duration) {
|
||||
func WaitForConsistentResponse(t *testing.T, r roundtripper.RoundTripper, req roundtripper.Request, expected Assertion, threshold int, maxTimeToConsistency time.Duration) {
|
||||
awaitConvergence(t, threshold, maxTimeToConsistency, func(elapsed time.Duration) bool {
|
||||
cReq, cRes, err := r.CaptureRoundTrip(req)
|
||||
if err != nil {
|
||||
@@ -188,43 +196,43 @@ func WaitForConsistentResponse(t *testing.T, r roundtripper.RoundTripper, req ro
|
||||
t.Logf("Request passed")
|
||||
}
|
||||
|
||||
func CompareRequest(req *roundtripper.Request, cReq *roundtripper.CapturedRequest, cRes *roundtripper.CapturedResponse, expected ExpectedResponse) error {
|
||||
if expected.Response.StatusCode != cRes.StatusCode {
|
||||
return fmt.Errorf("expected status code to be %d, got %d", expected.Response.StatusCode, cRes.StatusCode)
|
||||
func CompareRequest(req *roundtripper.Request, cReq *roundtripper.CapturedRequest, cRes *roundtripper.CapturedResponse, expected Assertion) error {
|
||||
if expected.Response.ExpectedResponse.StatusCode != cRes.StatusCode {
|
||||
return fmt.Errorf("expected status code to be %d, got %d", expected.Response.ExpectedResponse.StatusCode, cRes.StatusCode)
|
||||
}
|
||||
if cRes.StatusCode == 200 {
|
||||
// The request expected to arrive at the backend is
|
||||
// the same as the request made, unless otherwise
|
||||
// specified.
|
||||
if expected.ExpectedRequest == nil {
|
||||
expected.ExpectedRequest = &ExpectedRequest{Request: expected.Request}
|
||||
if expected.Request.ExpectedRequest == nil {
|
||||
expected.Request.ExpectedRequest = &ExpectedRequest{Request: expected.Request.ActualRequest}
|
||||
}
|
||||
|
||||
if expected.ExpectedRequest.Method == "" {
|
||||
expected.ExpectedRequest.Method = "GET"
|
||||
if expected.Request.ExpectedRequest.Method == "" {
|
||||
expected.Request.ExpectedRequest.Method = "GET"
|
||||
}
|
||||
|
||||
if expected.ExpectedRequest.Host != "" && expected.ExpectedRequest.Host != cReq.Host {
|
||||
return fmt.Errorf("expected host to be %s, got %s", expected.ExpectedRequest.Host, cReq.Host)
|
||||
if expected.Request.ExpectedRequest.Host != "" && expected.Request.ExpectedRequest.Host != cReq.Host {
|
||||
return fmt.Errorf("expected host to be %s, got %s", expected.Request.ExpectedRequest.Host, cReq.Host)
|
||||
}
|
||||
|
||||
if expected.ExpectedRequest.Path != cReq.Path {
|
||||
return fmt.Errorf("expected path to be %s, got %s", expected.ExpectedRequest.Path, cReq.Path)
|
||||
if expected.Request.ExpectedRequest.Path != cReq.Path {
|
||||
return fmt.Errorf("expected path to be %s, got %s", expected.Request.ExpectedRequest.Path, cReq.Path)
|
||||
}
|
||||
if expected.ExpectedRequest.Method != cReq.Method {
|
||||
return fmt.Errorf("expected method to be %s, got %s", expected.ExpectedRequest.Method, cReq.Method)
|
||||
if expected.Request.ExpectedRequest.Method != cReq.Method {
|
||||
return fmt.Errorf("expected method to be %s, got %s", expected.Request.ExpectedRequest.Method, cReq.Method)
|
||||
}
|
||||
if expected.Namespace != cReq.Namespace {
|
||||
return fmt.Errorf("expected namespace to be %s, got %s", expected.Namespace, cReq.Namespace)
|
||||
if expected.Meta.TargetNamespace != cReq.Namespace {
|
||||
return fmt.Errorf("expected namespace to be %s, got %s", expected.Meta.TargetNamespace, cReq.Namespace)
|
||||
}
|
||||
if expected.ExpectedRequest.Headers != nil {
|
||||
if expected.Request.ExpectedRequest.Headers != nil {
|
||||
if cReq.Headers == nil {
|
||||
return fmt.Errorf("no headers captured, expected %v", len(expected.ExpectedRequest.Headers))
|
||||
return fmt.Errorf("no headers captured, expected %v", len(expected.Request.ExpectedRequest.Headers))
|
||||
}
|
||||
for name, val := range cReq.Headers {
|
||||
cReq.Headers[strings.ToLower(name)] = val
|
||||
}
|
||||
for name, expectedVal := range expected.ExpectedRequest.Headers {
|
||||
for name, expectedVal := range expected.Request.ExpectedRequest.Headers {
|
||||
actualVal, ok := cReq.Headers[strings.ToLower(name)]
|
||||
if !ok {
|
||||
return fmt.Errorf("expected %s header to be set, actual headers: %v", name, cReq.Headers)
|
||||
@@ -235,15 +243,15 @@ func CompareRequest(req *roundtripper.Request, cReq *roundtripper.CapturedReques
|
||||
}
|
||||
}
|
||||
|
||||
if expected.Response.Headers != nil {
|
||||
if expected.Response.ExpectedResponse.Headers != nil {
|
||||
if cRes.Headers == nil {
|
||||
return fmt.Errorf("no headers captured, expected %v", len(expected.ExpectedRequest.Headers))
|
||||
return fmt.Errorf("no headers captured, expected %v", len(expected.Request.ExpectedRequest.Headers))
|
||||
}
|
||||
for name, val := range cRes.Headers {
|
||||
cRes.Headers[strings.ToLower(name)] = val
|
||||
}
|
||||
|
||||
for name, expectedVal := range expected.Response.Headers {
|
||||
for name, expectedVal := range expected.Response.ExpectedResponse.Headers {
|
||||
actualVal, ok := cRes.Headers[strings.ToLower(name)]
|
||||
if !ok {
|
||||
return fmt.Errorf("expected %s header to be set, actual headers: %v", name, cRes.Headers)
|
||||
@@ -253,12 +261,12 @@ func CompareRequest(req *roundtripper.Request, cReq *roundtripper.CapturedReques
|
||||
}
|
||||
}
|
||||
|
||||
if len(expected.Response.AbsentHeaders) > 0 {
|
||||
if len(expected.Response.ExpectedResponse.AbsentHeaders) > 0 {
|
||||
for name, val := range cRes.Headers {
|
||||
cRes.Headers[strings.ToLower(name)] = val
|
||||
}
|
||||
|
||||
for _, name := range expected.Response.AbsentHeaders {
|
||||
for _, name := range expected.Response.ExpectedResponse.AbsentHeaders {
|
||||
val, ok := cRes.Headers[strings.ToLower(name)]
|
||||
if ok {
|
||||
return fmt.Errorf("expected %s header to not be set, got %s", name, val)
|
||||
@@ -268,12 +276,12 @@ func CompareRequest(req *roundtripper.Request, cReq *roundtripper.CapturedReques
|
||||
|
||||
// Verify that headers expected *not* to be present on the
|
||||
// request are actually not present.
|
||||
if len(expected.ExpectedRequest.AbsentHeaders) > 0 {
|
||||
if len(expected.Request.ExpectedRequest.AbsentHeaders) > 0 {
|
||||
for name, val := range cReq.Headers {
|
||||
cReq.Headers[strings.ToLower(name)] = val
|
||||
}
|
||||
|
||||
for _, name := range expected.ExpectedRequest.AbsentHeaders {
|
||||
for _, name := range expected.Request.ExpectedRequest.AbsentHeaders {
|
||||
val, ok := cReq.Headers[strings.ToLower(name)]
|
||||
if ok {
|
||||
return fmt.Errorf("expected %s header to not be set, got %s", name, val)
|
||||
@@ -281,74 +289,74 @@ func CompareRequest(req *roundtripper.Request, cReq *roundtripper.CapturedReques
|
||||
}
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(cReq.Pod, expected.Backend) {
|
||||
return fmt.Errorf("expected pod name to start with %s, got %s", expected.Backend, cReq.Pod)
|
||||
if !strings.HasPrefix(cReq.Pod, expected.Meta.TargetBackend) {
|
||||
return fmt.Errorf("expected pod name to start with %s, got %s", expected.Meta.TargetBackend, cReq.Pod)
|
||||
}
|
||||
} else if roundtripper.IsRedirect(cRes.StatusCode) {
|
||||
if expected.RedirectRequest == nil {
|
||||
if expected.Request.RedirectRequest == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
setRedirectRequestDefaults(req, cRes, &expected)
|
||||
|
||||
if expected.RedirectRequest.Host != cRes.RedirectRequest.Host {
|
||||
return fmt.Errorf("expected redirected hostname to be %s, got %s", expected.RedirectRequest.Host, cRes.RedirectRequest.Host)
|
||||
if expected.Request.RedirectRequest.Host != cRes.RedirectRequest.Host {
|
||||
return fmt.Errorf("expected redirected hostname to be %s, got %s", expected.Request.RedirectRequest.Host, cRes.RedirectRequest.Host)
|
||||
}
|
||||
|
||||
if expected.RedirectRequest.Port != cRes.RedirectRequest.Port {
|
||||
return fmt.Errorf("expected redirected port to be %s, got %s", expected.RedirectRequest.Port, cRes.RedirectRequest.Port)
|
||||
if expected.Request.RedirectRequest.Port != cRes.RedirectRequest.Port {
|
||||
return fmt.Errorf("expected redirected port to be %s, got %s", expected.Request.RedirectRequest.Port, cRes.RedirectRequest.Port)
|
||||
}
|
||||
|
||||
if expected.RedirectRequest.Scheme != cRes.RedirectRequest.Scheme {
|
||||
return fmt.Errorf("expected redirected scheme to be %s, got %s", expected.RedirectRequest.Scheme, cRes.RedirectRequest.Scheme)
|
||||
if expected.Request.RedirectRequest.Scheme != cRes.RedirectRequest.Scheme {
|
||||
return fmt.Errorf("expected redirected scheme to be %s, got %s", expected.Request.RedirectRequest.Scheme, cRes.RedirectRequest.Scheme)
|
||||
}
|
||||
|
||||
if expected.RedirectRequest.Path != cRes.RedirectRequest.Path {
|
||||
return fmt.Errorf("expected redirected path to be %s, got %s", expected.RedirectRequest.Path, cRes.RedirectRequest.Path)
|
||||
if expected.Request.RedirectRequest.Path != cRes.RedirectRequest.Path {
|
||||
return fmt.Errorf("expected redirected path to be %s, got %s", expected.Request.RedirectRequest.Path, cRes.RedirectRequest.Path)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get User-defined test case name or generate from expected response to a given request.
|
||||
func (er *ExpectedResponse) GetTestCaseName(i int) string {
|
||||
func (er *Assertion) GetTestCaseName(i int) string {
|
||||
|
||||
// If TestCase name is provided then use that or else generate one.
|
||||
if er.TestCaseName != "" {
|
||||
return er.TestCaseName
|
||||
if er.Meta.TestCaseName != "" {
|
||||
return er.Meta.TestCaseName
|
||||
}
|
||||
|
||||
headerStr := ""
|
||||
reqStr := ""
|
||||
|
||||
if er.Request.Headers != nil {
|
||||
if er.Request.ActualRequest.Headers != nil {
|
||||
headerStr = " with headers"
|
||||
}
|
||||
|
||||
reqStr = fmt.Sprintf("%d request to '%s%s'%s", i, er.Request.Host, er.Request.Path, headerStr)
|
||||
reqStr = fmt.Sprintf("%d request to '%s%s'%s", i, er.Request.ActualRequest.Host, er.Request.ActualRequest.Path, headerStr)
|
||||
|
||||
if er.Backend != "" {
|
||||
return fmt.Sprintf("%s should go to %s", reqStr, er.Backend)
|
||||
if er.Meta.TargetBackend != "" {
|
||||
return fmt.Sprintf("%s should go to %s", reqStr, er.Meta.TargetBackend)
|
||||
}
|
||||
return fmt.Sprintf("%s should receive a %d", reqStr, er.Response.StatusCode)
|
||||
return fmt.Sprintf("%s should receive a %d", reqStr, er.Response.ExpectedResponse.StatusCode)
|
||||
}
|
||||
|
||||
func setRedirectRequestDefaults(req *roundtripper.Request, cRes *roundtripper.CapturedResponse, expected *ExpectedResponse) {
|
||||
func setRedirectRequestDefaults(req *roundtripper.Request, cRes *roundtripper.CapturedResponse, expected *Assertion) {
|
||||
// If the expected host is nil it means we do not test host redirect.
|
||||
// In that case we are setting it to the one we got from the response because we do not know the ip/host of the gateway.
|
||||
if expected.RedirectRequest.Host == "" {
|
||||
expected.RedirectRequest.Host = cRes.RedirectRequest.Host
|
||||
if expected.Request.RedirectRequest.Host == "" {
|
||||
expected.Request.RedirectRequest.Host = cRes.RedirectRequest.Host
|
||||
}
|
||||
|
||||
if expected.RedirectRequest.Port == "" {
|
||||
expected.RedirectRequest.Port = req.URL.Port()
|
||||
if expected.Request.RedirectRequest.Port == "" {
|
||||
expected.Request.RedirectRequest.Port = req.URL.Port()
|
||||
}
|
||||
|
||||
if expected.RedirectRequest.Scheme == "" {
|
||||
expected.RedirectRequest.Scheme = req.URL.Scheme
|
||||
if expected.Request.RedirectRequest.Scheme == "" {
|
||||
expected.Request.RedirectRequest.Scheme = req.URL.Scheme
|
||||
}
|
||||
|
||||
if expected.RedirectRequest.Path == "" {
|
||||
expected.RedirectRequest.Path = req.URL.Path
|
||||
if expected.Request.RedirectRequest.Path == "" {
|
||||
expected.Request.RedirectRequest.Path = req.URL.Path
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,15 +39,8 @@ import (
|
||||
// them to the Kubernetes cluster.
|
||||
type Applier struct {
|
||||
NamespaceLabels map[string]string
|
||||
// ValidUniqueListenerPorts maps each listener port of each Gateway in the
|
||||
// manifests to a valid, unique port. There must be as many
|
||||
// ValidUniqueListenerPorts as there are listeners in the set of manifests.
|
||||
// For example, given two Gateways, each with 2 listeners, there should be
|
||||
// four ValidUniqueListenerPorts.
|
||||
// If empty or nil, ports are not modified.
|
||||
ValidUniqueListenerPorts []int
|
||||
|
||||
// IngressClass will be used as the spec.gatewayClassName when applying Gateway resources
|
||||
// IngressClass will be used as the spec.ingressClassName when applying Ingress resources
|
||||
IngressClass string
|
||||
}
|
||||
|
||||
|
||||
@@ -87,13 +87,6 @@ type Options struct {
|
||||
RoundTripper roundtripper.RoundTripper
|
||||
BaseManifests string
|
||||
NamespaceLabels map[string]string
|
||||
// ValidUniqueListenerPorts maps each listener port of each Gateway in the
|
||||
// manifests to a valid, unique port. There must be as many
|
||||
// ValidUniqueListenerPorts as there are listeners in the set of manifests.
|
||||
// For example, given two Gateways, each with 2 listeners, there should be
|
||||
// four ValidUniqueListenerPorts.
|
||||
// If empty or nil, ports are not modified.
|
||||
ValidUniqueListenerPorts []int
|
||||
|
||||
// CleanupBaseResources indicates whether or not the base test
|
||||
// resources such as Gateways should be cleaned up after the run.
|
||||
@@ -130,8 +123,7 @@ func New(s Options) *ConformanceTestSuite {
|
||||
BaseManifests: s.BaseManifests,
|
||||
GatewayAddress: s.GatewayAddress,
|
||||
Applier: kubernetes.Applier{
|
||||
NamespaceLabels: s.NamespaceLabels,
|
||||
ValidUniqueListenerPorts: s.ValidUniqueListenerPorts,
|
||||
NamespaceLabels: s.NamespaceLabels,
|
||||
},
|
||||
SupportedFeatures: s.SupportedFeatures,
|
||||
TimeoutConfig: s.TimeoutConfig,
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
//go:build conformance
|
||||
// +build conformance
|
||||
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -21,16 +18,15 @@ import (
|
||||
"flag"
|
||||
"testing"
|
||||
|
||||
"github.com/alibaba/higress/test/ingress/conformance/tests"
|
||||
"github.com/alibaba/higress/test/ingress/conformance/utils/flags"
|
||||
"github.com/alibaba/higress/test/ingress/conformance/utils/suite"
|
||||
"github.com/stretchr/testify/require"
|
||||
v1 "k8s.io/api/networking/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
)
|
||||
|
||||
var useUniquePorts = flag.Bool("use-unique-ports", true, "whether to use unique ports")
|
||||
"github.com/alibaba/higress/test/ingress/conformance/tests"
|
||||
"github.com/alibaba/higress/test/ingress/conformance/utils/flags"
|
||||
"github.com/alibaba/higress/test/ingress/conformance/utils/suite"
|
||||
)
|
||||
|
||||
func TestHigressConformanceTests(t *testing.T) {
|
||||
flag.Parse()
|
||||
@@ -43,27 +39,23 @@ func TestHigressConformanceTests(t *testing.T) {
|
||||
|
||||
require.NoError(t, v1.AddToScheme(client.Scheme()))
|
||||
|
||||
validUniqueListenerPorts := []int{
|
||||
80,
|
||||
443,
|
||||
}
|
||||
|
||||
if !*useUniquePorts {
|
||||
validUniqueListenerPorts = []int{}
|
||||
}
|
||||
|
||||
cSuite := suite.New(suite.Options{
|
||||
Client: client,
|
||||
IngressClassName: *flags.IngressClassName,
|
||||
Debug: *flags.ShowDebug,
|
||||
CleanupBaseResources: *flags.CleanupBaseResources,
|
||||
ValidUniqueListenerPorts: validUniqueListenerPorts,
|
||||
SupportedFeatures: map[suite.SupportedFeature]bool{},
|
||||
GatewayAddress: "localhost",
|
||||
Client: client,
|
||||
IngressClassName: *flags.IngressClassName,
|
||||
Debug: *flags.ShowDebug,
|
||||
CleanupBaseResources: *flags.CleanupBaseResources,
|
||||
SupportedFeatures: map[suite.SupportedFeature]bool{},
|
||||
GatewayAddress: "localhost",
|
||||
})
|
||||
|
||||
cSuite.Setup(t)
|
||||
higressTests := []suite.ConformanceTest{
|
||||
tests.HTTPRouteSimpleSameNamespace,
|
||||
tests.HTTPRouteHostNameSameNamespace,
|
||||
tests.HTTPRouteRewritePath,
|
||||
tests.HTTPRouteRewriteHost,
|
||||
tests.HTTPRouteCanaryHeader,
|
||||
}
|
||||
|
||||
cSuite.Run(t, higressTests)
|
||||
}
|
||||
|
||||
BIN
test/ingress/pipeline.png
Normal file
BIN
test/ingress/pipeline.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 359 KiB |
Reference in New Issue
Block a user