refactor: e2e infras (#407)

Signed-off-by: bitliu <bitliu@tencent.com>
This commit is contained in:
Xunzhuo
2023-06-30 17:28:10 +08:00
committed by GitHub
parent 81e467b624
commit 3fd37abab7
73 changed files with 324 additions and 445 deletions

View File

@@ -0,0 +1,364 @@
# 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.
# This file contains the base resources that most conformance tests will rely
# on. This includes 3 namespaces along with Gateways, Services and Deployments
# that can be used as backends for routing traffic. The most important
# resources included are the Gateways (all in the higress-conformance-infra
# namespace):
# - same-namespace (only supports route in same ns)
# - all-namespaces (supports routes in all ns)
# - backend-namespaces (supports routes in ns with backend label)
apiVersion: v1
kind: Namespace
metadata:
name: higress-conformance-infra
labels:
higress-conformance: infra
---
apiVersion: v1
kind: Service
metadata:
name: infra-backend-v1
namespace: higress-conformance-infra
spec:
selector:
app: infra-backend-v1
ports:
- protocol: TCP
port: 8080
targetPort: 3000
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: infra-backend-v1
namespace: higress-conformance-infra
labels:
app: infra-backend-v1
spec:
replicas: 2
selector:
matchLabels:
app: infra-backend-v1
template:
metadata:
labels:
app: infra-backend-v1
spec:
containers:
- name: infra-backend-v1
# From https://github.com/kubernetes-sigs/ingress-controller-conformance/tree/master/images/echoserver
image: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/echoserver:v20221109-7ee2f3e
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
resources:
requests:
cpu: 10m
---
apiVersion: v1
kind: Service
metadata:
name: infra-backend-v2
namespace: higress-conformance-infra
spec:
selector:
app: infra-backend-v2
ports:
- protocol: TCP
port: 8080
targetPort: 3000
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: infra-backend-v2
namespace: higress-conformance-infra
labels:
app: infra-backend-v2
spec:
replicas: 2
selector:
matchLabels:
app: infra-backend-v2
template:
metadata:
labels:
app: infra-backend-v2
spec:
containers:
- name: infra-backend-v2
image: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/echoserver:v20221109-7ee2f3e
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
resources:
requests:
cpu: 10m
---
apiVersion: v1
kind: Service
metadata:
name: infra-backend-v3
namespace: higress-conformance-infra
spec:
selector:
app: infra-backend-v3
ports:
- protocol: TCP
port: 8080
targetPort: 3000
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: infra-backend-v3
namespace: higress-conformance-infra
labels:
app: infra-backend-v3
spec:
replicas: 2
selector:
matchLabels:
app: infra-backend-v3
template:
metadata:
labels:
app: infra-backend-v3
spec:
containers:
- name: infra-backend-v3
image: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/echoserver:v20221109-7ee2f3e
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
resources:
requests:
cpu: 10m
---
apiVersion: v1
kind: Namespace
metadata:
name: higress-conformance-app-backend
labels:
higress-conformance: backend
---
apiVersion: v1
kind: Service
metadata:
name: app-backend-v1
namespace: higress-conformance-app-backend
spec:
selector:
app: app-backend-v1
ports:
- protocol: TCP
port: 8080
targetPort: 3000
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-backend-v1
namespace: higress-conformance-app-backend
labels:
app: app-backend-v1
spec:
replicas: 2
selector:
matchLabels:
app: app-backend-v1
template:
metadata:
labels:
app: app-backend-v1
spec:
containers:
- name: app-backend-v1
image: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/echoserver:v20221109-7ee2f3e
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
resources:
requests:
cpu: 10m
---
apiVersion: v1
kind: Service
metadata:
name: app-backend-v2
namespace: higress-conformance-app-backend
spec:
selector:
app: app-backend-v2
ports:
- protocol: TCP
port: 8080
targetPort: 3000
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-backend-v2
namespace: higress-conformance-app-backend
labels:
app: app-backend-v2
spec:
replicas: 2
selector:
matchLabels:
app: app-backend-v2
template:
metadata:
labels:
app: app-backend-v2
spec:
containers:
- name: app-backend-v2
image: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/echoserver:v20221109-7ee2f3e
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
resources:
requests:
cpu: 10m
---
apiVersion: v1
kind: Namespace
metadata:
name: higress-conformance-web-backend
labels:
higress-conformance: backend
---
apiVersion: v1
kind: Service
metadata:
name: web-backend
namespace: higress-conformance-web-backend
spec:
selector:
app: web-backend
ports:
- protocol: TCP
port: 8080
targetPort: 3000
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-backend
namespace: higress-conformance-web-backend
labels:
app: web-backend
spec:
replicas: 2
selector:
matchLabels:
app: web-backend
template:
metadata:
labels:
app: web-backend
spec:
containers:
- name: web-backend
image: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/echoserver:v20221109-7ee2f3e
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
resources:
requests:
cpu: 10m
---
apiVersion: v1
kind: Pod
metadata:
name: nacos-standlone-rc3
namespace: higress-conformance-app-backend
labels:
name: nacos-standlone-rc3
spec:
containers:
- name: nacos-standlone-rc3
image: registry.cn-hangzhou.aliyuncs.com/hinsteny/nacos-standlone-rc3:1.0.0-RC3
ports:
- containerPort: 8848
---
apiVersion: v1
kind: Service
metadata:
name: nacos-standlone-rc3-service
namespace: higress-conformance-app-backend
spec:
selector:
name: nacos-standlone-rc3
clusterIP: None
ports:
- name: foo # name is not required for single-port Services
port: 8848
---
apiVersion: v1
kind: Pod
metadata:
name: dubbo-demo-provider
namespace: higress-conformance-app-backend
spec:
containers:
- name: dubbo-demo-provider
image: registry.cn-hangzhou.aliyuncs.com/hinsteny/dubbo-provider-demo:0.0.2
env:
- name: NACOS_K8S_NAMESPACE
value: higress-conformance-app-backend
- name: DUBBO_GROUP
value: dev
ports:
- containerPort: 20880

View File

@@ -0,0 +1,20 @@
// 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 ingress
import "embed"
//go:embed tests/* base/*
var Manifests embed.FS

View File

@@ -0,0 +1,65 @@
// 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/e2e/conformance/utils/http"
"github.com/alibaba/higress/test/e2e/conformance/utils/roundtripper"
"github.com/alibaba/higress/test/e2e/conformance/utils/suite"
)
func init() {
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteAppRoot)
}
var HTTPRouteAppRoot = suite.ConformanceTest{
ShortName: "HTTPRouteAppRoot",
Description: "The Ingress in the higress-conformance-infra namespace uses the app root.",
Manifests: []string{"tests/httproute-app-root.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{
Host: "foo.com",
Path: "/",
UnfollowRedirect: true,
},
RedirectRequest: &roundtripper.RedirectRequest{
Scheme: "http",
Host: "foo.com",
Path: "/foo",
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 302,
},
},
},
}
t.Run("HTTPRoute app root", func(t *testing.T) {
for _, testcase := range testcases {
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, testcase)
}
})
},
}

View File

@@ -0,0 +1,34 @@
# 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/app-root: "/foo"
name: httproute-app-root
namespace: higress-conformance-infra
spec:
ingressClassName: higress
rules:
- host: "foo.com"
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: infra-backend-v1
port:
number: 8080

View File

@@ -0,0 +1,133 @@
// 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/e2e/conformance/utils/http"
"github.com/alibaba/higress/test/e2e/conformance/utils/suite"
)
func init() {
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteCanaryHeaderWithCustomizedHeader)
}
var HTTPRouteCanaryHeaderWithCustomizedHeader = suite.ConformanceTest{
ShortName: "HTTPRouteCanaryHeaderWithCustomizedHeader",
Description: "The Ingress in the higress-conformance-infra namespace uses the canary header traffic split when same host and path but different header",
Manifests: []string{"tests/httproute-canary-header-with-customized-header.yaml"},
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
testcases := []http.Assertion{
{
// test canary ingress with different customized header
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: 404,
},
},
}, {
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/echo",
Host: "canary.higress.io",
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 404,
},
},
},
{
Meta: http.AssertionMeta{
TargetBackend: "infra-backend-v2",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/echo",
Host: "canary.higress.io",
Headers: map[string]string{
"abc": "123",
},
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 200,
},
},
},
// test canary ingress with same customized header
{
Meta: http.AssertionMeta{
TargetBackend: "infra-backend-v1",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/echo",
Host: "same.canary.higress.io",
Headers: map[string]string{
"with-same-customized-header": "true",
"user": "higress",
},
},
},
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: "same.canary.higress.io",
Headers: map[string]string{
"user": "higress",
},
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 200,
},
},
},
}
t.Run("Canary HTTPRoute Traffic Split With customized header", func(t *testing.T) {
for _, testcase := range testcases {
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, testcase)
}
})
},
}

View File

@@ -0,0 +1,107 @@
# 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.
# ingress-canary-header-with-customized-header-01 and -02 is to test canary ingress can't match ingress with different customized header
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-canary-header-with-customized-header-01
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:
annotations:
higress.io/exact-match-header-abc: "123"
name: ingress-canary-header-with-customized-header-02
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
---
# ingress-canary-header-with-customized-header-03 and -04 is to test canary ingress can match ingress with same customized header
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-by-header: "with-same-customized-header"
nginx.ingress.kubernetes.io/canary-by-header-value: "true"
higress.io/exact-match-header-user: "higress"
name: ingress-canary-header-with-customized-header-03
namespace: higress-conformance-infra
spec:
ingressClassName: higress
rules:
- host: same.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:
annotations:
higress.io/exact-match-header-user: "higress"
name: ingress-canary-header-with-customized-header-04
namespace: higress-conformance-infra
spec:
ingressClassName: higress
rules:
- host: same.canary.higress.io
http:
paths:
- path: /echo
pathType: Exact
backend:
service:
name: infra-backend-v2
port:
number: 8080

View File

@@ -0,0 +1,144 @@
// 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/e2e/conformance/utils/http"
"github.com/alibaba/higress/test/e2e/conformance/utils/suite"
)
func init() {
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteCanaryHeader)
}
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,
},
},
}, {
Meta: http.AssertionMeta{
TargetBackend: "infra-backend-v2",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/foo",
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-v1",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/foo/bar",
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-v3",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/foo",
Host: "canary.higress.io",
},
},
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: "/foo/bar",
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)
}
})
},
}

View File

@@ -0,0 +1,111 @@
# 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
---
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-prefix
namespace: higress-conformance-infra
spec:
ingressClassName: higress
rules:
- host: canary.higress.io
http:
paths:
- path: /foo
pathType: Exact
backend:
service:
name: infra-backend-v2
port:
number: 8080
- path: /foo
pathType: Prefix
backend:
service:
name: infra-backend-v1
port:
number: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-echo-prefix
namespace: higress-conformance-infra
spec:
ingressClassName: higress
rules:
- host: canary.higress.io
http:
paths:
- path: /foo
pathType: Exact
backend:
service:
name: infra-backend-v3
port:
number: 8080
- path: /foo
pathType: Prefix
backend:
service:
name: infra-backend-v3
port:
number: 8080

View File

@@ -0,0 +1,78 @@
// 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/e2e/conformance/utils/http"
"github.com/alibaba/higress/test/e2e/conformance/utils/suite"
)
func init() {
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteCanaryWeight)
}
var HTTPRouteCanaryWeight = suite.ConformanceTest{
ShortName: "HTTPRouteCanaryWeight",
Description: "The Ingress in the higress-conformance-infra namespace uses the canary weight traffic split.",
Manifests: []string{"tests/httproute-canary-weight.yaml"},
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
testcases := []http.Assertion{
// test if the weight is 0
{
Meta: http.AssertionMeta{
TargetBackend: "infra-backend-v1",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/weight-0",
Host: "canary.higress.io",
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 200,
},
},
},
// test if the weight is 100
{
Meta: http.AssertionMeta{
TargetBackend: "infra-backend-v2",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/weight-100",
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)
}
})
},
}

View File

@@ -0,0 +1,97 @@
# 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.
# Test if the weight is 0
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "0"
name: ingress-echo-canary-weight-0
namespace: higress-conformance-infra
spec:
ingressClassName: higress
rules:
- host: canary.higress.io
http:
paths:
- path: /weight-0
pathType: Exact
backend:
service:
name: infra-backend-v3
port:
number: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-echo-weight-0
namespace: higress-conformance-infra
spec:
ingressClassName: higress
rules:
- host: canary.higress.io
http:
paths:
- path: /weight-0
pathType: Exact
backend:
service:
name: infra-backend-v1
port:
number: 8080
---
# Test if the weight is 100
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "100"
name: ingress-echo-canary-weight-100
namespace: higress-conformance-infra
spec:
ingressClassName: higress
rules:
- host: canary.higress.io
http:
paths:
- path: /weight-100
pathType: Exact
backend:
service:
name: infra-backend-v2
port:
number: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-echo-weight-100
namespace: higress-conformance-infra
spec:
ingressClassName: higress
rules:
- host: canary.higress.io
http:
paths:
- path: /weight-100
pathType: Exact
backend:
service:
name: infra-backend-v1
port:
number: 8080

View File

@@ -0,0 +1,206 @@
// 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 (
"crypto/tls"
"testing"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/alibaba/higress/test/e2e/conformance/utils/cert"
"github.com/alibaba/higress/test/e2e/conformance/utils/http"
"github.com/alibaba/higress/test/e2e/conformance/utils/kubernetes"
"github.com/alibaba/higress/test/e2e/conformance/utils/suite"
)
func init() {
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteDownstreamEncryption)
}
var HTTPRouteDownstreamEncryption = suite.ConformanceTest{
ShortName: "HTTPRouteDownstreamEncryption",
Description: "A single Ingress in the higress-conformance-infra namespace for downstream encryption.",
Manifests: []string{"tests/httproute-downstream-encryption.yaml"},
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
// Prepare certificates and secrets for testcases
caCertOut, _, caCert, caKey := cert.MustGenerateCaCert(t)
svcCertOut, svcKeyOut := cert.MustGenerateCertWithCA(t, cert.ServerCertType, caCert, caKey, []string{"foo.com"})
cliCertOut, cliKeyOut := cert.MustGenerateCertWithCA(t, cert.ClientCertType, caCert, caKey, nil)
fooSecret := kubernetes.ConstructTLSSecret("higress-conformance-infra", "foo-secret", svcCertOut.Bytes(), svcKeyOut.Bytes())
fooSecretCACert := kubernetes.ConstructCASecret("higress-conformance-infra", "foo-secret-cacert", caCertOut.Bytes())
suite.Applier.MustApplyObjectsWithCleanup(t, suite.Client, suite.TimeoutConfig, []client.Object{fooSecret, fooSecretCACert}, suite.Cleanup)
testcases := []http.Assertion{
{
Meta: http.AssertionMeta{
TestCaseName: "case 1: auth-tls-secret annotation",
TargetBackend: "infra-backend-v1",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/foo1",
Host: "foo1.com",
TLSConfig: &http.TLSConfig{
SNI: "foo1.com",
Certificates: http.Certificates{
CACerts: [][]byte{caCertOut.Bytes()},
ClientKeyPairs: []http.ClientKeyPair{{
ClientCert: cliCertOut.Bytes(),
ClientKey: cliKeyOut.Bytes()},
},
},
},
},
ExpectedRequest: &http.ExpectedRequest{
Request: http.Request{
Path: "/foo1",
Host: "foo1.com",
},
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 200,
},
},
},
{
Meta: http.AssertionMeta{
TestCaseName: "case 2: ssl-cipher annotation, ingress of one cipher suite",
TargetBackend: "infra-backend-v2",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/foo2",
Host: "foo2.com",
TLSConfig: &http.TLSConfig{
SNI: "foo2.com",
MaxVersion: tls.VersionTLS12,
CipherSuites: []uint16{tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
Certificates: http.Certificates{
CACerts: [][]byte{caCertOut.Bytes()},
ClientKeyPairs: []http.ClientKeyPair{{
ClientCert: cliCertOut.Bytes(),
ClientKey: cliKeyOut.Bytes()},
},
},
},
},
ExpectedRequest: &http.ExpectedRequest{
Request: http.Request{
Path: "/foo2",
Host: "foo2.com",
},
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 200,
},
},
},
{
Meta: http.AssertionMeta{
TestCaseName: "case 3: ssl-cipher annotation, ingress of multiple cipher suites",
TargetBackend: "infra-backend-v3",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/foo3",
Host: "foo3.com",
TLSConfig: &http.TLSConfig{
SNI: "foo3.com",
MaxVersion: tls.VersionTLS12,
CipherSuites: []uint16{tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305},
Certificates: http.Certificates{
CACerts: [][]byte{caCertOut.Bytes()},
ClientKeyPairs: []http.ClientKeyPair{{
ClientCert: cliCertOut.Bytes(),
ClientKey: cliKeyOut.Bytes()},
},
},
},
},
ExpectedRequest: &http.ExpectedRequest{
Request: http.Request{
Path: "/foo3",
Host: "foo3.com",
},
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 200,
},
},
},
{
Meta: http.AssertionMeta{
TestCaseName: "case 4: ssl-cipher annotation, TLSv1.2 cipher suites are invalid in TLSv1.3",
TargetBackend: "infra-backend-v3",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/foo3",
Host: "foo3.com",
TLSConfig: &http.TLSConfig{
SNI: "foo3.com",
MinVersion: tls.VersionTLS13,
MaxVersion: tls.VersionTLS13,
CipherSuites: []uint16{tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384},
Certificates: http.Certificates{
CACerts: [][]byte{caCertOut.Bytes()},
ClientKeyPairs: []http.ClientKeyPair{{
ClientCert: cliCertOut.Bytes(),
ClientKey: cliKeyOut.Bytes()},
},
},
},
},
ExpectedRequest: &http.ExpectedRequest{
Request: http.Request{
Path: "/foo3",
Host: "foo3.com",
},
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 200,
},
},
},
}
t.Run("Downstream encryption", func(t *testing.T) {
for _, testcase := range testcases {
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, testcase)
}
})
},
}

View File

@@ -0,0 +1,90 @@
# 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:
higress.io/auth-tls-secret: foo-secret-cacert
name: httproute-downstream-encryption-auth
namespace: higress-conformance-infra
spec:
ingressClassName: higress
tls:
- hosts:
- "foo1.com"
secretName: foo-secret
rules:
- host: "foo1.com"
http:
paths:
- pathType: Exact
path: "/foo1"
backend:
service:
name: infra-backend-v1
port:
number: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
higress.io/ssl-cipher: ECDHE-RSA-AES128-SHA
higress.io/auth-tls-secret: foo-secret-cacert
name: httproute-downstream-encryption-cipher-1
namespace: higress-conformance-infra
spec:
ingressClassName: higress
tls:
- hosts:
- "foo2.com"
secretName: foo-secret
rules:
- host: "foo2.com"
http:
paths:
- pathType: Exact
path: "/foo2"
backend:
service:
name: infra-backend-v2
port:
number: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
higress.io/ssl-cipher: ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES128-SHA,ECDHE-ECDSA-AES256-SHA
higress.io/auth-tls-secret: foo-secret-cacert
name: httproute-downstream-encryption-cipher-2
namespace: higress-conformance-infra
spec:
ingressClassName: higress
tls:
- hosts:
- "foo3.com"
secretName: foo-secret
rules:
- host: "foo3.com"
http:
paths:
- pathType: Exact
path: "/foo3"
backend:
service:
name: infra-backend-v3
port:
number: 8080

View File

@@ -0,0 +1,124 @@
// 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/e2e/conformance/utils/http"
"github.com/alibaba/higress/test/e2e/conformance/utils/suite"
)
func init() {
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteEnableCors)
}
var HTTPRouteEnableCors = suite.ConformanceTest{
ShortName: "HTTPRouteEnableCors",
Description: "A single Ingress in the higress-conformance-infra namespace demonstrates enable cors ability.",
Manifests: []string{"tests/httproute-enable-cors.yaml"},
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
testcases := []http.Assertion{
{
Meta: http.AssertionMeta{
TestCaseName: "case1: unable cors",
TargetBackend: "infra-backend-v1",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/foo",
Host: "foo1.com",
Method: "OPTIONS",
Headers: map[string]string{"Origin": "http://bar.com"},
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 200,
AbsentHeaders: []string{"Access-Control-Allow-Credentials", "Access-Control-Allow-Origin"},
},
},
}, {
Meta: http.AssertionMeta{
TestCaseName: "case2: enable cors",
TargetBackend: "infra-backend-v2",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/foo",
Host: "foo2.com",
Method: "OPTIONS",
Headers: map[string]string{"Origin": "http://bar.com"},
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 200,
Headers: map[string]string{"Access-Control-Allow-Credentials": "true", "Access-Control-Allow-Origin": "http://bar.com"},
},
},
}, {
Meta: http.AssertionMeta{
TestCaseName: "case3: enable cors and allow origin headers",
TargetBackend: "infra-backend-v3",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/foo",
Host: "foo3.com",
Method: "OPTIONS",
Headers: map[string]string{"Origin": "http://bar.com"},
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 200,
Headers: map[string]string{"Access-Control-Allow-Credentials": "true", "Access-Control-Allow-Origin": "http://bar.com", "Access-Control-Expose-Headers": "*"},
},
},
}, {
Meta: http.AssertionMeta{
TestCaseName: "case4: enable cors and use forbidden Origin",
TargetBackend: "infra-backend-v3",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/foo",
Host: "foo3.com",
Method: "OPTIONS",
Headers: map[string]string{"Origin": "http://foo.com"},
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 200,
AbsentHeaders: []string{"Access-Control-Allow-Credentials", "Access-Control-Allow-Origin", "Access-Control-Expose-Headers"},
},
},
},
}
t.Run("Enable Cors Cases Split", func(t *testing.T) {
for _, testcase := range testcases {
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, testcase)
}
})
},
}

View File

@@ -0,0 +1,82 @@
# 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: higress-conformance-infra-unable-cors-test
namespace: higress-conformance-infra
annotations:
nginx.ingress.kubernetes.io/enable-cors: "false"
spec:
ingressClassName: higress
rules:
- host: "foo1.com"
http:
paths:
- pathType: Prefix
path: "/foo"
backend:
service:
name: infra-backend-v1
port:
number: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: higress-conformance-infra-enable-cors-test
namespace: higress-conformance-infra
annotations:
nginx.ingress.kubernetes.io/enable-cors: "true"
spec:
ingressClassName: higress
rules:
- host: "foo2.com"
http:
paths:
- pathType: Prefix
path: "/foo"
backend:
service:
name: infra-backend-v2
port:
number: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: higress-conformance-infra-enable-cors-config-test
namespace: higress-conformance-infra
annotations:
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-methods: "POST"
nginx.ingress.kubernetes.io/cors-allow-headers: "Host,Origin"
nginx.ingress.kubernetes.io/cors-expose-headers: "*"
nginx.ingress.kubernetes.io/cors-allow-origin: "http://bar.com"
nginx.ingress.kubernetes.io/cors-allow-credentials: "true"
nginx.ingress.kubernetes.io/cors-max-age: "60"
spec:
ingressClassName: higress
rules:
- host: "foo3.com"
http:
paths:
- pathType: Prefix
path: "/foo"
backend:
service:
name: infra-backend-v3
port:
number: 8080

View File

@@ -0,0 +1,112 @@
// 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/e2e/conformance/utils/http"
"github.com/alibaba/higress/test/e2e/conformance/utils/suite"
)
func init() {
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteEnableIgnoreCase)
}
var HTTPRouteEnableIgnoreCase = suite.ConformanceTest{
ShortName: "HTTPRouteEnableIgnoreCase",
Description: "A Ingress in the higress-conformance-infra namespace that ignores URI case in HTTP match.",
Manifests: []string{"tests/httproute-enable-ignore-case.yaml"},
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
testcases := []http.Assertion{
{
Meta: http.AssertionMeta{
TestCaseName: "case1: normal request",
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{
TestCaseName: "case2: enable ignoreCase",
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{
TestCaseName: "case3: enable ignoreCase",
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{
TestCaseName: "case4: enable ignoreCase",
TargetBackend: "infra-backend-v3",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/CAT/ok",
Host: "foo.com",
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 200,
},
},
},
}
t.Run("Enable IgnoreCase Cases Split", func(t *testing.T) {
for _, testcase := range testcases {
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, testcase)
}
})
},
}

View File

@@ -0,0 +1,48 @@
# 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:
higress.io/ignore-path-case: "true"
name: httproute-ignore-case-match
namespace: higress-conformance-infra
spec:
ingressClassName: higress
rules:
- host: "foo.com"
http:
paths:
- pathType: Exact
path: "/foo"
backend:
service:
name: infra-backend-v1
port:
number: 8080
- pathType: Prefix
path: "/bar"
backend:
service:
name: infra-backend-v2
port:
number: 8080
- pathType: Prefix
path: "/cat/"
backend:
service:
name: infra-backend-v3
port:
number: 8080

View File

@@ -0,0 +1,67 @@
// 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/e2e/conformance/utils/http"
"github.com/alibaba/higress/test/e2e/conformance/utils/roundtripper"
"github.com/alibaba/higress/test/e2e/conformance/utils/suite"
)
func init() {
HigressConformanceTests = append(HigressConformanceTests, HttpForceRedirectHttps)
}
var HttpForceRedirectHttps = suite.ConformanceTest{
ShortName: "HttpForceRedirectHttps",
Description: " The ingress in the higress-conformance-infra namespace enforces server-side HTTPS with forced redirection.",
Manifests: []string{"tests/httproute-force-redirect-https.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{
Host: "test.com",
Path: "/test",
UnfollowRedirect: true,
},
RedirectRequest: &roundtripper.RedirectRequest{
Scheme: "https",
Host: "test.com",
Path: "/test",
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 308,
},
},
},
}
t.Run("HttpForceRedirectHttps", func(t *testing.T) {
for _, testcase := range testcases {
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, testcase)
}
})
},
}

View File

@@ -0,0 +1,49 @@
# 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/force-ssl-redirect: "true"
name: http-redirect-as-https
namespace: higress-conformance-infra
spec:
ingressClassName: higress
tls:
- hosts:
- "test.com"
secretName: my-app-tls-secret
rules:
- host: "test.com"
http:
paths:
- pathType: Prefix
path: "/test"
backend:
service:
name: infra-backend-v1
port:
number: 8080
---
apiVersion: v1
kind: Secret
metadata:
name: my-app-tls-secret
namespace: higress-conformance-infra
type: kubernetes.io/tls
data:
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUQxekNDQXIrZ0F3SUJBZ0lVWXh4dE1Ia0tIQXpxM25yUG0rd0Y2UEtNdmw4d0RRWUpLb1pJaHZjTkFRRUwKQlFBd2V6RUxNQWtHQTFVRUJoTUNRMDR4Q3pBSkJnTlZCQWdNQWxOWU1Rc3dDUVlEVlFRSERBSllRVEVOTUFzRwpBMVVFQ2d3RVdGVlFWREVUTUJFR0ExVUVDd3dLVEVsT1ZWaEhVazlWVURFTU1Bb0dBMVVFQXd3RFJsbFVNU0F3CkhnWUpLb1pJaHZjTkFRa0JGaEZtWjNrNE9UTTJRR2R0WVdsc0xtTnZiVEFlRncweU16QTFNRGd4TkRVM016UmEKRncweU5EQTFNRGN4TkRVM016UmFNSHN4Q3pBSkJnTlZCQVlUQWtOT01Rc3dDUVlEVlFRSURBSlRXREVMTUFrRwpBMVVFQnd3Q1dFRXhEVEFMQmdOVkJBb01CRmhWVUZReEV6QVJCZ05WQkFzTUNreEpUbFZZUjFKUFZWQXhEREFLCkJnTlZCQU1NQTBaWlZERWdNQjRHQ1NxR1NJYjNEUUVKQVJZUlptZDVPRGt6TmtCbmJXRnBiQzVqYjIwd2dnRWkKTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFEUEVHaDJxeFpFMmpJUnMvNkFXYkI2b3oxZQpic0c4enE0YWxLTzVUcjgzWFlrUzhOa2g4UkdiU1l3ODlVR3NBWmZ0WkFqT0M2Mml1aEVOUTUzZjhwTmoySWQ1Ck9PNGVhVDN6bndKQ0xGMmRHcThRZE90c1RjU09FZE11N2dORWVOZkxVeWRFNitnYjcxSi9PRkNlZTlQM1dWWWgKQ05adG1nYWcyWm0wQUZxT0F2b1hUV3lGdDBzWEYyVG90VENnWFhNM1kydmdCY3JRMHRTbllHZmVqOVRUcmpENgpGQTBTYmFlL0F6Y001cC9FNmdKNWFXREhLekY5c2lvOHRUOUZuN1Fzb3djR1BSTElOL1o3OGxhaEZITGpsVFBtCmZqUEFmdWVUWVYzY05ZNXRGNjZlV1duazI0WG4vTEFaSlhHU0hXRm5aWHhxZWIxQlBQQlRKSFpWNmFScEFnTUIKQUFHalV6QlJNQjBHQTFVZERnUVdCQlFScHRWS3hCNGpGTjJnZTAwVStBd3FLM2czTkRBZkJnTlZIU01FR0RBVwpnQlFScHRWS3hCNGpGTjJnZTAwVStBd3FLM2czTkRBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUEwR0NTcUdTSWIzCkRRRUJDd1VBQTRJQkFRQ2tJTVJKYzRqZWtCazZUTUVUckFmOWJiKzBMcVkyYi9EMUlXdjUycFZzRkNmeWRDQ0wKRS9KVU1USGpTUXZvd1FRSHh1S291d3VHd2VoVFVocHJISzNzUXptUnZLTkpMVGlkT0tlNWJSUEZuTEVCa1JMRQpnQ1hrRXFNY2dvSjlMdzQvWW5sVm5UakRxK1lVN21QUkJlV3U3WDNFTXE4MWpjNHg1RWtubDZXem95MjIxd1RKCkhMTEl2OGFsbTBuYzAzV2lBbVBsUGpLL3Z3N0lRNDlKMTlydnROMXNDQ2xyUDBSVyt1NjRQL0luL3pBeE1HMC8KeGkwTTdjYk1GYjh5UGFDeERPWVQ0enljdWRUWlhNS0FReDRxLzRhYVpRK1oxV2FBTkVtQi9OM1hNTHBTTUZJaApEdjdCbUVVOWRSUkx6dklQMHIxNDlKNnlaZ2VQYzc2WWR5R0oKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRRFBFR2gycXhaRTJqSVIKcy82QVdiQjZvejFlYnNHOHpxNGFsS081VHI4M1hZa1M4TmtoOFJHYlNZdzg5VUdzQVpmdFpBak9DNjJpdWhFTgpRNTNmOHBOajJJZDVPTzRlYVQzem53SkNMRjJkR3E4UWRPdHNUY1NPRWRNdTdnTkVlTmZMVXlkRTYrZ2I3MUovCk9GQ2VlOVAzV1ZZaENOWnRtZ2FnMlptMEFGcU9Bdm9YVFd5RnQwc1hGMlRvdFRDZ1hYTTNZMnZnQmNyUTB0U24KWUdmZWo5VFRyakQ2RkEwU2JhZS9BemNNNXAvRTZnSjVhV0RIS3pGOXNpbzh0VDlGbjdRc293Y0dQUkxJTi9aNwo4bGFoRkhMamxUUG1malBBZnVlVFlWM2NOWTV0RjY2ZVdXbmsyNFhuL0xBWkpYR1NIV0ZuWlh4cWViMUJQUEJUCkpIWlY2YVJwQWdNQkFBRUNnZ0VBRSs5UzkxWEtXNCtjTVdzZ1RmQVVsd0gvUndlbnZFc3pwTmg1bUw0Vmw3bDQKR0d3Nm8xTm5yQWtkS01NOTh0Ym1ieExwN0JoZ3U2RnBRZHNvS0diY3ZNaWNabFhPU3Z3NzNDZ0xXaDZXVnFrNgpnSDJaS3NDajh6K1JFdHdVVVhQRzVzclhKWUlHd3lXN3pnYTRjRUdncXhnZFBDbnpKdk1rdnppajNSb0puZEZNCktMMjBjeDArUDROMnZLem1FSDJaYmZLUUo0bXlpTlUzdTFjWE14L1hhU04yczJNSExqNHVZemFJV0Q1clU0S1YKOHVrTmNnT3ZFSHl1eUFYcGgyYlVXdjVMcWIvWnFuQnVqVDI1ZFF6Zk43NC81a3grR1dNVkpVMXM1cUFqOEVyMApWZXhhK0FkMU9hM2JTMktEVGt4MHROQVZ1NGprT2tLSkZxVWJjc2RtendLQmdRRHl1T2diSW9CcVY2NjRLTVhlCk1lendkRGVLdTV3dkhUUEhDQStnQlRkbE5Kb1orS3g2Z2FVOXhsN0o2Q0pIcXB2Ti8xdGZFdVY1bzMySmhMdzEKQWtJMDY1ODQ3Slgyb1BvWDFYdU4xelJNUjk4bW05YWkvNk10d20vYWpoOVdKNnhKV2tCYUpyVXB1UGV5K2d5QQp6cDRhSXFCY2xXUjJWK0dkS3RHODROeWNKd0tCZ1FEYVpDUjVMVzBSMmc2bTNHUk9nQS9vY0RMTEM3V0ovVzliCkxUcTZLcndWYlVKUFUvRk1IbG1wb3NBOHY0dUp2MklnM0FwTXphL0JJd2FCUHMvUXArN1hrZVI1em5MbEg2RlEKK3VNQnVRRDhBRXQxZTZiVzJYQkpCcDZjemJXMmF1bjYvUEd3WmpCZkdYT0RQNXJVWHFQNkpiZ2pqMjdwL2RYMwpFVzUrVGlyRTd3S0JnRjdLSzRyOVRGMDdaUFp5cGVPQ1o5LzM0d0VCQjV1MnNkUFdxQk44TmdnR0pQQmpseWc0Cm5VbWt3THZsTmczNjZPSG9DY3oxV2p6SXhtd0FOR2dYTzdmakZNbHNTNXlIZldQMWNVMFJjRkVoK0ZuaG5rOEYKdXJwU0p0Q1psRTlYS3dkeWdaTXpicWllbmMxOXJZaFlLSkpZVjN3UXM2MHI0T1k2SkxLNHRpOGRBb0dCQUpjeQpyK0hKWm5MdWtpaEorNVF4cTFIVXBBWFpkSFUxcGl2czAzVGljMWN1VHJOWFBYN2lvRmNHbTZzelBlcy9PalBmCnc2M0sxYnlVZ0VObzlqM1NsbFJlNkZ6QVp1RmtsYTNZRk9RemJwQUpzRFNGU0V3RlBHMENqVHVvVy84UVpDL2wKZ1hzTU5MOFNndHZDWkhKVmw1ZHZGOTVleG41dncvd0s4SUczb25xM0FvR0FiYVV6UGZJWkRiTlJvM2tKeEZxawoxellzd3ZUUTdqU1lvMGlSbUVNSG9KTStvYWJPaFZjN0NZSjFoK1ZXelBmWXJCUFE5VEZjZEI3b1hueW9OTlZOClBjdGtUYXc1LyszNWdJWThHcmJsdzlqWmtmalFFVDJPNkFmMG5tQTd4a1F2djZkZkgzQTI0WlRyTExrY0pJTzYKZGVtNFpXbitiUWFRNnBvYThJdngvelU9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K

View File

@@ -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 tests
import (
"testing"
"github.com/alibaba/higress/test/e2e/conformance/utils/http"
"github.com/alibaba/higress/test/e2e/conformance/utils/suite"
)
func init() {
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteFullPathRegex)
}
var HTTPRouteFullPathRegex = suite.ConformanceTest{
ShortName: "HTTPRouteFullPathRegex",
Description: "test for 'higress.io/full-path-regex' annotation",
Manifests: []string{"tests/httproute-full-path-regex.yaml"},
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
testCases := []http.Assertion{
{
Request: http.AssertionRequest{
ActualRequest: http.Request{Path: "/foo/1234"},
},
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: "/bar/123"},
},
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/1234"},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 404,
},
},
Meta: http.AssertionMeta{
TargetBackend: "infra-backend-v2",
TargetNamespace: "higress-conformance-infra",
},
},
}
t.Run("Test for 'higress.io/full-path-regex'", func(t *testing.T) {
for _, testCase := range testCases {
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, testCase)
}
})
},
}

View File

@@ -0,0 +1,53 @@
# 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/use-regex: "true"
name: httproute-ingress-use-regex
namespace: higress-conformance-infra
spec:
ingressClassName: higress
rules:
- http:
paths:
- pathType: ImplementationSpecific
path: "/foo/[A-Z0-9]{3}"
backend:
service:
name: infra-backend-v1
port:
number: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
higress.io/full-path-regex: "true"
name: httproute-higress-full-path-regex
namespace: higress-conformance-infra
spec:
ingressClassName: higress
rules:
- http:
paths:
- pathType: ImplementationSpecific
path: "/bar/[A-Z0-9]{3}"
backend:
service:
name: infra-backend-v2
port:
number: 8080

View File

@@ -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/e2e/conformance/utils/http"
"github.com/alibaba/higress/test/e2e/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)
}
})
},
}

View File

@@ -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

View 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/e2e/conformance/utils/http"
"github.com/alibaba/higress/test/e2e/conformance/utils/suite"
)
func init() {
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteHttp2Rpc)
}
var HTTPRouteHttp2Rpc = suite.ConformanceTest{
ShortName: "HTTPRouteHttp2Rpc",
Description: "The Ingress in the higress-conformance-infra namespace uses the http2rpc.",
Manifests: []string{"tests/httproute-http2rpc.yaml"},
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
testcases := []http.Assertion{
{
Request: http.AssertionRequest{
ActualRequest: http.Request{
Host: "foo.com",
Path: "/dubbo/hello?name=higress",
Method: "GET",
},
},
Response: http.AssertionResponse{
ExpectedResponseNoRequest: true,
ExpectedResponse: http.Response{
StatusCode: 200,
},
},
},
}
t.Run("HTTPRoute app root", func(t *testing.T) {
for _, testcase := range testcases {
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, testcase)
}
})
},
}

View File

@@ -0,0 +1,70 @@
# 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.higress.io/v1
kind: Http2Rpc
metadata:
name: httproute-http2rpc-demo
namespace: higress-system
spec:
dubbo:
service: com.dubbo.demo.api.DemoService
version: 1.0.0
group: dev
methods:
- serviceMethod: sayHello
headersAttach: "*"
httpMethods:
- GET
httpPath: "/dubbo/hello"
params:
- paramKey: name
paramSource: QUERY
paramType: "java.lang.String"
---
apiVersion: networking.higress.io/v1
kind: McpBridge
metadata:
name: default
namespace: higress-system
spec:
registries:
- domain: nacos-standlone-rc3-service.higress-conformance-app-backend
nacosGroups:
- DEFAULT_GROUP
name: nacos-service-resource
port: 8848
type: nacos
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
higress.io/destination: providers:com.dubbo.demo.api.DemoService:1.0.0:dev.DEFAULT-GROUP.public.nacos
higress.io/rpc-destination-name: httproute-http2rpc-demo
name: httproute-http2rpc-demo-ingress
namespace: higress-system
spec:
ingressClassName: higress
rules:
- host: "foo.com"
http:
paths:
- pathType: Prefix
path: /dubbo
backend:
resource:
apiGroup: networking.higress.io
kind: McpBridge
name: default

View File

@@ -0,0 +1,106 @@
// 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/e2e/conformance/utils/http"
"github.com/alibaba/higress/test/e2e/conformance/utils/suite"
)
func init() {
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteMatchHeaders)
}
var HTTPRouteMatchHeaders = suite.ConformanceTest{
ShortName: "HTTPRouteMatchHeaders",
Description: "A single Ingress in the higress-conformance-infra namespace uses the match headers.",
Manifests: []string{"tests/httproute-match-headers.yaml"},
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
testcases := []http.Assertion{
{
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/foo",
Host: "foo.com",
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 404,
},
},
Meta: http.AssertionMeta{
TargetBackend: "infra-backend-v1",
TargetNamespace: "higress-conformance-infra",
},
},
{
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/foo",
Host: "foo.com",
Headers: map[string]string{
"abc": "12",
},
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 404,
},
},
Meta: http.AssertionMeta{
TargetBackend: "infra-backend-v1",
TargetNamespace: "higress-conformance-infra",
},
}, {
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/foo",
Host: "foo.com",
Headers: map[string]string{
"abc": "123",
"content-type": "application/json",
"user-id": "10086-1-1",
},
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 200,
},
},
Meta: http.AssertionMeta{
TargetBackend: "infra-backend-v1",
TargetNamespace: "higress-conformance-infra",
},
},
}
t.Run("Match HTTPRoute by headers", func(t *testing.T) {
for _, testcase := range testcases {
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, testcase)
}
})
},
}

View File

@@ -0,0 +1,39 @@
# 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:
# exact matching
higress.io/exact-match-header-abc: "123"
# regex matching
higress.io/regex-match-header-content-type: "application/(json|xml)"
# prefix matching
higress.io/prefix-match-header-user-id: "10086-1"
name: httproute-match-headers
namespace: higress-conformance-infra
spec:
ingressClassName: higress
rules:
- host: "foo.com"
http:
paths:
- pathType: Exact
path: "/foo"
backend:
service:
name: infra-backend-v1
port:
number: 8080

View File

@@ -0,0 +1,101 @@
// 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/e2e/conformance/utils/http"
"github.com/alibaba/higress/test/e2e/conformance/utils/suite"
)
func init() {
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteMatchMethods)
}
var HTTPRouteMatchMethods = suite.ConformanceTest{
ShortName: "HTTPRouteMatchMethods",
Description: "A single Ingress in the higress-conformance-infra namespace uses the match methods.",
Manifests: []string{"tests/httproute-match-methods.yaml"},
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
testcases := []http.Assertion{
{
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/foo",
Method: "GET",
Host: "foo.com",
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 404,
},
},
Meta: http.AssertionMeta{
TargetBackend: "infra-backend-v1",
TargetNamespace: "higress-conformance-infra",
},
}, {
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/foo",
Method: "POST",
Host: "foo.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: "/foo",
Method: "PUT",
Host: "foo.com",
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 200,
},
},
Meta: http.AssertionMeta{
TargetBackend: "infra-backend-v1",
TargetNamespace: "higress-conformance-infra",
},
},
}
t.Run("Match HTTPRoute by methods", func(t *testing.T) {
for _, testcase := range testcases {
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, testcase)
}
})
},
}

View File

@@ -0,0 +1,34 @@
# 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:
higress.io/match-method: "POST PUT PATCH"
name: httproute-match-methods
namespace: higress-conformance-infra
spec:
ingressClassName: higress
rules:
- host: "foo.com"
http:
paths:
- pathType: Exact
path: "/foo"
backend:
service:
name: infra-backend-v1
port:
number: 8080

View File

@@ -0,0 +1,143 @@
// 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/e2e/conformance/utils/http"
"github.com/alibaba/higress/test/e2e/conformance/utils/suite"
)
func init() {
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteMatchPath)
}
var HTTPRouteMatchPath = suite.ConformanceTest{
ShortName: "HTTPRouteMatchPath",
Description: "A Ingress in the higress-conformance-infra namespace that match different path.",
Manifests: []string{"tests/httproute-match-path.yaml"},
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
testcases := []http.Assertion{
{
Meta: http.AssertionMeta{
TestCaseName: "case1: normal request",
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{
TestCaseName: "case2: path is '/bar' and match prefix path successfully",
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{
TestCaseName: "case2: path is '/bar' and match prefix path failed",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/bard",
Host: "foo.com",
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 404,
},
},
}, {
Meta: http.AssertionMeta{
TestCaseName: "case3: path is '/cat/' and match prefix path successfully",
TargetBackend: "infra-backend-v3",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/cat",
Host: "foo.com",
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 200,
},
},
}, {
Meta: http.AssertionMeta{
TestCaseName: "case4: path is '/cat/' and match prefix path successfully",
TargetBackend: "infra-backend-v3",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/cat/ok",
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: "foo.com",
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 200,
},
},
},
}
t.Run("HTTPRoute Match different path Cases", func(t *testing.T) {
for _, testcase := range testcases {
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, testcase)
}
})
},
}

View File

@@ -0,0 +1,53 @@
# 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-match-path
namespace: higress-conformance-infra
spec:
ingressClassName: higress
rules:
- host: "foo.com"
http:
paths:
- pathType: Prefix
path: "/foo"
backend:
service:
name: infra-backend-v2
port:
number: 8080
- pathType: Exact
path: "/foo"
backend:
service:
name: infra-backend-v1
port:
number: 8080
- pathType: Prefix
path: "/bar"
backend:
service:
name: infra-backend-v2
port:
number: 8080
- pathType: Prefix
path: "/cat/"
backend:
service:
name: infra-backend-v3
port:
number: 8080

View File

@@ -0,0 +1,79 @@
// 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/e2e/conformance/utils/http"
"github.com/alibaba/higress/test/e2e/conformance/utils/suite"
)
func init() {
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteMatchQueryParams)
}
var HTTPRouteMatchQueryParams = suite.ConformanceTest{
ShortName: "HTTPRouteMatchQueryParams",
Description: "A single Ingress in the higress-conformance-infra namespace uses the match queryParams.",
Manifests: []string{"tests/httproute-match-query-params.yaml"},
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
testcases := []http.Assertion{
{
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/foo",
Host: "foo.com",
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 404,
},
},
Meta: http.AssertionMeta{
TargetBackend: "infra-backend-v1",
TargetNamespace: "higress-conformance-infra",
},
}, {
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/foo?abc=123&content-type=application/json&user-id=10086-1-1",
Host: "foo.com",
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 200,
},
},
Meta: http.AssertionMeta{
TargetBackend: "infra-backend-v1",
TargetNamespace: "higress-conformance-infra",
},
},
}
t.Run("Match HTTPRoute by queryParams", func(t *testing.T) {
for _, testcase := range testcases {
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, testcase)
}
})
},
}

View File

@@ -0,0 +1,39 @@
# 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:
# exact matching
higress.io/exact-match-query-abc: "123"
# regex matching
higress.io/regex-match-query-content-type: "application/(json|xml)"
# prefix matching
higress.io/prefix-match-query-user-id: "10086-1"
name: httproute-match-query-params
namespace: higress-conformance-infra
spec:
ingressClassName: higress
rules:
- host: "foo.com"
http:
paths:
- pathType: Exact
path: "/foo"
backend:
service:
name: infra-backend-v1
port:
number: 8080

View File

@@ -0,0 +1,65 @@
// 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/e2e/conformance/utils/http"
"github.com/alibaba/higress/test/e2e/conformance/utils/roundtripper"
"github.com/alibaba/higress/test/e2e/conformance/utils/suite"
)
func init() {
HigressConformanceTests = append(HigressConformanceTests, HTTPRoutePermanentRedirectCode)
}
var HTTPRoutePermanentRedirectCode = suite.ConformanceTest{
ShortName: "HTTPRoutePermanentRedirectCode",
Description: "The Ingress in the higress-conformance-infra namespace uses the permanent redirect code header.",
Manifests: []string{"tests/httproute-permanent-redirect-code.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{
Host: "foo.com",
Path: "/foo",
UnfollowRedirect: true,
},
RedirectRequest: &roundtripper.RedirectRequest{
Scheme: "http",
Host: "bar.com",
Path: "/foo",
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 308,
},
},
},
}
t.Run("HTTPRoute permanent redirect code", func(t *testing.T) {
for _, testcase := range testcases {
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, testcase)
}
})
},
}

View File

@@ -0,0 +1,35 @@
# 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/permanent-redirect: "http://bar.com"
nginx.ingress.kubernetes.io/permanent-redirect-code: "308"
name: httproute-permanent-redirect-code
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

View File

@@ -0,0 +1,65 @@
// 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/e2e/conformance/utils/http"
"github.com/alibaba/higress/test/e2e/conformance/utils/roundtripper"
"github.com/alibaba/higress/test/e2e/conformance/utils/suite"
)
func init() {
HigressConformanceTests = append(HigressConformanceTests, HTTPRoutePermanentRedirect)
}
var HTTPRoutePermanentRedirect = suite.ConformanceTest{
ShortName: "HTTPRoutePermanentRedirect",
Description: "The Ingress in the higress-conformance-infra namespace uses the permanent redirect header.",
Manifests: []string{"tests/httproute-permanent-redirect.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{
Host: "foo.com",
Path: "/foo",
UnfollowRedirect: true,
},
RedirectRequest: &roundtripper.RedirectRequest{
Scheme: "http",
Host: "bar.com",
Path: "/foo",
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 301,
},
},
},
}
t.Run("HTTPRoute permanent redirect", func(t *testing.T) {
for _, testcase := range testcases {
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, testcase)
}
})
},
}

View File

@@ -0,0 +1,34 @@
# 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/permanent-redirect: "http://bar.com"
name: httproute-permanent-redirect
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

View File

@@ -0,0 +1,50 @@
# 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/ssl-redirect: "true"
name: http-redirect-as-https
namespace: higress-conformance-infra
spec:
ingressClassName: higress
tls:
- hosts:
- "test.com"
secretName: my-app-tls-secret
rules:
- host: "test.com"
http:
paths:
- pathType: Prefix
path: "/test"
backend:
service:
name: infra-backend-v1
port:
number: 8080
---
apiVersion: v1
kind: Secret
metadata:
name: my-app-tls-secret
namespace: higress-conformance-infra
type: kubernetes.io/tls
data:
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURzVENDQXBtZ0F3SUJBZ0lVUGRDZENSdm1pbVZwK2VCcTMxUm9oZ1JsYTBzd0RRWUpLb1pJaHZjTkFRRUwKQlFBd2FERUxNQWtHQTFVRUJoTUNZWE14RERBS0JnTlZCQWdNQTJSaGN6RU1NQW9HQTFVRUJ3d0RZWE5rTVF3dwpDZ1lEVlFRS0RBTmhaSE14RERBS0JnTlZCQXNNQTNwNFl6RU5NQXNHQTFVRUF3d0VZWGRsY2pFU01CQUdDU3FHClNJYjNEUUVKQVJZRGNYZGxNQjRYRFRJek1EVXlNREEzTVRRd09Wb1hEVEkwTURVeE9UQTNNVFF3T1Zvd2FERUwKTUFrR0ExVUVCaE1DWVhNeEREQUtCZ05WQkFnTUEyUmhjekVNTUFvR0ExVUVCd3dEWVhOa01Rd3dDZ1lEVlFRSwpEQU5oWkhNeEREQUtCZ05WQkFzTUEzcDRZekVOTUFzR0ExVUVBd3dFWVhkbGNqRVNNQkFHQ1NxR1NJYjNEUUVKCkFSWURjWGRsTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUExeHB6Z21UVzdwQmUKa3ZlRkRBU0JZLzdpZmxvWFk1T0x3OUZwOXJqZHNMRmh5Y1dPR0U5NHJ1RmhDSkVqYmNzQW5URHZOKzg4WVRzOApkV2tWbVArQ1RQQjVoN2EvZmJtelJPYVE5SjVDQXhqaGJuRGhCc3YrYVpzeVlhVzBHQkp3Y3h6U1RoVUJpbm9aCmJwYUhvU1QxUjBlZ0FHQ2lkWk4wU2xDMW9KYmxOMHJoOGxKNUROcWxWSnBYejUxaGdLU1NmSm1UcElRQkhBQVkKSDVMUU1CTCtQem9INy83cWhUSUVaWWJvU0o2ajV1ZDc1YUZzVVhndmdLWFgvZ2JZTHlaQ0ljTXUyR2YzRjQ5bwoyc1QwdFZzQmxHbWsrVkswcmgxSi9xQjBWNDBheU8xR3NFalRhelBLUitrYTZJS1N6SjJLVkJadCtHQTdGMUkrCmlHOXcrVDQwVFFJREFRQUJvMU13VVRBZEJnTlZIUTRFRmdRVTFLZSs3aHZTaTljRjhXM2hZTW5hd0NKblcwVXcKSHdZRFZSMGpCQmd3Rm9BVTFLZSs3aHZTaTljRjhXM2hZTW5hd0NKblcwVXdEd1lEVlIwVEFRSC9CQVV3QXdFQgovekFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBQ3JGQU1KV3g1QThsaXVLY1NETmlWY0Rvem5sU1p6cFYyeGRNCnpkSU5SRytFVXFzV3lpekNtWk5rSUMyV0lKTWhNdVBkOWhJdTZ3cWd5YjJaNnN3MmdjOXpwVnpsbkRKYlUwSlUKZEZJZDFuYmtQRG01ekk3NzAyRTk0eVZqUUs5ZllRVDU3cHFTdlkvOUpSeXRpcGdpdnAxMGR2UC95NUpocDVBawo3VHNQcnZ2K3gzWGhLYTRwRG40eEhzZHp4MGx0ZHdUdExiaWFGU1IwUHpBZzdpOUNsbjQ5aWRRd3YxaHpaNTV5CkFtOStESHhmTkgreGFIYk9zWTJFRHpiZ0FxR0JMaTNEMmtxMkdRREFmRDdOdGovRUNlODl1bG5zSWpMajB5YmUKNWR0QzRXYndBNER3UHBFWUJvbTFTZmdxQlJSSG5seTVIMUxZMS80ZnpUZkNIYkFtdWc9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2Z0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktnd2dnU2tBZ0VBQW9JQkFRRFhHbk9DWk5idWtGNlMKOTRVTUJJRmovdUorV2hkams0dkQwV24ydU4yd3NXSEp4WTRZVDNpdTRXRUlrU050eXdDZE1PODM3enhoT3p4MQphUldZLzRKTThIbUh0cjk5dWJORTVwRDBua0lER09GdWNPRUd5LzVwbXpKaHBiUVlFbkJ6SE5KT0ZRR0tlaGx1CmxvZWhKUFZIUjZBQVlLSjFrM1JLVUxXZ2x1VTNTdUh5VW5rTTJxVlVtbGZQbldHQXBKSjhtWk9raEFFY0FCZ2YKa3RBd0V2NC9PZ2Z2L3VxRk1nUmxodWhJbnFQbTUzdmxvV3hSZUMrQXBkZitCdGd2SmtJaHd5N1laL2NYajJqYQp4UFMxV3dHVWFhVDVVclN1SFVuK29IUlhqUnJJN1Vhd1NOTnJNOHBINlJyb2dwTE1uWXBVRm0zNFlEc1hVajZJCmIzRDVQalJOQWdNQkFBRUNnZ0VBTVRZT2ZpTDIzejV0UEo5Zk0zZ21hQXVzb3E2VzBrT3p3cGw2N2lTdUoxbjEKbnRWUkpIT3VEd2htRERFMFUwNlJ0ZVMzbmVyZ08vaHk1UU9sR3NzOThyOURkbzZUTWI3VjZpbjd1Tk1xRkE1UgpxTlF2VDBCRlZNRGFYbWVzRTZQSVVUV2pVWlRSdE80cE9sazY3MTJHdGdlSGJmNnR2RXQvVys4cUZuTGZQdTQ1Cm50Nld6NzgrRkVuUW5qWVk3R1N4bTlaVFEraUw3VGMyR09MMUFjdkxVcmV1VS9aMXNwTVFPTFRUUzBLbkJka0YKY0FVeFBkNGpnZ3lwbGNpSHBQaktNNG1VRGkvSDluTWFjNyt4K08zZFZCNXlpb2xLREJCOHUybDIvb3BPSzVVZwpTSUg0ajVqU1NJY3lNNUNSdkFpR2Q3eE9HaE1Zb3hQV1FNNnduNGMyMFFLQmdRRFpEbkc5TmNTeHVSbnNUSVFMCnZIMForUWFMZmZ4WGF6eWswM21NMldiOWd3NEoxOGF5ZlF4STk2RU13VXd5ODJkRFh5QldzWFgxK0NxMnd2ZUUKMnB1cU1kV3pvUWI4UnZlOVJ6RDlwM0Z1V2kyR3BmL3JsWkVmZ1lRek1FWENtWnN0VlVDZXo5aVZVdW5ZQzRoZQovNU83Zm12VkVlQ1k3TUFIT2JtWUNOWmNtUUtCZ1FEOXNreTdqNjViai9ZYzlER2pQVzNraUZKeWl5YU9rb2dQCnVtbk5vQ2w4bHhIZ2d1Ym8wT3pubEFZQ0M5V3pxTmdBa3E4eWNlMkdTVkFEOTNoZisycDNON3lGRktMS0I3L0YKTlFsUHMyV2pyK25DUytxSHBSdnpQUjgzN01DUkZnbVlxRlNlaXE5NlcyeStwVTJBYVJkQTlqYWtPeWd0TGl5YQpSbHFDeWNVUjFRS0JnUUROWXBLVFpGWmJpUGdUbFk5NC80RXMyMnVyQUtxUEdhVEhubWVzdEdaMHlkYTF6NXh2CmRrM3ltWWFsNkI0dk5BeHBQcEQrRjJ1ME5JQk9jWXYvQlZBNHFuRTVTTXl3V0lMQmNxVFR6K1pRY2pvVDUrMlMKd1BNU2FkNXJCV2x0S3lZZnJrUzRRWm9DS2ZPbC83dXBrSkw4M2pJdzZucW9tWlZYQVBNeC9tTEFPUUtCZ1FDQQpWeXplVGNlRTVvVTVESWYzN3VHakZSdXdlcGljMDZBbFpNYVZrWXFyVHJscWZJNVlCU2x6MWJ4Y1dLUlphUGN0CkF3ZkNXMFF3QlBLSHJ5K2tUc29EV1p6ekxnZFVjU3NnbHI0SkpkWXJRcGpkQkE2M1pGMkpaY2hmUUZRQ2tjVjEKQnVNWCtVemdkMVBCOWxvSXRpRmZhYThtMGc1M0hMN1BwUHV3NG1YaHFRS0JnQmY5NGRrTXplUW44SDBrK2xXcQpJV3lJaGtqOHpNUmw2ODNzRFFOQUdSYngyTnR2RjYyL2dUK1ZJSXdmSzV5MmI4WXEwNFVEQy9rL1hkK0lBc3dVClFTRGdUVFpmYzZkUkVtRU8zc0M0a0xYa1N3Y1BQZmcvTC92T29YRDJiZWxmWUFtaHhSUnByQ0p4ZVowRVBvRUEKc25RblpMN1VsZCtSTmF3dmJNR05XTXJiCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K

View File

@@ -0,0 +1,67 @@
// 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/e2e/conformance/utils/http"
"github.com/alibaba/higress/test/e2e/conformance/utils/roundtripper"
"github.com/alibaba/higress/test/e2e/conformance/utils/suite"
)
func init() {
HigressConformanceTests = append(HigressConformanceTests, HttpRedirectAsHttps)
}
var HttpRedirectAsHttps = suite.ConformanceTest{
ShortName: "HttpRedirectAsHttps",
Description: "The Ingress in the higress-conformance-infra namespace Server-side HTTPS enforcement through redirect.",
Manifests: []string{"tests/httproute-redirct-as-https.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{
Host: "test.com",
Path: "/test",
UnfollowRedirect: true,
},
RedirectRequest: &roundtripper.RedirectRequest{
Scheme: "https",
Host: "test.com",
Path: "/test",
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 308,
},
},
},
}
t.Run("HttpRedirectAsHttps", func(t *testing.T) {
for _, testcase := range testcases {
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, testcase)
}
})
},
}

View File

@@ -0,0 +1,228 @@
// 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/e2e/conformance/utils/http"
"github.com/alibaba/higress/test/e2e/conformance/utils/suite"
)
func init() {
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteRequestHeaderControl)
}
var HTTPRouteRequestHeaderControl = suite.ConformanceTest{
ShortName: "HTTPRouteRequestHeaderControl",
Description: "A single Ingress in the higress-conformance-infra namespace controls the request header.",
Manifests: []string{"tests/httproute-request-header-control.yaml"},
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
testcases := []http.Assertion{
{
Meta: http.AssertionMeta{
TestCaseName: "case 1: add one",
TargetBackend: "infra-backend-v1",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/foo1",
Host: "foo.com",
},
ExpectedRequest: &http.ExpectedRequest{
Request: http.Request{
Path: "/foo1",
Host: "foo.com",
Headers: map[string]string{
"stage": "test",
},
},
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 200,
},
},
},
{
Meta: http.AssertionMeta{
TestCaseName: "case 2: add more",
TargetBackend: "infra-backend-v1",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/foo2",
Host: "foo.com",
},
ExpectedRequest: &http.ExpectedRequest{
Request: http.Request{
Path: "/foo2",
Host: "foo.com",
Headers: map[string]string{
"stage": "test",
"canary": "true",
},
},
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 200,
},
},
},
{
Meta: http.AssertionMeta{
TestCaseName: "case 3: update one",
TargetBackend: "infra-backend-v1",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/foo3",
Host: "foo.com",
Headers: map[string]string{
"stage": "test",
},
},
ExpectedRequest: &http.ExpectedRequest{
Request: http.Request{
Path: "/foo3",
Host: "foo.com",
Headers: map[string]string{
"stage": "pro",
},
},
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 200,
},
},
},
{
Meta: http.AssertionMeta{
TestCaseName: "case 4: update more",
TargetBackend: "infra-backend-v1",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/foo4",
Host: "foo.com",
Headers: map[string]string{
"stage": "test",
"canary": "true",
},
},
ExpectedRequest: &http.ExpectedRequest{
Request: http.Request{
Path: "/foo4",
Host: "foo.com",
Headers: map[string]string{
"stage": "pro",
"canary": "false",
},
},
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 200,
},
},
},
{
Meta: http.AssertionMeta{
TestCaseName: "case 5: remove one",
TargetBackend: "infra-backend-v1",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/foo5",
Host: "foo.com",
Headers: map[string]string{
"stage": "test",
},
},
ExpectedRequest: &http.ExpectedRequest{
Request: http.Request{
Path: "/foo5",
Host: "foo.com",
},
AbsentHeaders: []string{"stage"},
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 200,
},
},
},
{
Meta: http.AssertionMeta{
TestCaseName: "case 6: remove more",
TargetBackend: "infra-backend-v1",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/foo6",
Host: "foo.com",
Headers: map[string]string{
"stage": "test",
"canary": "true",
},
},
ExpectedRequest: &http.ExpectedRequest{
Request: http.Request{
Path: "/foo6",
Host: "foo.com",
},
AbsentHeaders: []string{"stage", "canary"},
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 200,
},
},
},
}
t.Run("Request header control", func(t *testing.T) {
for _, testcase := range testcases {
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, testcase)
}
})
},
}

View File

@@ -0,0 +1,143 @@
# 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:
higress.io/request-header-control-add: stage test
name: httproute-request-header-control-add-one
namespace: higress-conformance-infra
spec:
ingressClassName: higress
rules:
- host: "foo.com"
http:
paths:
- pathType: Exact
path: "/foo1"
backend:
service:
name: infra-backend-v1
port:
number: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
higress.io/request-header-control-add: |
stage test
canary true
name: httproute-request-header-control-add-more
namespace: higress-conformance-infra
spec:
ingressClassName: higress
rules:
- host: "foo.com"
http:
paths:
- pathType: Exact
path: "/foo2"
backend:
service:
name: infra-backend-v1
port:
number: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
higress.io/request-header-control-update: stage pro
name: httproute-request-header-control-update-one
namespace: higress-conformance-infra
spec:
ingressClassName: higress
rules:
- host: "foo.com"
http:
paths:
- pathType: Exact
path: "/foo3"
backend:
service:
name: infra-backend-v1
port:
number: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
higress.io/request-header-control-update: |
stage pro
canary false
name: httproute-request-header-control-update-more
namespace: higress-conformance-infra
spec:
ingressClassName: higress
rules:
- host: "foo.com"
http:
paths:
- pathType: Exact
path: "/foo4"
backend:
service:
name: infra-backend-v1
port:
number: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
higress.io/request-header-control-remove: stage
name: httproute-request-header-control-remove-one
namespace: higress-conformance-infra
spec:
ingressClassName: higress
rules:
- host: "foo.com"
http:
paths:
- pathType: Exact
path: "/foo5"
backend:
service:
name: infra-backend-v1
port:
number: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
higress.io/request-header-control-remove: stage,canary
name: httproute-request-header-control-remove-more
namespace: higress-conformance-infra
spec:
ingressClassName: higress
rules:
- host: "foo.com"
http:
paths:
- pathType: Exact
path: "/foo6"
backend:
service:
name: infra-backend-v1
port:
number: 8080

View 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/e2e/conformance/utils/http"
"github.com/alibaba/higress/test/e2e/conformance/utils/suite"
)
func init() {
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteRewriteHost)
}
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)
}
})
},
}

View 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

View File

@@ -0,0 +1,62 @@
// 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/e2e/conformance/utils/http"
"github.com/alibaba/higress/test/e2e/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) {
testCases := []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",
},
},
}
t.Run("Rewrite HTTPRoute Path", func(t *testing.T) {
for _, testCase := range testCases {
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, testCase)
}
})
},
}

View File

@@ -0,0 +1,34 @@
# 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

View File

@@ -0,0 +1,101 @@
// 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/e2e/conformance/utils/http"
"github.com/alibaba/higress/test/e2e/conformance/utils/suite"
)
func init() {
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteSameHostAndPath)
}
var HTTPRouteSameHostAndPath = suite.ConformanceTest{
ShortName: "HTTPRouteSameHostAndPath",
Description: "A single Ingress in the higress-conformance-infra namespace demonstrates the situation with same path and host",
Manifests: []string{"tests/httproute-same-host-and-path.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: "/hello-world",
Headers: map[string]string{
"abc": "123",
"def": "456",
},
},
},
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: "/hello-world",
Headers: map[string]string{
"abc": "123",
"def": "def",
},
},
},
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: "/hello-world",
Headers: map[string]string{
"abc": "123",
},
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 200,
},
},
},
}
t.Run("Match Routes With same host and path", func(t *testing.T) {
for _, testcase := range testcases {
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, testcase)
}
})
},
}

View File

@@ -0,0 +1,104 @@
# 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.
# higress-same-host-and-path-01 and -02: to test same header key and different header value
# higress-same-host-and-path-03 and -04: to test route match precedence (04 > 03)
# higress-same-host-and-path-01 and -04: to test same header key and value but different order
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
# exact matching
higress.io/exact-match-header-abc: "123"
higress.io/exact-match-header-def: "456"
name: higress-same-host-and-path-01
namespace: higress-conformance-infra
spec:
ingressClassName: higress
rules:
- http:
paths:
- pathType: Prefix
path: "/hello-world"
backend:
service:
name: infra-backend-v1
port:
number: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
# exact matching
higress.io/exact-match-header-abc: "123"
higress.io/exact-match-header-def: "def"
name: higress-same-host-and-path-02
namespace: higress-conformance-infra
spec:
ingressClassName: higress
rules:
- http:
paths:
- pathType: Prefix
path: "/hello-world"
backend:
service:
name: infra-backend-v2
port:
number: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
# exact matching
higress.io/exact-match-header-abc: "123"
name: higress-same-host-and-path-03
namespace: higress-conformance-infra
spec:
ingressClassName: higress
rules:
- http:
paths:
- pathType: Prefix
path: "/hello-world"
backend:
service:
name: infra-backend-v3
port:
number: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
# exact matching
higress.io/exact-match-header-def: "456"
higress.io/exact-match-header-abc: "123"
name: higress-same-host-and-path-04
namespace: higress-conformance-infra
spec:
ingressClassName: higress
rules:
- http:
paths:
- pathType: Prefix
path: "/hello-world"
backend:
service:
name: infra-backend-v2
port:
number: 8080

View File

@@ -0,0 +1,52 @@
// 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/e2e/conformance/utils/http"
"github.com/alibaba/higress/test/e2e/conformance/utils/suite"
)
func init() {
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteSimpleSameNamespace)
}
var HTTPRouteSimpleSameNamespace = suite.ConformanceTest{
ShortName: "HTTPRouteSimpleSameNamespace",
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.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,
},
},
})
})
},
}

View File

@@ -0,0 +1,31 @@
# 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: higress-conformance-infra-test
namespace: higress-conformance-infra
spec:
ingressClassName: higress
rules:
- http:
paths:
- pathType: Prefix
path: "/hello-world"
backend:
service:
name: infra-backend-v1
port:
number: 8080

View File

@@ -0,0 +1,65 @@
// 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/e2e/conformance/utils/http"
"github.com/alibaba/higress/test/e2e/conformance/utils/roundtripper"
"github.com/alibaba/higress/test/e2e/conformance/utils/suite"
)
func init() {
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteTemporalRedirect)
}
var HTTPRouteTemporalRedirect = suite.ConformanceTest{
ShortName: "HTTPRouteTemporalRedirect",
Description: "The Ingress in the higress-conformance-infra namespace uses the temporal redirect header.",
Manifests: []string{"tests/httproute-temporal-redirect.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{
Host: "foo.com",
Path: "/foo",
UnfollowRedirect: true,
},
RedirectRequest: &roundtripper.RedirectRequest{
Scheme: "http",
Host: "bar.com",
Path: "/foo",
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 302,
},
},
},
}
t.Run("HTTPRoute temporal redirect", func(t *testing.T) {
for _, testcase := range testcases {
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, testcase)
}
})
},
}

View File

@@ -0,0 +1,34 @@
# 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/temporal-redirect: "http://bar.com"
name: httproute-temporal-redirect
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

View 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/e2e/conformance/utils/http"
"github.com/alibaba/higress/test/e2e/conformance/utils/suite"
)
func init() {
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteWhitelistSourceRange)
}
var HTTPRouteWhitelistSourceRange = suite.ConformanceTest{
ShortName: "HTTPRouteWhitelistSourceRange",
Description: "A single Ingress in the higress-conformance-infra namespace demonstrates ip access control",
Manifests: []string{"tests/httproute-whitelist-source-range.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: 403,
},
},
},
{
Meta: http.AssertionMeta{
TargetBackend: "infra-backend-v1",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/bar",
Host: "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)
}
})
},
}

View 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:
name: higress-conformance-infra-ip-not-in-whitelist-test
namespace: higress-conformance-infra
annotations:
nginx.ingress.kubernetes.io/whitelist-source-range: "1.1.1.1"
spec:
ingressClassName: higress
rules:
- host: "foo.com"
http:
paths:
- pathType: Prefix
path: "/foo"
backend:
service:
name: infra-backend-v1
port:
number: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: higress-conformance-infra-ip-in-whitelist-test
namespace: higress-conformance-infra
annotations:
nginx.ingress.kubernetes.io/whitelist-source-range: "*"
spec:
ingressClassName: higress
rules:
- host: "bar.com"
http:
paths:
- pathType: Prefix
path: "/bar"
backend:
service:
name: infra-backend-v1
port:
number: 8080

View File

@@ -0,0 +1,19 @@
// 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 "github.com/alibaba/higress/test/e2e/conformance/utils/suite"
var HigressConformanceTests []suite.ConformanceTest

View File

@@ -0,0 +1,59 @@
// 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/e2e/conformance/utils/http"
"github.com/alibaba/higress/test/e2e/conformance/utils/suite"
)
func init() {
HigressConformanceTests = append(HigressConformanceTests, WasmPluginsRequestBlock)
}
var WasmPluginsRequestBlock = suite.ConformanceTest{
ShortName: "WasmPluginsRequestBlock",
Description: "The Ingress in the higress-conformance-infra namespace test the request-block wasmplugins.",
Manifests: []string{"tests/request-block.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{
Host: "foo.com",
Path: "/swagger.html",
UnfollowRedirect: true,
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 403,
},
},
},
}
t.Run("WasmPlugins request-block", func(t *testing.T) {
for _, testcase := range testcases {
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, testcase)
}
})
},
}

View File

@@ -0,0 +1,45 @@
# 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/app-root: "/foo"
name: httproute-app-root
namespace: higress-conformance-infra
spec:
ingressClassName: higress
rules:
- host: "foo.com"
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: infra-backend-v1
port:
number: 8080
---
apiVersion: extensions.higress.io/v1alpha1
kind: WasmPlugin
metadata:
name: request-block
namespace: higress-system
spec:
defaultConfig:
block_urls:
- "swagger.html"
url: file:///opt/plugins/wasm-go/extensions/request-block/plugin.wasm

View File

@@ -0,0 +1,144 @@
/*
Copyright 2022 The Kubernetes 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 cert
import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"math/big"
"net"
"testing"
"time"
"github.com/stretchr/testify/require"
)
type CertType int
const (
CACertType CertType = iota
ServerCertType
ClientCertType
)
const (
// RSABits defines the bit length of the RSA private key
RSABits = 2048
// ValidFor defines the certificate validity period
ValidFor = 365 * 24 * time.Hour
)
// MustGenerateCaCert must generate a CA certificate and private key.
// `certOut` and `keyOut` are PEM format buffers for certificate and private key, respectively.
// `caCert` and `caKey` are the corresponding structures.
func MustGenerateCaCert(t *testing.T) (certOut, keyOut *bytes.Buffer, caCert *x509.Certificate, caKey *rsa.PrivateKey) {
notBefore := time.Now()
notAfter := notBefore.Add(ValidFor)
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1+int64(CACertType)), 128)
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
require.NoError(t, err, "failed to generate serial number")
caCert = &x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{
CommonName: "default",
Organization: []string{"Higress E2E Test"},
},
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
BasicConstraintsValid: true,
IsCA: true,
}
caKey, err = rsa.GenerateKey(rand.Reader, RSABits)
certOut, keyOut, err = GenerateCert(caCert, caKey, caCert, caKey)
return
}
// MustGenerateCertWithCA must generate a self-signed client/server certificate and private key
// using CA certificate and private key.
// `hosts` is used when CertType == ServerCertType
func MustGenerateCertWithCA(t *testing.T, certType CertType, caCert *x509.Certificate, caKey *rsa.PrivateKey, hosts []string) (certOut, keyOut *bytes.Buffer) {
notBefore := time.Now()
notAfter := notBefore.Add(ValidFor)
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1+int64(certType)), 128)
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
require.NoError(t, err, "failed to generate serial number")
template := &x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{
CommonName: "default",
Organization: []string{"Higress E2E Test"},
},
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
}
if certType == ServerCertType && hosts != nil {
for _, h := range hosts {
if ip := net.ParseIP(h); ip != nil {
template.IPAddresses = append(template.IPAddresses, ip)
} else {
template.DNSNames = append(template.DNSNames, h)
}
}
}
privateKey, err := rsa.GenerateKey(rand.Reader, RSABits)
require.NoError(t, err, "failed to generate ras key")
certOut, keyOut, err = GenerateCert(template, privateKey, caCert, caKey)
return
}
// GenerateCert obtains the corresponding certificate and private key buffers
// using the certificate template and private key.
func GenerateCert(cert *x509.Certificate, key *rsa.PrivateKey, caCert *x509.Certificate, caKey *rsa.PrivateKey) (
certOut, keyOut *bytes.Buffer, err error) {
var (
priv = key
pub = &priv.PublicKey
privPm = priv
)
if caKey != nil {
privPm = caKey
}
certDER, err := x509.CreateCertificate(rand.Reader, cert, caCert, pub, privPm)
if err != nil {
err = fmt.Errorf("failed to create certificate: %w", err)
return
}
certOut = new(bytes.Buffer)
err = pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: certDER})
if err != nil {
err = fmt.Errorf("failed creating cert: %w", err)
return
}
keyOut = new(bytes.Buffer)
privDER := x509.MarshalPKCS1PrivateKey(priv)
err = pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: privDER})
if err != nil {
err = fmt.Errorf("failed creating key: %w", err)
return
}
return
}

View File

@@ -0,0 +1,92 @@
/*
Copyright 2022 The Kubernetes 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 config
import "time"
type TimeoutConfig struct {
// CreateTimeout represents the maximum time for a Kubernetes object to be created.
// Max value for conformant implementation: None
CreateTimeout time.Duration
// DeleteTimeout represents the maximum time for a Kubernetes object to be deleted.
// Max value for conformant implementation: None
DeleteTimeout time.Duration
// GetTimeout represents the maximum time to get a Kubernetes object.
// Max value for conformant implementation: None
GetTimeout time.Duration
// ManifestFetchTimeout represents the maximum time for getting content from a https:// URL.
// Max value for conformant implementation: None
ManifestFetchTimeout time.Duration
// MaxTimeToConsistency is the maximum time for requiredConsecutiveSuccesses (default 3) requests to succeed in a row before failing the test.
// Max value for conformant implementation: 30 seconds
MaxTimeToConsistency time.Duration
// NamespacesMustBeReady represents the maximum time for all Pods and Gateways in a namespaces to be marked as ready.
// Max value for conformant implementation: None
NamespacesMustBeReady time.Duration
// RequestTimeout represents the maximum time for making an HTTP Request with the roundtripper.
// Max value for conformant implementation: None
RequestTimeout time.Duration
// TLSHandshakeTimeout represents the maximum time for waiting for a TLS handshake. Zero means no timeout.
// Max value for conformant implementation: None
TLSHandshakeTimeout time.Duration
}
// DefaultTimeoutConfig populates a TimeoutConfig with the default values.
func DefaultTimeoutConfig() TimeoutConfig {
return TimeoutConfig{
CreateTimeout: 60 * time.Second,
DeleteTimeout: 10 * time.Second,
GetTimeout: 10 * time.Second,
ManifestFetchTimeout: 10 * time.Second,
MaxTimeToConsistency: 30 * time.Second,
NamespacesMustBeReady: 300 * time.Second,
RequestTimeout: 10 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
}
}
func SetupTimeoutConfig(timeoutConfig *TimeoutConfig) {
defaultTimeoutConfig := DefaultTimeoutConfig()
if timeoutConfig.CreateTimeout == 0 {
timeoutConfig.CreateTimeout = defaultTimeoutConfig.CreateTimeout
}
if timeoutConfig.DeleteTimeout == 0 {
timeoutConfig.DeleteTimeout = defaultTimeoutConfig.DeleteTimeout
}
if timeoutConfig.GetTimeout == 0 {
timeoutConfig.GetTimeout = defaultTimeoutConfig.GetTimeout
}
if timeoutConfig.ManifestFetchTimeout == 0 {
timeoutConfig.ManifestFetchTimeout = defaultTimeoutConfig.ManifestFetchTimeout
}
if timeoutConfig.MaxTimeToConsistency == 0 {
timeoutConfig.MaxTimeToConsistency = defaultTimeoutConfig.MaxTimeToConsistency
}
if timeoutConfig.NamespacesMustBeReady == 0 {
timeoutConfig.NamespacesMustBeReady = defaultTimeoutConfig.NamespacesMustBeReady
}
if timeoutConfig.RequestTimeout == 0 {
timeoutConfig.RequestTimeout = defaultTimeoutConfig.RequestTimeout
}
if timeoutConfig.TLSHandshakeTimeout == 0 {
timeoutConfig.TLSHandshakeTimeout = defaultTimeoutConfig.TLSHandshakeTimeout
}
}

View File

@@ -0,0 +1,26 @@
/*
Copyright 2022 The Kubernetes 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 flags
import (
"flag"
)
var (
IngressClassName = flag.String("ingress-class", "higress", "Name of IngressClass to use for tests")
ShowDebug = flag.Bool("debug", false, "Whether to print debug logs")
CleanupBaseResources = flag.Bool("cleanup-base-resources", true, "Whether to cleanup base test resources after the run")
SupportedFeatures = flag.String("supported-features", "", "Supported features included in conformance tests suites")
ExemptFeatures = flag.String("exempt-features", "", "Exempt Features excluded from conformance tests suites")
)

View File

@@ -0,0 +1,426 @@
/*
Copyright 2022 The Kubernetes 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 http
import (
"fmt"
"net/url"
"strings"
"testing"
"time"
"github.com/alibaba/higress/test/e2e/conformance/utils/config"
"github.com/alibaba/higress/test/e2e/conformance/utils/roundtripper"
)
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
}
type AssertionResponse struct {
// ExpectedResponse defines what response the test case
// should receive.
ExpectedResponse Response
// AdditionalResponseHeaders is a set of headers
// the echoserver should set in its response.
AdditionalResponseHeaders map[string]string
// set not need to judge response has request info
ExpectedResponseNoRequest bool
}
// Request can be used as both the request to make and a means to verify
// that echoserver received the expected request. Note that multiple header
// values can be provided, as a comma-separated value.
type Request struct {
Host string
Method string
Path string
Headers map[string]string
UnfollowRedirect bool
TLSConfig *TLSConfig
}
// TLSConfig defines the TLS configuration for the client.
// When this field is set, the HTTPS protocol is used.
type TLSConfig struct {
// MinVersion specifies the minimum TLS version,
// e.g. tls.VersionTLS12.
MinVersion uint16
// MinVersion specifies the maximum TLS version,
// e.g. tls.VersionTLS13.
MaxVersion uint16
// SNI is short for Server Name Indication.
// If this field is not specified, the value will be equal to `Host`.
SNI string
// CipherSuites can specify multiple client cipher suites,
// e.g. tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA.
CipherSuites []uint16
// Certificates defines the certificate chain
Certificates Certificates
}
// Certificates contains CA and client certificate chain
type Certificates struct {
CACerts [][]byte
ClientKeyPairs []ClientKeyPair
}
// ClientKeyPair is a pair of client certificate and private key.
type ClientKeyPair struct {
ClientCert []byte
ClientKey []byte
}
// ExpectedRequest defines expected properties of a request that reaches a backend.
type ExpectedRequest struct {
Request
// AbsentHeaders are names of headers that are expected
// *not* to be present on the request.
AbsentHeaders []string
}
// Response defines expected properties of a response from a backend.
type Response struct {
StatusCode int
Headers map[string]string
AbsentHeaders []string
}
// requiredConsecutiveSuccesses is the number of requests that must succeed in a row
// for MakeRequestAndExpectEventuallyConsistentResponse to consider the response "consistent"
// before making additional assertions on the response body. If this number is not reached within
// maxTimeToConsistency, the test will fail.
const requiredConsecutiveSuccesses = 3
// MakeRequestAndExpectEventuallyConsistentResponse makes a request with the given parameters,
// understanding that the request may fail for some amount of time.
//
// 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 Assertion) {
t.Helper()
var (
scheme = "http"
protocol = "HTTP"
tlsConfig *roundtripper.TLSConfig
)
if expected.Request.ActualRequest.TLSConfig != nil {
scheme = "https"
protocol = "HTTPS"
clientKeyPairs := make([]roundtripper.ClientKeyPair, 0, len(expected.Request.ActualRequest.TLSConfig.Certificates.ClientKeyPairs))
for _, keyPair := range expected.Request.ActualRequest.TLSConfig.Certificates.ClientKeyPairs {
clientKeyPairs = append(clientKeyPairs, roundtripper.ClientKeyPair{
ClientCert: keyPair.ClientCert,
ClientKey: keyPair.ClientKey,
})
}
tlsConfig = &roundtripper.TLSConfig{
MinVersion: expected.Request.ActualRequest.TLSConfig.MinVersion,
MaxVersion: expected.Request.ActualRequest.TLSConfig.MaxVersion,
SNI: expected.Request.ActualRequest.TLSConfig.SNI,
CipherSuites: expected.Request.ActualRequest.TLSConfig.CipherSuites,
Certificates: roundtripper.Certificates{
CACert: expected.Request.ActualRequest.TLSConfig.Certificates.CACerts,
ClientKeyPairs: clientKeyPairs,
},
}
if tlsConfig.SNI == "" {
tlsConfig.SNI = expected.Request.ActualRequest.Host
}
}
if expected.Request.ActualRequest.Method == "" {
expected.Request.ActualRequest.Method = "GET"
}
if expected.Response.ExpectedResponse.StatusCode == 0 {
expected.Response.ExpectedResponse.StatusCode = 200
}
t.Logf("Making %s request to %s://%s%s", expected.Request.ActualRequest.Method, scheme, gwAddr, expected.Request.ActualRequest.Path)
path, query, _ := strings.Cut(expected.Request.ActualRequest.Path, "?")
req := roundtripper.Request{
Method: expected.Request.ActualRequest.Method,
Host: expected.Request.ActualRequest.Host,
URL: url.URL{Scheme: scheme, Host: gwAddr, Path: path, RawQuery: query},
Protocol: protocol,
Headers: map[string][]string{},
UnfollowRedirect: expected.Request.ActualRequest.UnfollowRedirect,
TLSConfig: tlsConfig,
}
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.Response.AdditionalResponseHeaders {
backendSetHeaders = append(backendSetHeaders, name+":"+val)
}
req.Headers["X-Echo-Set-Header"] = []string{strings.Join(backendSetHeaders, ",")}
WaitForConsistentResponse(t, r, req, expected, requiredConsecutiveSuccesses, timeoutConfig.MaxTimeToConsistency)
}
// awaitConvergence runs the given function until it returns 'true' `threshold` times in a row.
// Each failed attempt has a 1s delay; successful attempts have no delay.
func awaitConvergence(t *testing.T, threshold int, maxTimeToConsistency time.Duration, fn func(elapsed time.Duration) bool) {
successes := 0
attempts := 0
start := time.Now()
to := time.After(maxTimeToConsistency)
delay := time.Second
for {
select {
case <-to:
t.Fatalf("timeout while waiting after %d attempts", attempts)
default:
}
completed := fn(time.Now().Sub(start))
attempts++
if completed {
successes++
if successes >= threshold {
return
}
// Skip delay if we have a success
continue
}
successes = 0
select {
// Capture the overall timeout
case <-to:
t.Fatalf("timeout while waiting after %d attempts, %d/%d sucessess", attempts, successes, threshold)
// And the per-try delay
case <-time.After(delay):
}
}
}
// 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 Assertion, threshold int, maxTimeToConsistency time.Duration) {
awaitConvergence(t, threshold, maxTimeToConsistency, func(elapsed time.Duration) bool {
cReq, cRes, err := r.CaptureRoundTrip(req)
if err != nil {
t.Logf("Request failed, not ready yet: %v (after %v)", err.Error(), elapsed)
return false
}
if err := CompareRequest(&req, cReq, cRes, expected); err != nil {
t.Logf("Response expectation failed for request: %v not ready yet: %v (after %v)", req, err, elapsed)
return false
}
return true
})
t.Logf("Request passed")
}
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 && !expected.Response.ExpectedResponseNoRequest {
// The request expected to arrive at the backend is
// the same as the request made, unless otherwise
// specified.
if expected.Request.ExpectedRequest == nil {
expected.Request.ExpectedRequest = &ExpectedRequest{Request: expected.Request.ActualRequest}
}
if expected.Request.ExpectedRequest.Method == "" {
expected.Request.ExpectedRequest.Method = "GET"
}
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.Request.ExpectedRequest.Path != cReq.Path {
return fmt.Errorf("expected path to be %s, got %s", expected.Request.ExpectedRequest.Path, cReq.Path)
}
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.Meta.TargetNamespace != cReq.Namespace {
return fmt.Errorf("expected namespace to be %s, got %s", expected.Meta.TargetNamespace, cReq.Namespace)
}
if expected.Request.ExpectedRequest.Headers != nil {
if cReq.Headers == nil {
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.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)
} else if strings.Join(actualVal, ",") != expectedVal {
return fmt.Errorf("expected %s header to be set to %s, got %s", name, expectedVal, strings.Join(actualVal, ","))
}
}
}
if expected.Response.ExpectedResponse.Headers != nil {
if cRes.Headers == nil {
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.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)
} else if strings.Join(actualVal, ",") != expectedVal {
return fmt.Errorf("expected %s header to be set to %s, got %s", name, expectedVal, strings.Join(actualVal, ","))
}
}
}
if len(expected.Response.ExpectedResponse.AbsentHeaders) > 0 {
for name, val := range cRes.Headers {
cRes.Headers[strings.ToLower(name)] = val
}
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)
}
}
}
// Verify that headers expected *not* to be present on the
// request are actually not present.
if len(expected.Request.ExpectedRequest.AbsentHeaders) > 0 {
for name, val := range cReq.Headers {
cReq.Headers[strings.ToLower(name)] = val
}
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)
}
}
}
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.Request.RedirectRequest == nil {
return nil
}
setRedirectRequestDefaults(req, cRes, &expected)
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.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.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.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 *Assertion) GetTestCaseName(i int) string {
// If TestCase name is provided then use that or else generate one.
if er.Meta.TestCaseName != "" {
return er.Meta.TestCaseName
}
headerStr := ""
reqStr := ""
if er.Request.ActualRequest.Headers != nil {
headerStr = " with headers"
}
reqStr = fmt.Sprintf("%d request to '%s%s'%s", i, er.Request.ActualRequest.Host, er.Request.ActualRequest.Path, headerStr)
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.ExpectedResponse.StatusCode)
}
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.Request.RedirectRequest.Host == "" {
expected.Request.RedirectRequest.Host = cRes.RedirectRequest.Host
}
if expected.Request.RedirectRequest.Port == "" {
expected.Request.RedirectRequest.Port = req.URL.Port()
}
if expected.Request.RedirectRequest.Scheme == "" {
expected.Request.RedirectRequest.Scheme = req.URL.Scheme
}
if expected.Request.RedirectRequest.Path == "" {
expected.Request.RedirectRequest.Path = req.URL.Path
}
}

View File

@@ -0,0 +1,218 @@
/*
Copyright 2022 The Kubernetes 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 kubernetes
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"net/http"
"strings"
"testing"
ingress "github.com/alibaba/higress/test/e2e/conformance"
"github.com/stretchr/testify/require"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/yaml"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/alibaba/higress/test/e2e/conformance/utils/config"
)
// Applier prepares manifests depending on the available options and applies
// them to the Kubernetes cluster.
type Applier struct {
NamespaceLabels map[string]string
// IngressClass will be used as the spec.ingressClassName when applying Ingress resources
IngressClass string
}
// prepareNamespace adjusts the Namespace labels.
func prepareNamespace(t *testing.T, uObj *unstructured.Unstructured, namespaceLabels map[string]string) {
labels, _, err := unstructured.NestedStringMap(uObj.Object, "metadata", "labels")
require.NoErrorf(t, err, "error getting labels on Namespace %s", uObj.GetName())
for k, v := range namespaceLabels {
if labels == nil {
labels = map[string]string{}
}
labels[k] = v
}
// SetNestedStringMap converts nil to an empty map
if labels != nil {
err = unstructured.SetNestedStringMap(uObj.Object, labels, "metadata", "labels")
}
require.NoErrorf(t, err, "error setting labels on Namespace %s", uObj.GetName())
}
// prepareResources uses the options from an Applier to tweak resources given by
// a set of manifests.
func (a Applier) prepareResources(t *testing.T, decoder *yaml.YAMLOrJSONDecoder) ([]unstructured.Unstructured, error) {
var resources []unstructured.Unstructured
for {
uObj := unstructured.Unstructured{}
if err := decoder.Decode(&uObj); err != nil {
if errors.Is(err, io.EOF) {
break
}
return nil, err
}
if len(uObj.Object) == 0 {
continue
}
if uObj.GetKind() == "Namespace" && uObj.GetObjectKind().GroupVersionKind().Group == "" {
prepareNamespace(t, &uObj, a.NamespaceLabels)
}
resources = append(resources, uObj)
}
return resources, nil
}
func (a Applier) MustApplyObjectsWithCleanup(t *testing.T, c client.Client, timeoutConfig config.TimeoutConfig, resources []client.Object, cleanup bool) {
for _, resource := range resources {
resource := resource
ctx, cancel := context.WithTimeout(context.Background(), timeoutConfig.CreateTimeout)
defer cancel()
t.Logf("Creating %s %s", resource.GetName(), resource.GetObjectKind().GroupVersionKind().Kind)
err := c.Create(ctx, resource)
if err != nil {
if !apierrors.IsAlreadyExists(err) {
require.NoError(t, err, "error creating resource")
}
}
if cleanup {
t.Cleanup(func() {
ctx, cancel = context.WithTimeout(context.Background(), timeoutConfig.DeleteTimeout)
defer cancel()
t.Logf("Deleting %s %s", resource.GetName(), resource.GetObjectKind().GroupVersionKind().Kind)
err = c.Delete(ctx, resource)
require.NoErrorf(t, err, "error deleting resource")
})
}
}
}
// MustApplyWithCleanup creates or updates Kubernetes resources defined with the
// provided YAML file and registers a cleanup function for resources it created.
// Note that this does not remove resources that already existed in the cluster.
func (a Applier) MustApplyWithCleanup(t *testing.T, c client.Client, timeoutConfig config.TimeoutConfig, location string, cleanup bool) {
data, err := getContentsFromPathOrURL(location, timeoutConfig)
require.NoError(t, err)
decoder := yaml.NewYAMLOrJSONDecoder(data, 4096)
resources, err := a.prepareResources(t, decoder)
if err != nil {
t.Logf("manifest: %s", data.String())
require.NoErrorf(t, err, "error parsing manifest")
}
for i := range resources {
uObj := &resources[i]
ctx, cancel := context.WithTimeout(context.Background(), timeoutConfig.CreateTimeout)
defer cancel()
namespacedName := types.NamespacedName{Namespace: uObj.GetNamespace(), Name: uObj.GetName()}
fetchedObj := uObj.DeepCopy()
err := c.Get(ctx, namespacedName, fetchedObj)
if err != nil {
if !apierrors.IsNotFound(err) {
require.NoErrorf(t, err, "error getting resource")
}
t.Logf("Creating %s %s", uObj.GetName(), uObj.GetKind())
err = c.Create(ctx, uObj)
require.NoErrorf(t, err, "error creating resource")
if cleanup {
t.Cleanup(func() {
ctx, cancel = context.WithTimeout(context.Background(), timeoutConfig.DeleteTimeout)
defer cancel()
t.Logf("Deleting %s %s", uObj.GetName(), uObj.GetKind())
err = c.Delete(ctx, uObj)
require.NoErrorf(t, err, "error deleting resource")
})
}
continue
}
uObj.SetResourceVersion(fetchedObj.GetResourceVersion())
t.Logf("Updating %s %s", uObj.GetName(), uObj.GetKind())
err = c.Update(ctx, uObj)
if cleanup {
t.Cleanup(func() {
ctx, cancel = context.WithTimeout(context.Background(), timeoutConfig.DeleteTimeout)
defer cancel()
t.Logf("Deleting %s %s", uObj.GetName(), uObj.GetKind())
err = c.Delete(ctx, uObj)
require.NoErrorf(t, err, "error deleting resource")
})
}
require.NoErrorf(t, err, "error updating resource")
}
}
// getContentsFromPathOrURL takes a string that can either be a local file
// path or an https:// URL to YAML manifests and provides the contents.
func getContentsFromPathOrURL(location string, timeoutConfig config.TimeoutConfig) (*bytes.Buffer, error) {
if strings.HasPrefix(location, "http://") {
return nil, fmt.Errorf("data can't be retrieved from %s: http is not supported, use https", location)
} else if strings.HasPrefix(location, "https://") {
ctx, cancel := context.WithTimeout(context.Background(), timeoutConfig.ManifestFetchTimeout)
defer cancel()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, location, nil)
if err != nil {
return nil, err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
manifests := new(bytes.Buffer)
count, err := manifests.ReadFrom(resp.Body)
if err != nil {
return nil, err
}
if resp.ContentLength != -1 && count != resp.ContentLength {
return nil, fmt.Errorf("received %d bytes from %s, expected %d", count, location, resp.ContentLength)
}
return manifests, nil
}
b, err := ingress.Manifests.ReadFile(location)
if err != nil {
return nil, err
}
return bytes.NewBuffer(b), nil
}

View File

@@ -0,0 +1,18 @@
/*
Copyright 2022 The Kubernetes 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 kubernetes
import (
_ "github.com/alibaba/higress/test/e2e/conformance/utils/flags"
)

View File

@@ -0,0 +1,123 @@
/*
Copyright 2022 The Kubernetes 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 kubernetes
import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"fmt"
"io"
"math/big"
"net"
"strings"
"testing"
"time"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
// ensure auth plugins are loaded
_ "k8s.io/client-go/plugin/pkg/client/auth"
"github.com/alibaba/higress/test/e2e/conformance/utils/cert"
)
// MustCreateSelfSignedCertSecret creates a self-signed SSL certificate and stores it in a secret
func MustCreateSelfSignedCertSecret(t *testing.T, namespace, secretName string, hosts []string) *corev1.Secret {
require.Greater(t, len(hosts), 0, "require a non-empty hosts for Subject Alternate Name values")
var serverKey, serverCert bytes.Buffer
host := strings.Join(hosts, ",")
require.NoError(t, generateRSACert(host, &serverKey, &serverCert), "failed to generate RSA certificate")
return ConstructTLSSecret(namespace, secretName, serverCert.Bytes(), serverKey.Bytes())
}
// ConstructTLSSecret constructs a secret of type "kubernetes.io/tls"
func ConstructTLSSecret(namespace, secretName string, cert, key []byte) *corev1.Secret {
return &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
Name: secretName,
},
Type: corev1.SecretTypeTLS,
Data: map[string][]byte{
corev1.TLSCertKey: cert,
corev1.TLSPrivateKeyKey: key,
},
}
}
// ConstructCASecret construct a CA secret of type "Opaque"
func ConstructCASecret(namespace, secretName string, cert []byte) *corev1.Secret {
return &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
Name: secretName,
},
Type: corev1.SecretTypeOpaque,
Data: map[string][]byte{
corev1.ServiceAccountRootCAKey: cert,
},
}
}
// generateRSACert generates a basic self signed certificate valid for a year
func generateRSACert(host string, keyOut, certOut io.Writer) error {
notBefore := time.Now()
notAfter := notBefore.Add(cert.ValidFor)
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
if err != nil {
return fmt.Errorf("failed to generate serial number: %w", err)
}
template := &x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{
CommonName: "default",
Organization: []string{"Higress E2E Test"},
},
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}
hosts := strings.Split(host, ",")
for _, h := range hosts {
if ip := net.ParseIP(h); ip != nil {
template.IPAddresses = append(template.IPAddresses, ip)
} else {
template.DNSNames = append(template.DNSNames, h)
}
}
priv, err := rsa.GenerateKey(rand.Reader, cert.RSABits)
if err != nil {
return fmt.Errorf("failed to generate key: %w", err)
}
certOut, keyOut, err = cert.GenerateCert(template, priv, template, nil)
if err != nil {
return fmt.Errorf("failed to generate rsa certificate: %w", err)
}
return nil
}

View File

@@ -0,0 +1,121 @@
/*
Copyright 2022 The Kubernetes 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 kubernetes
import (
"context"
"strings"
"testing"
"time"
"github.com/stretchr/testify/require"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/alibaba/higress/test/e2e/conformance/utils/config"
)
// FilterStaleConditions returns the list of status condition whos observedGeneration does not
// match the objects metadata.Generation
func FilterStaleConditions(obj metav1.Object, conditions []metav1.Condition) []metav1.Condition {
stale := make([]metav1.Condition, 0, len(conditions))
for _, condition := range conditions {
if obj.GetGeneration() != condition.ObservedGeneration {
stale = append(stale, condition)
}
}
return stale
}
// NamespacesMustBeAccepted waits until all Pods are marked ready
// in the provided namespaces. This will cause the test to
// halt if the specified timeout is exceeded.
func NamespacesMustBeAccepted(t *testing.T, c client.Client, timeoutConfig config.TimeoutConfig, namespaces []string) {
t.Helper()
waitErr := wait.PollImmediate(1*time.Second, timeoutConfig.NamespacesMustBeReady, func() (bool, error) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
for _, ns := range namespaces {
podList := &v1.PodList{}
err := c.List(ctx, podList, client.InNamespace(ns))
if err != nil {
t.Errorf("Error listing Pods: %v", err)
}
for _, pod := range podList.Items {
if !FindPodConditionInList(t, pod.Status.Conditions, "Ready", "True") &&
pod.Status.Phase != v1.PodSucceeded {
t.Logf("%s/%s Pod not ready yet", ns, pod.Name)
return false, nil
}
}
}
t.Logf("Gateways and Pods in %s namespaces ready", strings.Join(namespaces, ", "))
return true, nil
})
require.NoErrorf(t, waitErr, "error waiting for %s namespaces to be ready", strings.Join(namespaces, ", "))
}
func ConditionsMatch(t *testing.T, expected, actual []metav1.Condition) bool {
if len(actual) < len(expected) {
t.Logf("Expected more conditions to be present")
return false
}
for _, condition := range expected {
if !FindConditionInList(t, actual, condition.Type, string(condition.Status), condition.Reason) {
return false
}
}
t.Logf("Conditions matched expectations")
return true
}
// findConditionInList finds a condition in a list of Conditions, checking
// the Name, Value, and Reason. If an empty reason is passed, any Reason will match.
func FindConditionInList(t *testing.T, conditions []metav1.Condition, condName, expectedStatus, expectedReason string) bool {
for _, cond := range conditions {
if cond.Type == condName {
if cond.Status == metav1.ConditionStatus(expectedStatus) {
// an empty Reason string means "Match any reason".
if expectedReason == "" || cond.Reason == expectedReason {
return true
}
t.Logf("%s condition Reason set to %s, expected %s", condName, cond.Reason, expectedReason)
}
t.Logf("%s condition set to Status %s with Reason %v, expected Status %s", condName, cond.Status, cond.Reason, expectedStatus)
}
}
t.Logf("%s was not in conditions list", condName)
return false
}
func FindPodConditionInList(t *testing.T, conditions []v1.PodCondition, condName, condValue string) bool {
for _, cond := range conditions {
if cond.Type == v1.PodConditionType(condName) {
if cond.Status == v1.ConditionStatus(condValue) {
return true
}
t.Logf("%s condition set to %s, expected %s", condName, cond.Status, condValue)
}
}
t.Logf("%s was not in conditions list", condName)
return false
}

View File

@@ -0,0 +1,253 @@
/*
Copyright 2022 The Kubernetes 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 roundtripper
import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/json"
"fmt"
"io"
"net/http"
"net/http/httputil"
"net/url"
"regexp"
"github.com/alibaba/higress/test/e2e/conformance/utils/config"
)
// RoundTripper is an interface used to make requests within conformance tests.
// This can be overridden with custom implementations whenever necessary.
type RoundTripper interface {
CaptureRoundTrip(Request) (*CapturedRequest, *CapturedResponse, error)
}
// Request is the primary input for making a request.
type Request struct {
URL url.URL
Host string
Protocol string
Method string
Headers map[string][]string
UnfollowRedirect bool
TLSConfig *TLSConfig
}
// TLSConfig defines the TLS configuration for the client.
// When this field is set, the HTTPS protocol is used.
type TLSConfig struct {
MinVersion uint16
MaxVersion uint16
SNI string
CipherSuites []uint16
Certificates Certificates
}
// Certificates defines the self-signed client and CA certificate chain
type Certificates struct {
CACert [][]byte
ClientKeyPairs []ClientKeyPair
}
// ClientKeyPair is a pair of client certificate and private key.
type ClientKeyPair struct {
ClientCert []byte
ClientKey []byte
}
// CapturedRequest contains request metadata captured from an echoserver
// response.
type CapturedRequest struct {
Path string `json:"path"`
Host string `json:"host"`
Method string `json:"method"`
Protocol string `json:"proto"`
Headers map[string][]string `json:"headers"`
Namespace string `json:"namespace"`
Pod string `json:"pod"`
}
// RedirectRequest contains a follow up request metadata captured from a redirect
// response.
type RedirectRequest struct {
Scheme string
Host string
Port string
Path string
}
// CapturedResponse contains response metadata.
type CapturedResponse struct {
StatusCode int
ContentLength int64
Protocol string
Headers map[string][]string
RedirectRequest *RedirectRequest
}
// DefaultRoundTripper is the default implementation of a RoundTripper. It will
// be used if a custom implementation is not specified.
type DefaultRoundTripper struct {
Debug bool
TimeoutConfig config.TimeoutConfig
}
// CaptureRoundTrip makes a request with the provided parameters and returns the
// captured request and response from echoserver. An error will be returned if
// there is an error running the function but not if an HTTP error status code
// is received.
func (d *DefaultRoundTripper) CaptureRoundTrip(request Request) (*CapturedRequest, *CapturedResponse, error) {
cReq := &CapturedRequest{}
client := &http.Client{}
if request.UnfollowRedirect {
client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}
}
if request.TLSConfig != nil {
pool := x509.NewCertPool()
for _, caCert := range request.TLSConfig.Certificates.CACert {
pool.AppendCertsFromPEM(caCert)
}
var clientCerts []tls.Certificate
for _, keyPair := range request.TLSConfig.Certificates.ClientKeyPairs {
newClientCert, err := tls.X509KeyPair(keyPair.ClientCert, keyPair.ClientKey)
if err != nil {
return nil, nil, fmt.Errorf("failed to load client key pair: %w", err)
}
clientCerts = append(clientCerts, newClientCert)
}
client.Transport = &http.Transport{
TLSHandshakeTimeout: d.TimeoutConfig.TLSHandshakeTimeout,
DisableKeepAlives: true,
TLSClientConfig: &tls.Config{
MinVersion: request.TLSConfig.MinVersion,
MaxVersion: request.TLSConfig.MaxVersion,
ServerName: request.TLSConfig.SNI,
CipherSuites: request.TLSConfig.CipherSuites,
RootCAs: pool,
Certificates: clientCerts,
InsecureSkipVerify: true,
},
}
}
method := "GET"
if request.Method != "" {
method = request.Method
}
ctx, cancel := context.WithTimeout(context.Background(), d.TimeoutConfig.RequestTimeout)
defer cancel()
req, err := http.NewRequestWithContext(ctx, method, request.URL.String(), nil)
if err != nil {
return nil, nil, err
}
if request.Host != "" {
req.Host = request.Host
}
if request.Headers != nil {
for name, value := range request.Headers {
req.Header.Set(name, value[0])
}
}
if d.Debug {
var dump []byte
dump, err = httputil.DumpRequestOut(req, true)
if err != nil {
return nil, nil, err
}
fmt.Printf("Sending Request:\n%s\n\n", formatDump(dump, "< "))
}
resp, err := client.Do(req)
if err != nil {
return nil, nil, err
}
defer client.CloseIdleConnections()
defer resp.Body.Close()
if d.Debug {
var dump []byte
dump, err = httputil.DumpResponse(resp, true)
if err != nil {
return nil, nil, err
}
fmt.Printf("Received Response:\n%s\n\n", formatDump(dump, "< "))
}
body, _ := io.ReadAll(resp.Body)
// we cannot assume the response is JSON
if resp.Header.Get("Content-type") == "application/json" {
err = json.Unmarshal(body, cReq)
if err != nil {
return nil, nil, fmt.Errorf("unexpected error reading response: %w", err)
}
}
cRes := &CapturedResponse{
StatusCode: resp.StatusCode,
ContentLength: resp.ContentLength,
Protocol: resp.Proto,
Headers: resp.Header,
}
if IsRedirect(resp.StatusCode) {
redirectURL, err := resp.Location()
if err != nil {
return nil, nil, err
}
cRes.RedirectRequest = &RedirectRequest{
Scheme: redirectURL.Scheme,
Host: redirectURL.Hostname(),
Port: redirectURL.Port(),
Path: redirectURL.Path,
}
}
return cReq, cRes, nil
}
// IsRedirect returns true if a given status code is a redirect code.
func IsRedirect(statusCode int) bool {
switch statusCode {
case http.StatusMultipleChoices,
http.StatusMovedPermanently,
http.StatusFound,
http.StatusSeeOther,
http.StatusNotModified,
http.StatusUseProxy,
http.StatusTemporaryRedirect,
http.StatusPermanentRedirect:
return true
}
return false
}
var startLineRegex = regexp.MustCompile(`(?m)^`)
func formatDump(data []byte, prefix string) string {
data = startLineRegex.ReplaceAllLiteral(data, []byte(prefix))
return string(data)
}

View File

@@ -0,0 +1,157 @@
/*
Copyright 2022 The Kubernetes 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 suite
import (
"fmt"
"testing"
"github.com/alibaba/higress/test/e2e/conformance/utils/config"
"github.com/alibaba/higress/test/e2e/conformance/utils/kubernetes"
"github.com/alibaba/higress/test/e2e/conformance/utils/roundtripper"
"sigs.k8s.io/controller-runtime/pkg/client"
)
// ConformanceTestSuite defines the test suite used to run Gateway API
// conformance tests.
type ConformanceTestSuite struct {
Client client.Client
RoundTripper roundtripper.RoundTripper
GatewayAddress string
IngressClassName string
Debug bool
Cleanup bool
BaseManifests string
Applier kubernetes.Applier
TimeoutConfig config.TimeoutConfig
}
// Options can be used to initialize a ConformanceTestSuite.
type Options struct {
Client client.Client
GatewayAddress string
IngressClassName string
Debug bool
RoundTripper roundtripper.RoundTripper
BaseManifests string
NamespaceLabels map[string]string
// CleanupBaseResources indicates whether or not the base test
// resources such as Gateways should be cleaned up after the run.
CleanupBaseResources bool
TimeoutConfig config.TimeoutConfig
}
// New returns a new ConformanceTestSuite.
func New(s Options) *ConformanceTestSuite {
config.SetupTimeoutConfig(&s.TimeoutConfig)
roundTripper := s.RoundTripper
if roundTripper == nil {
roundTripper = &roundtripper.DefaultRoundTripper{Debug: s.Debug, TimeoutConfig: s.TimeoutConfig}
}
suite := &ConformanceTestSuite{
Client: s.Client,
RoundTripper: roundTripper,
IngressClassName: s.IngressClassName,
Debug: s.Debug,
Cleanup: s.CleanupBaseResources,
BaseManifests: s.BaseManifests,
GatewayAddress: s.GatewayAddress,
Applier: kubernetes.Applier{
NamespaceLabels: s.NamespaceLabels,
},
TimeoutConfig: s.TimeoutConfig,
}
// apply defaults
if suite.BaseManifests == "" {
suite.BaseManifests = "base/manifests.yaml"
}
return suite
}
// Setup ensures the base resources required for conformance tests are installed
// in the cluster. It also ensures that all relevant resources are ready.
func (suite *ConformanceTestSuite) Setup(t *testing.T) {
t.Logf("Test Setup: Ensuring IngressClass has been accepted")
suite.Applier.IngressClass = suite.IngressClassName
t.Logf("Test Setup: Applying base manifests")
suite.Applier.MustApplyWithCleanup(t, suite.Client, suite.TimeoutConfig, suite.BaseManifests, suite.Cleanup)
t.Logf("Test Setup: Applying programmatic resources")
secret := kubernetes.MustCreateSelfSignedCertSecret(t, "higress-conformance-web-backend", "certificate", []string{"*"})
suite.Applier.MustApplyObjectsWithCleanup(t, suite.Client, suite.TimeoutConfig, []client.Object{secret}, suite.Cleanup)
secret = kubernetes.MustCreateSelfSignedCertSecret(t, "higress-conformance-infra", "tls-validity-checks-certificate", []string{"*"})
suite.Applier.MustApplyObjectsWithCleanup(t, suite.Client, suite.TimeoutConfig, []client.Object{secret}, suite.Cleanup)
t.Logf("Test Setup: Ensuring Pods from base manifests are ready")
namespaces := []string{
"higress-conformance-infra",
"higress-conformance-app-backend",
"higress-conformance-web-backend",
"nacos-standlone-rc3",
"dubbo-demo-provider",
}
kubernetes.NamespacesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, namespaces)
}
// Run runs the provided set of conformance tests.
func (suite *ConformanceTestSuite) Run(t *testing.T, tests []ConformanceTest) {
t.Logf("Start Running %d Test Cases: \n\n%s", len(tests), globalConformanceTestsListInfo(tests))
for _, test := range tests {
t.Run(test.ShortName, func(t *testing.T) {
test.Run(t, suite)
})
}
}
func globalConformanceTestsListInfo(tests []ConformanceTest) string {
var cases string
for index, test := range tests {
cases += fmt.Sprintf("CaseNum: %d\nCaseName: %s\nScenario: %s\n\n", index+1, test.ShortName, test.Description)
}
return cases
}
// ConformanceTest is used to define each individual conformance test.
type ConformanceTest struct {
ShortName string
Description string
Manifests []string
Slow bool
Parallel bool
Test func(*testing.T, *ConformanceTestSuite)
}
// Run runs an individual tests, applying and cleaning up the required manifests
// before calling the Test function.
func (test *ConformanceTest) Run(t *testing.T, suite *ConformanceTestSuite) {
if test.Parallel {
t.Parallel()
}
for _, manifestLocation := range test.Manifests {
t.Logf("Applying %s", manifestLocation)
suite.Applier.MustApplyWithCleanup(t, suite.Client, suite.TimeoutConfig, manifestLocation, true)
}
test.Test(t, suite)
}