mirror of
https://github.com/alibaba/higress.git
synced 2026-02-26 21:50:53 +08:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc54c581f3 | ||
|
|
b47d74bce5 | ||
|
|
8d8ad6d624 | ||
|
|
8062625d75 | ||
|
|
54a8a906ae | ||
|
|
8659895a91 | ||
|
|
dc3e496aa0 | ||
|
|
8747e1ddad | ||
|
|
2b9e3a14c2 | ||
|
|
1051201e97 | ||
|
|
8b24a20651 |
6
.github/workflows/build-and-test.yaml
vendored
6
.github/workflows/build-and-test.yaml
vendored
@@ -147,6 +147,10 @@ jobs:
|
||||
higress-wasmplugin-test:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build]
|
||||
strategy:
|
||||
matrix:
|
||||
# TODO(Xunzhuo): Enable C WASM Filters in CI
|
||||
wasmPluginType: [ GO ]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -179,7 +183,7 @@ jobs:
|
||||
- run: git stash # restore patch
|
||||
|
||||
- name: "Run Ingress WasmPlugins Tests"
|
||||
run: GOPROXY="https://proxy.golang.org,direct" make higress-wasmplugin-test
|
||||
run: GOPROXY="https://proxy.golang.org,direct" PLUGIN_TYPE=${{ matrix.wasmPluginType }} make higress-wasmplugin-test
|
||||
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -172,10 +172,9 @@ ENVOY_LATEST_IMAGE_TAG ?= 1.1.1
|
||||
ISTIO_LATEST_IMAGE_TAG ?= 1.1.1
|
||||
|
||||
install-dev: pre-install
|
||||
helm install higress helm/core -n higress-system --create-namespace --set 'controller.tag=$(TAG)' --set 'gateway.replicas=1' --set 'gateway.tag=$(ENVOY_LATEST_IMAGE_TAG)' --set 'global.local=true'
|
||||
|
||||
helm install higress helm/core -n higress-system --create-namespace --set 'controller.tag=$(TAG)' --set 'gateway.replicas=1' --set 'pilot.tag=$(ISTIO_LATEST_IMAGE_TAG)' --set 'gateway.tag=$(ENVOY_LATEST_IMAGE_TAG)' --set 'global.local=true'
|
||||
install-dev-wasmplugin: build-wasmplugins pre-install
|
||||
helm install higress helm/core -n higress-system --create-namespace --set 'controller.tag=$(TAG)' --set 'gateway.replicas=1' --set 'gateway.tag=$(ENVOY_LATEST_IMAGE_TAG)' --set 'global.local=true' --set 'global.volumeWasmPlugins=true'
|
||||
helm install higress helm/core -n higress-system --create-namespace --set 'controller.tag=$(TAG)' --set 'gateway.replicas=1' --set 'pilot.tag=$(ISTIO_LATEST_IMAGE_TAG)' --set 'gateway.tag=$(ENVOY_LATEST_IMAGE_TAG)' --set 'global.local=true' --set 'global.volumeWasmPlugins=true'
|
||||
|
||||
uninstall:
|
||||
helm uninstall higress -n higress-system
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
apiVersion: v2
|
||||
appVersion: 1.1.1
|
||||
appVersion: 1.1.2
|
||||
description: Helm chart for deploying higress gateways
|
||||
icon: https://higress.io/img/higress_logo_small.png
|
||||
home: http://higress.io/
|
||||
@@ -10,4 +10,4 @@ name: higress-core
|
||||
sources:
|
||||
- http://github.com/alibaba/higress
|
||||
type: application
|
||||
version: 1.1.1
|
||||
version: 1.1.2
|
||||
|
||||
@@ -33,7 +33,7 @@ spec:
|
||||
{{- if contains "/" .Values.pilot.image }}
|
||||
image: "{{ .Values.pilot.image }}"
|
||||
{{- else }}
|
||||
image: "{{ .Values.pilot.hub | default .Values.global.hub }}/{{ .Values.pilot.image | default "pilot" }}:{{ .Values.pilot.tag | default .Values.global.tag }}"
|
||||
image: "{{ .Values.pilot.hub | default .Values.global.hub }}/{{ .Values.pilot.image | default "pilot" }}:{{ .Values.pilot.tag | default .Chart.AppVersion }}"
|
||||
{{- end }}
|
||||
{{- if .Values.global.imagePullPolicy }}
|
||||
imagePullPolicy: {{ .Values.global.imagePullPolicy }}
|
||||
|
||||
@@ -44,8 +44,6 @@ global:
|
||||
# Releases are published to docker hub under 'istio' project.
|
||||
# Dev builds from prow are on gcr.io
|
||||
hub: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress
|
||||
# Default tag for Istio images.
|
||||
tag: 1.1.1
|
||||
|
||||
# Specify image pull policy if default behavior isn't desired.
|
||||
# Default behavior: latest images will be Always else IfNotPresent.
|
||||
@@ -369,7 +367,7 @@ gateway:
|
||||
name: "higress-gateway"
|
||||
replicas: 2
|
||||
image: gateway
|
||||
tag: "1.1.1"
|
||||
tag: ""
|
||||
# revision declares which revision this gateway is a part of
|
||||
revision: ""
|
||||
|
||||
@@ -457,7 +455,7 @@ controller:
|
||||
name: "higress-controller"
|
||||
replicas: 1
|
||||
image: higress
|
||||
tag: "1.1.1"
|
||||
tag: ""
|
||||
env: {}
|
||||
|
||||
labels: {}
|
||||
@@ -547,7 +545,7 @@ pilot:
|
||||
rollingMaxUnavailable: 25%
|
||||
|
||||
hub: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress
|
||||
tag: 1.1.1
|
||||
tag: ""
|
||||
|
||||
# Can be a full hub/image:tag
|
||||
image: pilot
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
dependencies:
|
||||
- name: higress-core
|
||||
repository: file://../core
|
||||
version: 1.1.1
|
||||
version: 1.1.2
|
||||
- name: higress-console
|
||||
repository: https://higress.io/helm-charts/
|
||||
version: 1.1.1
|
||||
digest: sha256:dd74a69c4031fa3e7798233602b44f0da3f657cbb40c61754298fbc877be2ae6
|
||||
generated: "2023-08-10T10:54:46.8520756+08:00"
|
||||
version: 1.1.2
|
||||
digest: sha256:8fc099c4ad77bcdc4b9dde2ef14f89b2159b6fdcc49a3dc7e1cccb01a7ed99b9
|
||||
generated: "2023-09-19T21:46:20.2567789+08:00"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
apiVersion: v2
|
||||
appVersion: 1.1.1
|
||||
appVersion: 1.1.2
|
||||
description: Helm chart for deploying Higress gateways
|
||||
icon: https://higress.io/img/higress_logo_small.png
|
||||
home: http://higress.io/
|
||||
@@ -12,9 +12,9 @@ sources:
|
||||
dependencies:
|
||||
- name: higress-core
|
||||
repository: "file://../core"
|
||||
version: 1.1.1
|
||||
version: 1.1.2
|
||||
- name: higress-console
|
||||
repository: "https://higress.io/helm-charts/"
|
||||
version: 1.1.1
|
||||
version: 1.1.2
|
||||
type: application
|
||||
version: 1.1.1
|
||||
version: 1.1.2
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
"github.com/gogo/protobuf/jsonpb"
|
||||
"github.com/gogo/protobuf/types"
|
||||
"github.com/golang/protobuf/ptypes/wrappers"
|
||||
"go.uber.org/atomic"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
extensions "istio.io/api/extensions/v1alpha1"
|
||||
networking "istio.io/api/networking/v1alpha3"
|
||||
@@ -109,7 +110,7 @@ type IngressConfig struct {
|
||||
|
||||
RegistryReconciler *reconcile.Reconciler
|
||||
|
||||
mcpbridgeReconciled bool
|
||||
mcpbridgeReconciled *atomic.Bool
|
||||
|
||||
mcpbridgeController mcpbridge.McpBridgeController
|
||||
|
||||
@@ -154,7 +155,7 @@ func NewIngressConfig(localKubeClient kube.Client, XDSUpdater model.XDSUpdater,
|
||||
common.CreateConvertedName(clusterId, "global"),
|
||||
watchedSecretSet: sets.NewSet(),
|
||||
namespace: namespace,
|
||||
mcpbridgeReconciled: true,
|
||||
mcpbridgeReconciled: atomic.NewBool(true),
|
||||
wasmPlugins: make(map[string]*extensions.WasmPlugin),
|
||||
http2rpcs: make(map[string]*higressv1.Http2Rpc),
|
||||
}
|
||||
@@ -947,9 +948,7 @@ func (m *IngressConfig) AddOrUpdateMcpBridge(clusterNamespacedName util.ClusterN
|
||||
clusterNamespacedName.Namespace, clusterNamespacedName.Name)
|
||||
return
|
||||
}
|
||||
m.mutex.Lock()
|
||||
m.mcpbridgeReconciled = false
|
||||
m.mutex.Unlock()
|
||||
m.mcpbridgeReconciled.Store(false)
|
||||
if m.RegistryReconciler == nil {
|
||||
m.RegistryReconciler = reconcile.NewReconciler(func() {
|
||||
metadata := config.Meta{
|
||||
@@ -966,12 +965,12 @@ func (m *IngressConfig) AddOrUpdateMcpBridge(clusterNamespacedName util.ClusterN
|
||||
}, m.localKubeClient, m.namespace)
|
||||
}
|
||||
reconciler := m.RegistryReconciler
|
||||
go func() {
|
||||
reconciler.Reconcile(mcpbridge)
|
||||
m.mutex.Lock()
|
||||
m.mcpbridgeReconciled = true
|
||||
m.mutex.Unlock()
|
||||
}()
|
||||
err = reconciler.Reconcile(mcpbridge)
|
||||
if err != nil {
|
||||
IngressLog.Errorf("Mcpbridge reconcile failed, err:%v", err)
|
||||
return
|
||||
}
|
||||
m.mcpbridgeReconciled.Store(true)
|
||||
}
|
||||
|
||||
func (m *IngressConfig) DeleteMcpBridge(clusterNamespacedName util.ClusterNamespacedName) {
|
||||
@@ -1405,7 +1404,7 @@ func (m *IngressConfig) HasSynced() bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if !m.mcpbridgeController.HasSynced() || !m.mcpbridgeReconciled {
|
||||
if !m.mcpbridgeController.HasSynced() || !m.mcpbridgeReconciled.Load() {
|
||||
return false
|
||||
}
|
||||
if !m.wasmPluginController.HasSynced() {
|
||||
|
||||
@@ -13,6 +13,7 @@ $ PLUGIN_NAME=request_block make build
|
||||
<details>
|
||||
<summary>Output</summary>
|
||||
<pre><code>
|
||||
|
||||
DOCKER_BUILDKIT=1 docker build --build-arg PLUGIN_NAME=request_block \
|
||||
-t request_block:20230721-141120-aa17e95 \
|
||||
--output extensions/request_block \
|
||||
@@ -106,16 +107,17 @@ spec:
|
||||
|
||||
The rules will be matched in the order of configuration. If one match is found, it will stop, and the matching configuration will take effect.
|
||||
|
||||
|
||||
## E2E test
|
||||
|
||||
When you complete a GO plug-in function, you can create associated e2e test cases at the same time, and complete the test verification of the plug-in function locally.
|
||||
|
||||
### step1. write test cases
|
||||
|
||||
In the directory of `./ test/e2e/conformance/tests/`, add the xxx.yaml file and xxx.go file. Such as test for `request-block` wasm-plugin,
|
||||
|
||||
./test/e2e/conformance/tests/request-block.yaml
|
||||
```
|
||||
|
||||
``` yaml
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
...
|
||||
@@ -126,16 +128,18 @@ spec:
|
||||
- "swagger.html"
|
||||
url: file:///opt/plugins/wasm-go/extensions/request-block/plugin.wasm
|
||||
```
|
||||
|
||||
`Above of the url, the name of after extensions indicates the name of the folder where the plug-in resides.`
|
||||
|
||||
./test/e2e/conformance/tests/request-block.go
|
||||
|
||||
### step2. add test cases
|
||||
|
||||
Add the test cases written above to the e2e test list,
|
||||
|
||||
./test/e2e/e2e_test.go
|
||||
|
||||
```
|
||||
```go
|
||||
...
|
||||
cSuite.Setup(t)
|
||||
var higressTests []suite.ConformanceTest
|
||||
@@ -160,8 +164,9 @@ cSuite.Setup(t)
|
||||
```
|
||||
|
||||
### step3. compile and run test cases
|
||||
|
||||
Considering that building wasm locally is time-consuming, we support building only the plug-ins that need to be tested (at the same time, you can also temporarily modify the list of test cases in the second small step above, and only execute your newly written cases).
|
||||
|
||||
```bash
|
||||
PLUGIN_TYPE=CPP PLUGIN_NAME=request_block make higress-wasmplugin-test
|
||||
```
|
||||
```
|
||||
|
||||
114
plugins/wasm-go/extensions/basic-auth/README.md
Normal file
114
plugins/wasm-go/extensions/basic-auth/README.md
Normal file
@@ -0,0 +1,114 @@
|
||||
---
|
||||
title: Basic 认证
|
||||
keywords: [higress,basic auth]
|
||||
description: Basic 认证插件配置参考
|
||||
---
|
||||
|
||||
## 功能说明
|
||||
`basic-auth`插件实现了基于 HTTP Basic Auth 标准进行认证鉴权的功能
|
||||
|
||||
## 运行属性
|
||||
|
||||
插件执行阶段:`认证阶段`
|
||||
插件执行优先级:`320`
|
||||
|
||||
## 配置字段
|
||||
|
||||
### 全局配置
|
||||
|
||||
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
|
||||
| ----------- | --------------- | -------- | ------ | ---------------------------------------------------- |
|
||||
| `consumers` | array of object | 必填 | - | 配置服务的调用者,用于对请求进行认证 |
|
||||
| `global_auth` | bool | 选填 | - | 若配置为true,则全局生效认证机制; 若配置为false,则只对做了配置的域名和路由生效认证机制; 若不配置则仅当没有域名和路由配置时全局生效(兼容机制) |
|
||||
|
||||
`consumers`中每一项的配置字段说明如下:
|
||||
|
||||
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
|
||||
| ------------ | -------- | -------- | ------ | ------------------------ |
|
||||
| `credential` | string | 必填 | - | 配置该consumer的访问凭证 |
|
||||
| `name` | string | 必填 | - | 配置该consumer的名称 |
|
||||
|
||||
### 域名和路由级配置
|
||||
|
||||
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
|
||||
| ---------------- | --------------- | ------------------------------------------------- | ------ | -------------------------------------------------- |
|
||||
| `allow` | array of string | 必填 | - | 对于符合匹配条件的请求,配置允许访问的consumer名称 |
|
||||
|
||||
**注意:**
|
||||
- 对于通过认证鉴权的请求,请求的header会被添加一个`X-Mse-Consumer`字段,用以标识调用者的名称。
|
||||
|
||||
## 配置示例
|
||||
|
||||
### 对特定路由或域名开启认证和鉴权
|
||||
|
||||
以下配置将对网关特定路由或域名开启 Basic Auth 认证和鉴权,注意凭证信息中的用户名和密码之间使用":"分隔,`credential`字段不能重复
|
||||
|
||||
**全局配置**
|
||||
|
||||
```yaml
|
||||
consumers:
|
||||
- credential: 'admin:123456'
|
||||
name: consumer1
|
||||
- credential: 'guest:abc'
|
||||
name: consumer2
|
||||
global_auth: false
|
||||
```
|
||||
|
||||
|
||||
**路由级配置**
|
||||
|
||||
对 route-a 和 route-b 这两个路由做如下配置:
|
||||
|
||||
```yaml
|
||||
allow:
|
||||
- consumer1
|
||||
```
|
||||
|
||||
对 *.example.com 和 test.com 在这两个域名做如下配置:
|
||||
|
||||
```yaml
|
||||
allow:
|
||||
- consumer2
|
||||
```
|
||||
|
||||
若是在控制台进行配置,此例指定的 `route-a` 和 `route-b` 即在控制台创建路由时填写的路由名称,当匹配到这两个路由时,将允许`name`为`consumer1`的调用者访问,其他调用者不允许访问;
|
||||
|
||||
此例指定的 `*.example.com` 和 `test.com` 用于匹配请求的域名,当发现域名匹配时,将允许`name`为`consumer2`的调用者访问,其他调用者不允许访问。
|
||||
|
||||
#### 根据该配置,下列请求可以允许访问:
|
||||
|
||||
**请求指定用户名密码**
|
||||
|
||||
```bash
|
||||
# 假设以下请求将会匹配到route-a路由
|
||||
# 使用 curl 的 -u 参数指定
|
||||
curl -u admin:123456 http://xxx.hello.com/test
|
||||
# 或者直接指定 Authorization 请求头,用户名密码使用 base64 编码
|
||||
curl -H 'Authorization: Basic YWRtaW46MTIzNDU2' http://xxx.hello.com/test
|
||||
```
|
||||
|
||||
认证鉴权通过后,请求的header中会被添加一个`X-Mse-Consumer`字段,在此例中其值为`consumer1`,用以标识调用方的名称
|
||||
|
||||
#### 下列请求将拒绝访问:
|
||||
|
||||
**请求未提供用户名密码,返回401**
|
||||
```bash
|
||||
curl http://xxx.hello.com/test
|
||||
```
|
||||
**请求提供的用户名密码错误,返回401**
|
||||
```bash
|
||||
curl -u admin:abc http://xxx.hello.com/test
|
||||
```
|
||||
**根据请求的用户名和密码匹配到的调用者无访问权限,返回403**
|
||||
```bash
|
||||
# consumer2不在route-a的allow列表里
|
||||
curl -u guest:abc http://xxx.hello.com/test
|
||||
```
|
||||
|
||||
## 相关错误码
|
||||
|
||||
| HTTP 状态码 | 出错信息 | 原因说明 |
|
||||
| ----------- |--------------------------------------------------------------------------------| ---------------------- |
|
||||
| 401 | Request denied by Basic Auth check. No Basic Authentication information found. | 请求未提供凭证 |
|
||||
| 401 | Request denied by Basic Auth check. Invalid username and/or password. | 请求凭证无效 |
|
||||
| 403 | Request denied by Basic Auth check. Unauthorized consumer. | 请求的调用方无访问权限 |
|
||||
119
plugins/wasm-go/extensions/basic-auth/README_EN.md
Normal file
119
plugins/wasm-go/extensions/basic-auth/README_EN.md
Normal file
@@ -0,0 +1,119 @@
|
||||
---
|
||||
title: Basic Auth
|
||||
keywords: [higress, basic auth]
|
||||
description: Basic authentication plug-in configuration reference
|
||||
---
|
||||
|
||||
## Description
|
||||
`basic-auth` plugin implements the function of authentication based on the HTTP Basic Auth standard.
|
||||
|
||||
## Configuration Fields
|
||||
|
||||
| Name | Type | Requirement | Default Value | Description |
|
||||
| ----------- | --------------- | -------- | ------ | ---------------------------------------------------- |
|
||||
| `consumers` | array of object | Required | - | Caller of the service for authentication of requests |
|
||||
| `_rules_` | array of object | Optional | - | Configure access permission list for specific routes or domains to authenticate requests |
|
||||
|
||||
Filed descriptions of `consumers` items:
|
||||
|
||||
| Name | Type | Requirement | Default Value | Description |
|
||||
| ------------ | ------ | ----------- | ------------- | ------------------------------------- |
|
||||
| `credential` | string | Required | - | Credential for this consumer's access |
|
||||
| `name` | string | Required | - | Name of this consumer |
|
||||
|
||||
Configuration field descriptions for each item in `_rules_` are as follows:
|
||||
|
||||
| Field Name | Data Type | Requirement | Default | Description |
|
||||
| ---------------- | --------------- | ------------------------------------------------- | ------ | -------------------------------------------------- |
|
||||
| `_match_route_` | array of string | One of `_match_route_` or `_match_domain_` | - | Configure the routes to match for request authorization |
|
||||
| `_match_domain_` | array of string | One of `_match_route_` , `_match_domain_` | - | Configure the domains to match for request authorization |
|
||||
| `allow` | array of string | Required | - | Configure the consumer names allowed to access requests that match the match condition |
|
||||
|
||||
**Note:**
|
||||
|
||||
- If the `_rules_` field is not configured, authentication is enabled for all routes of the current gateway instance by default;
|
||||
- For authenticated requests, `X-Mse-Consumer` field will be added to the request header to identify the name of the caller.
|
||||
|
||||
## Configuration Samples
|
||||
|
||||
### Enable Authentication and Authorization for specific routes or domains
|
||||
|
||||
The following configuration will enable Basic Auth authentication and authorization for specific routes or domains of the gateway. Note that the username and password in the credential information are separated by a ":", and the `credential` field cannot be repeated.
|
||||
|
||||
|
||||
|
||||
```yaml
|
||||
# use the _rules_ field for fine-grained rule configuration.
|
||||
consumers:
|
||||
- credential: 'admin:123456'
|
||||
name: consumer1
|
||||
- credential: 'guest:abc'
|
||||
name: consumer2
|
||||
_rules_:
|
||||
# rule 1: match by the route name.
|
||||
- _match_route_:
|
||||
- route-a
|
||||
- route-b
|
||||
allow:
|
||||
- consumer1
|
||||
# rule 2: match by the domain.
|
||||
- _match_domain_:
|
||||
- "*.example.com"
|
||||
- test.com
|
||||
allow:
|
||||
- consumer2
|
||||
```
|
||||
In this sample, `route-a` and `route-b` specified in `_match_route_` are the route names filled in when creating gateway routes. When these two routes are matched, the caller with `name` as `consumer1` is allowed to access, and other callers are not allowed to access.
|
||||
|
||||
The `*.example.com` and `test.com` specified in `_match_domain_` are used to match the domain name of the request. When the domain name is matched, the caller with `name` as `consumer2` is allowed to access, and other callers are not allowed to access.
|
||||
|
||||
|
||||
#### According to this configuration, the following requests are allowed:
|
||||
|
||||
**Requests with specified username and password**
|
||||
|
||||
```bash
|
||||
# Assuming the following request will match with route-a
|
||||
# Use -u option of curl to specify the credentials
|
||||
curl -u admin:123456 http://xxx.hello.com/test
|
||||
# Or specify the Authorization request header directly with the credentials in base64 encoding
|
||||
curl -H 'Authorization: Basic YWRtaW46MTIzNDU2' http://xxx.hello.com/test
|
||||
```
|
||||
|
||||
A `X-Mse-Consumer` field will be added to the headers of the request, and its value in this example is `consumer1`, used to identify the name of the caller when passed authentication and authorization.
|
||||
|
||||
#### The following requests will be denied:
|
||||
|
||||
**Requests without providing username and password, returning 401**
|
||||
```bash
|
||||
curl http://xxx.hello.com/test
|
||||
```
|
||||
**Requests with incorrect username or password, returning 401**
|
||||
```bash
|
||||
curl -u admin:abc http://xxx.hello.com/test
|
||||
```
|
||||
**Requests matched with a caller who has no access permission, returning 403**
|
||||
```bash
|
||||
# consumer2 is not in the allow list of route-a
|
||||
curl -u guest:abc http://xxx.hello.com/test
|
||||
```
|
||||
|
||||
### Enable basic auth for gateway instance
|
||||
|
||||
The following configuration does not specify the `_rules_` field, so Basic Auth authentication will be effective for the whole gateway instance.
|
||||
|
||||
```yaml
|
||||
consumers:
|
||||
- credential: 'admin:123456'
|
||||
name: consumer1
|
||||
- credential: 'guest:abc'
|
||||
name: consumer2
|
||||
```
|
||||
|
||||
## Error Codes
|
||||
|
||||
| HTTP Status Code | Error Info | Reason |
|
||||
| ----------- |--------------------------------------------------------------------------------| ---------------------- |
|
||||
| 401 | Request denied by Basic Auth check. No Basic Authentication information found. | Credentials not provided in the request |
|
||||
| 401 | Request denied by Basic Auth check. Invalid username and/or password. | Invalid username and/or password |
|
||||
| 403 | Request denied by Basic Auth check. Unauthorized consumer. | Unauthorized consumer |
|
||||
1
plugins/wasm-go/extensions/basic-auth/VERSION
Normal file
1
plugins/wasm-go/extensions/basic-auth/VERSION
Normal file
@@ -0,0 +1 @@
|
||||
1.0.0
|
||||
22
plugins/wasm-go/extensions/basic-auth/go.mod
Normal file
22
plugins/wasm-go/extensions/basic-auth/go.mod
Normal file
@@ -0,0 +1,22 @@
|
||||
module github.com/alibaba/higress/plugins/wasm-go/extensions/basic-auth
|
||||
|
||||
go 1.19
|
||||
|
||||
|
||||
replace github.com/alibaba/higress/plugins/wasm-go => ../..
|
||||
|
||||
|
||||
require (
|
||||
github.com/alibaba/higress/plugins/wasm-go v0.0.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/tetratelabs/proxy-wasm-go-sdk v0.22.0
|
||||
github.com/tidwall/gjson v1.14.3
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/magefile/mage v1.14.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/wasilibs/nottinygc v0.3.0 // indirect
|
||||
)
|
||||
9
plugins/wasm-go/extensions/basic-auth/go.sum
Normal file
9
plugins/wasm-go/extensions/basic-auth/go.sum
Normal file
@@ -0,0 +1,9 @@
|
||||
github.com/WeixinX/higress/plugins/wasm-go v0.0.0-20230911073755-f281286d0cdb/go.mod h1:shD9qvrDS6xklAVjKYho8kHIVdW4A1vhNEOAL2miEEE=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/tetratelabs/proxy-wasm-go-sdk v0.22.0/go.mod h1:qkW5MBz2jch2u8bS59wws65WC+Gtx3x0aPUX5JL7CXI=
|
||||
github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/wasilibs/nottinygc v0.3.0/go.mod h1:oDcIotskuYNMpqMF23l7Z8uzD4TC0WXHK8jetlB3HIo=
|
||||
330
plugins/wasm-go/extensions/basic-auth/main.go
Normal file
330
plugins/wasm-go/extensions/basic-auth/main.go
Normal file
@@ -0,0 +1,330 @@
|
||||
// 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.
|
||||
|
||||
// The 'Basic' HTTP Authentication Scheme: https://datatracker.ietf.org/doc/html/rfc7617
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/alibaba/higress/plugins/wasm-go/pkg/wrapper"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm"
|
||||
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types"
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
func main() {
|
||||
wrapper.SetCtx(
|
||||
"basic-auth",
|
||||
wrapper.ParseOverrideConfigBy(parseGlobalConfig, parseOverrideRuleConfig),
|
||||
wrapper.ProcessRequestHeadersBy(onHttpRequestHeaders),
|
||||
)
|
||||
}
|
||||
|
||||
// @Name basic-auth
|
||||
// @Category auth
|
||||
// @Phase AUTHN
|
||||
// @Priority 320
|
||||
// @Title zh-CN Basic Auth
|
||||
// @Description zh-CN 本插件实现了基于 HTTP Basic Auth 标准进行认证鉴权的功能。
|
||||
// @Description en-US This plugin implements an authentication function based on HTTP Basic Auth standard.
|
||||
// @IconUrl https://img.alicdn.com/imgextra/i4/O1CN01BPFGlT1pGZ2VDLgaH_!!6000000005333-2-tps-42-42.png
|
||||
// @Version 1.0.0
|
||||
//
|
||||
// @Contact.name Higress Team
|
||||
// @Contact.url http://higress.io/
|
||||
// @Contact.email admin@higress.io
|
||||
//
|
||||
// @Example
|
||||
// global_auth: false
|
||||
// consumers:
|
||||
// - name: consumer1
|
||||
// credential: admin:123456
|
||||
// - name: consumer2
|
||||
// credential: guest:abc
|
||||
//
|
||||
// @End
|
||||
type BasicAuthConfig struct {
|
||||
// @Title 是否开启全局认证
|
||||
// @Title en-US Enable Global Auth
|
||||
// @Description 若不开启全局认证,则全局配置只提供凭证信息。只有在域名或路由上进行了配置才会启用认证。
|
||||
// @Description en-US If set to false, only consumer info will be accepted from the global config. Auth feature shall only be enabled if the corresponding domain or route is configured.
|
||||
// @Scope GLOBAL
|
||||
globalAuth *bool `yaml:"global_auth"`
|
||||
|
||||
// @Title 调用方列表
|
||||
// @Title en-US Consumer List
|
||||
// @Description 服务调用方列表,用于对请求进行认证。
|
||||
// @Description en-US List of service consumers which will be used in request authentication.
|
||||
// @Scope GLOBAL
|
||||
consumers []Consumer `yaml:"consumers"`
|
||||
|
||||
// @Title 授权访问的调用方列表
|
||||
// @Title en-US Allowed Consumers
|
||||
// @Description 对于匹配上述条件的请求,允许访问的调用方列表。
|
||||
// @Description en-US Consumers to be allowed for matched requests.
|
||||
allow []string `yaml:"allow"`
|
||||
|
||||
credential2Name map[string]string `yaml:"-"`
|
||||
username2Passwd map[string]string `yaml:"-"`
|
||||
}
|
||||
|
||||
type Consumer struct {
|
||||
// @Title 名称
|
||||
// @Title en-US Name
|
||||
// @Description 该调用方的名称。
|
||||
// @Description en-US The name of the consumer.
|
||||
name string `yaml:"name"`
|
||||
|
||||
// @Title 访问凭证
|
||||
// @Title en-US Credential
|
||||
// @Description 该调用方的访问凭证。
|
||||
// @Description en-US The credential of the consumer.
|
||||
// @Scope GLOBAL
|
||||
credential string `yaml:"credential"`
|
||||
}
|
||||
|
||||
var (
|
||||
ruleSet bool // 插件是否至少在一个 domain 或 route 上生效
|
||||
protectionSpace = "MSE Gateway" // 认证失败时,返回响应头 WWW-Authenticate: Basic realm=MSE Gateway
|
||||
)
|
||||
|
||||
func parseGlobalConfig(json gjson.Result, global *BasicAuthConfig, log wrapper.Log) error {
|
||||
// log.Debug("global config")
|
||||
ruleSet = false
|
||||
global.credential2Name = make(map[string]string)
|
||||
global.username2Passwd = make(map[string]string)
|
||||
|
||||
consumers := json.Get("consumers")
|
||||
if !consumers.Exists() {
|
||||
return errors.New("consumers is required")
|
||||
}
|
||||
if len(consumers.Array()) == 0 {
|
||||
return errors.New("consumers cannot be empty")
|
||||
}
|
||||
|
||||
for _, item := range consumers.Array() {
|
||||
name := item.Get("name")
|
||||
if !name.Exists() || name.String() == "" {
|
||||
return errors.New("consumer name is required")
|
||||
}
|
||||
credential := item.Get("credential")
|
||||
if !credential.Exists() || credential.String() == "" {
|
||||
return errors.New("consumer credential is required")
|
||||
}
|
||||
if _, ok := global.credential2Name[credential.String()]; ok {
|
||||
return errors.Errorf("duplicate consumer credential: %s", credential.String())
|
||||
}
|
||||
userAndPasswd := strings.Split(credential.String(), ":")
|
||||
if len(userAndPasswd) != 2 {
|
||||
return errors.Errorf("invalid credential format: %s", credential.String())
|
||||
}
|
||||
|
||||
consumer := Consumer{
|
||||
name: name.String(),
|
||||
credential: credential.String(),
|
||||
}
|
||||
global.consumers = append(global.consumers, consumer)
|
||||
global.credential2Name[consumer.credential] = consumer.name
|
||||
global.username2Passwd[userAndPasswd[0]] = userAndPasswd[1]
|
||||
}
|
||||
|
||||
globalAuth := json.Get("global_auth")
|
||||
if globalAuth.Exists() {
|
||||
ga := globalAuth.Bool()
|
||||
global.globalAuth = &ga
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseOverrideRuleConfig(json gjson.Result, global BasicAuthConfig, config *BasicAuthConfig, log wrapper.Log) error {
|
||||
log.Debug("domain/route config")
|
||||
// override config via global
|
||||
*config = global
|
||||
|
||||
allow := json.Get("allow")
|
||||
if !allow.Exists() {
|
||||
return errors.New("allow is required")
|
||||
}
|
||||
if len(allow.Array()) == 0 {
|
||||
return errors.New("allow cannot be empty")
|
||||
}
|
||||
|
||||
for _, item := range allow.Array() {
|
||||
config.allow = append(config.allow, item.String())
|
||||
}
|
||||
ruleSet = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// basic-auth 插件认证逻辑:
|
||||
// - global_auth == true 开启全局生效:
|
||||
// - 若当前 domain/route 未配置 allow 列表,即未配置该插件:则在所有 consumers 中查找,如果找到则认证通过,否则认证失败 (1*)
|
||||
// - 若当前 domain/route 配置了该插件:则在 allow 列表中查找,如果找到则认证通过,否则认证失败
|
||||
//
|
||||
// - global_auth == false 非全局生效:(2*)
|
||||
// - 若当前 domain/route 未配置该插件:则直接放行
|
||||
// - 若当前 domain/route 配置了该插件:则在 allow 列表中查找,如果找到则认证通过,否则认证失败
|
||||
//
|
||||
// - global_auth 未设置:
|
||||
// - 若没有一个 domain/route 配置该插件:则遵循 (1*)
|
||||
// - 若有至少一个 domain/route 配置该插件:则遵循 (2*)
|
||||
func onHttpRequestHeaders(ctx wrapper.HttpContext, config BasicAuthConfig, log wrapper.Log) types.Action {
|
||||
var (
|
||||
noAllow = len(config.allow) == 0 // 未配置 allow 列表,表示插件在该 domain/route 未生效
|
||||
globalAuthNoSet = config.globalAuth == nil
|
||||
globalAuthSetTrue = !globalAuthNoSet && *config.globalAuth
|
||||
globalAuthSetFalse = !globalAuthNoSet && !*config.globalAuth
|
||||
)
|
||||
// log.Debugf("global auth set: %t", !globalAuthNoSet)
|
||||
// log.Debugf("rule set: %t", ruleSet)
|
||||
// log.Debugf("config: %+v", config)
|
||||
|
||||
// 不需要认证而直接放行的情况:
|
||||
// - global_auth == false 且 当前 domain/route 未配置该插件
|
||||
// - global_auth 未设置 且 有至少一个 domain/route 配置该插件 且 当前 domain/route 未配置该插件
|
||||
if globalAuthSetFalse || (globalAuthNoSet && ruleSet) {
|
||||
if noAllow {
|
||||
log.Info("authorization is not required")
|
||||
return types.ActionContinue
|
||||
}
|
||||
}
|
||||
|
||||
// 以下为需要认证的情况:
|
||||
auth, err := proxywasm.GetHttpRequestHeader("Authorization")
|
||||
if err != nil {
|
||||
log.Warnf("failed to get authorization: %v", err)
|
||||
return deniedNoBasicAuthData()
|
||||
}
|
||||
if auth == "" {
|
||||
log.Warnf("authorization is empty")
|
||||
return deniedNoBasicAuthData()
|
||||
}
|
||||
if !strings.HasPrefix(auth, "Basic ") {
|
||||
log.Warnf("authorization has no prefix 'Basic '")
|
||||
return deniedNoBasicAuthData()
|
||||
}
|
||||
|
||||
encodedCredential := strings.TrimPrefix(auth, "Basic ")
|
||||
credentialByte, err := base64.StdEncoding.DecodeString(encodedCredential)
|
||||
if err != nil {
|
||||
log.Warnf("failed to decode authorization %q: %v", string(credentialByte), err)
|
||||
return deniedInvalidCredentials()
|
||||
}
|
||||
|
||||
credential := string(credentialByte)
|
||||
userAndPasswd := strings.Split(credential, ":")
|
||||
if len(userAndPasswd) != 2 {
|
||||
log.Warnf("invalid credential format: %s", credential)
|
||||
return deniedInvalidCredentials()
|
||||
}
|
||||
|
||||
user, passwd := userAndPasswd[0], userAndPasswd[1]
|
||||
if correctPasswd, ok := config.username2Passwd[user]; !ok {
|
||||
log.Warnf("credential username %q is not configured", user)
|
||||
return deniedInvalidCredentials()
|
||||
} else {
|
||||
if passwd != correctPasswd {
|
||||
log.Warnf("credential password is not correct for username %q", user)
|
||||
return deniedInvalidCredentials()
|
||||
}
|
||||
}
|
||||
|
||||
// 以下为 username 和 password 正确的情况:
|
||||
name, ok := config.credential2Name[credential]
|
||||
if !ok { // 理论上该分支永远不可达,因为 username 和 password 都是从 credential 中获取的
|
||||
log.Warnf("credential %q is not configured", credential)
|
||||
return deniedUnauthorizedConsumer()
|
||||
}
|
||||
|
||||
// 全局生效:
|
||||
// - global_auth == true 且 当前 domain/route 未配置该插件
|
||||
// - global_auth 未设置 且 没有任何一个 domain/route 配置该插件
|
||||
if (globalAuthSetTrue && noAllow) || (globalAuthNoSet && !ruleSet) {
|
||||
// log.Debug("authenticated case 1")
|
||||
log.Infof("consumer %q authenticated", name)
|
||||
return authenticated(name)
|
||||
}
|
||||
|
||||
// 全局生效,但当前 domain/route 配置了 allow 列表
|
||||
if globalAuthSetTrue && !noAllow {
|
||||
if !contains(config.allow, name) {
|
||||
log.Warnf("consumer %q is not allowed", name)
|
||||
return deniedUnauthorizedConsumer()
|
||||
}
|
||||
// log.Debug("authenticated case 2")
|
||||
log.Infof("consumer %q authenticated", name)
|
||||
return authenticated(name)
|
||||
}
|
||||
|
||||
// 非全局生效
|
||||
if globalAuthSetFalse || (globalAuthNoSet && ruleSet) {
|
||||
if !noAllow { // 配置了 allow 列表
|
||||
if !contains(config.allow, name) {
|
||||
log.Warnf("consumer %q is not allowed", name)
|
||||
return deniedUnauthorizedConsumer()
|
||||
}
|
||||
// log.Debug("authenticated case 3")
|
||||
log.Infof("consumer %q authenticated", name)
|
||||
return authenticated(name)
|
||||
}
|
||||
}
|
||||
|
||||
return types.ActionContinue
|
||||
}
|
||||
|
||||
func deniedNoBasicAuthData() types.Action {
|
||||
_ = proxywasm.SendHttpResponse(401, WWWAuthenticateHeader(protectionSpace),
|
||||
[]byte("Request denied by Basic Auth check. No Basic Authentication information found."), -1)
|
||||
return types.ActionContinue
|
||||
}
|
||||
|
||||
func deniedInvalidCredentials() types.Action {
|
||||
_ = proxywasm.SendHttpResponse(401, WWWAuthenticateHeader(protectionSpace),
|
||||
[]byte("Request denied by Basic Auth check. Invalid username and/or password."), -1)
|
||||
return types.ActionContinue
|
||||
}
|
||||
|
||||
func deniedUnauthorizedConsumer() types.Action {
|
||||
_ = proxywasm.SendHttpResponse(403, WWWAuthenticateHeader(protectionSpace),
|
||||
[]byte("Request denied by Basic Auth check. Unauthorized consumer."), -1)
|
||||
return types.ActionContinue
|
||||
}
|
||||
|
||||
func authenticated(name string) types.Action {
|
||||
_ = proxywasm.AddHttpRequestHeader("X-Mse-Consumer", name)
|
||||
return types.ActionContinue
|
||||
}
|
||||
|
||||
func WWWAuthenticateHeader(realm string) [][2]string {
|
||||
return [][2]string{
|
||||
{"WWW-Authenticate", fmt.Sprintf("Basic realm=%s", realm)},
|
||||
}
|
||||
}
|
||||
|
||||
func contains(arr []string, item string) bool {
|
||||
for _, i := range arr {
|
||||
if i == item {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
15
plugins/wasm-go/extensions/jwt-auth/README.md
Normal file
15
plugins/wasm-go/extensions/jwt-auth/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# 功能说明
|
||||
`jwt-auth`插件基于wasm-go实现了Token解析认证功能,可以判断Token是否有效,如果Token有效则继续访问后端微服务,Token无效或不存在直接拒绝并返回401
|
||||
|
||||
# 配置字段
|
||||
| 名称 | 数据类型 | 填写要求 | 描述 |
|
||||
| ------------ | ------------ | ------------ | ------------ |
|
||||
| token_secret_key | string | 必填 | 配置Token解析使用的SecretKey|
|
||||
| token_headers | string | 必填 | 配置获取Token请求头名称|
|
||||
|
||||
# 配置示例
|
||||
```yaml
|
||||
token_secret_key: Dav7kfq3iA8S!JUj8&CUkdnQe72E@Cw6
|
||||
token_headers: token
|
||||
```
|
||||
此例`token_secret_key`中指定的是认证服务生成Token的SecretKey;`token_headers`是携带Token访问的请求头名称;
|
||||
1
plugins/wasm-go/extensions/jwt-auth/VERSION
Normal file
1
plugins/wasm-go/extensions/jwt-auth/VERSION
Normal file
@@ -0,0 +1 @@
|
||||
1.0.0
|
||||
18
plugins/wasm-go/extensions/jwt-auth/go.mod
Normal file
18
plugins/wasm-go/extensions/jwt-auth/go.mod
Normal file
@@ -0,0 +1,18 @@
|
||||
module jwt-auth
|
||||
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/alibaba/higress/plugins/wasm-go v0.0.0-20230811015533-49269b43032f
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/tetratelabs/proxy-wasm-go-sdk v0.22.0
|
||||
github.com/tidwall/gjson v1.16.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/magefile/mage v1.14.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/wasilibs/nottinygc v0.3.0 // indirect
|
||||
)
|
||||
22
plugins/wasm-go/extensions/jwt-auth/go.sum
Normal file
22
plugins/wasm-go/extensions/jwt-auth/go.sum
Normal file
@@ -0,0 +1,22 @@
|
||||
github.com/alibaba/higress/plugins/wasm-go v0.0.0-20230811015533-49269b43032f h1:H+2fEuroddobcGs2Vom+osc8CE3SBHLz+JbM036Lo9w=
|
||||
github.com/alibaba/higress/plugins/wasm-go v0.0.0-20230811015533-49269b43032f/go.mod h1:shD9qvrDS6xklAVjKYho8kHIVdW4A1vhNEOAL2miEEE=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo=
|
||||
github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/tetratelabs/proxy-wasm-go-sdk v0.22.0 h1:kS7BvMKN+FiptV4pfwiNX8e3q14evxAWkhYbxt8EI1M=
|
||||
github.com/tetratelabs/proxy-wasm-go-sdk v0.22.0/go.mod h1:qkW5MBz2jch2u8bS59wws65WC+Gtx3x0aPUX5JL7CXI=
|
||||
github.com/tidwall/gjson v1.16.0 h1:SyXa+dsSPpUlcwEDuKuEBJEz5vzTvOea+9rjyYodQFg=
|
||||
github.com/tidwall/gjson v1.16.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/wasilibs/nottinygc v0.3.0 h1:0L1jsJ1MsyN5tdinmFbLfuEA0TnHRcqaBM9pDTJVJmU=
|
||||
github.com/wasilibs/nottinygc v0.3.0/go.mod h1:oDcIotskuYNMpqMF23l7Z8uzD4TC0WXHK8jetlB3HIo=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
76
plugins/wasm-go/extensions/jwt-auth/main.go
Normal file
76
plugins/wasm-go/extensions/jwt-auth/main.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/alibaba/higress/plugins/wasm-go/pkg/wrapper"
|
||||
jwt "github.com/dgrijalva/jwt-go"
|
||||
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm"
|
||||
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types"
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
// 自定义插件配置
|
||||
|
||||
func main() {
|
||||
wrapper.SetCtx(
|
||||
"jwt-auth", // 配置插件名称
|
||||
wrapper.ParseConfigBy(parseConfig),
|
||||
wrapper.ProcessRequestHeadersBy(onHttpRequestHeaders),
|
||||
)
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
TokenSecretKey string // 解析Token SecretKey
|
||||
TokenHeaders string // 定义获取Token请求头名称
|
||||
}
|
||||
|
||||
type Res struct {
|
||||
Code int `json:"code"` // 返回状态码
|
||||
Msg string `json:"msg"` // 返回信息
|
||||
}
|
||||
|
||||
func parseConfig(json gjson.Result, config *Config, log wrapper.Log) error {
|
||||
// 解析出配置,更新到config中
|
||||
config.TokenSecretKey = json.Get("token_secret_key").String()
|
||||
config.TokenHeaders = json.Get("token_headers").String()
|
||||
return nil
|
||||
}
|
||||
|
||||
func onHttpRequestHeaders(ctx wrapper.HttpContext, config Config, log wrapper.Log) types.Action {
|
||||
var res Res
|
||||
if config.TokenHeaders == "" || config.TokenSecretKey == "" {
|
||||
res.Code = 401
|
||||
res.Msg = "参数不足"
|
||||
data, _ := json.Marshal(res)
|
||||
_ = proxywasm.SendHttpResponse(401, nil, data, -1)
|
||||
return types.ActionContinue
|
||||
}
|
||||
|
||||
token, err := proxywasm.GetHttpRequestHeader(config.TokenHeaders)
|
||||
if err != nil {
|
||||
res.Code = 401
|
||||
res.Msg = "认证失败"
|
||||
data, _ := json.Marshal(res)
|
||||
_ = proxywasm.SendHttpResponse(401, nil, data, -1)
|
||||
return types.ActionContinue
|
||||
}
|
||||
valid := ParseTokenValid(token, config.TokenSecretKey)
|
||||
if valid {
|
||||
_ = proxywasm.ResumeHttpRequest()
|
||||
return types.ActionPause
|
||||
} else {
|
||||
res.Code = 401
|
||||
res.Msg = "认证失败"
|
||||
data, _ := json.Marshal(res)
|
||||
_ = proxywasm.SendHttpResponse(401, nil, data, -1)
|
||||
return types.ActionContinue
|
||||
}
|
||||
}
|
||||
|
||||
func ParseTokenValid(tokenString, TokenSecretKey string) bool {
|
||||
token, _ := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||
// 在这里提供用于验证签名的密钥
|
||||
return []byte(TokenSecretKey), nil
|
||||
})
|
||||
return token.Valid
|
||||
}
|
||||
@@ -90,7 +90,8 @@ func (m RuleMatcher[PluginConfig]) GetMatchConfig() (*PluginConfig, error) {
|
||||
}
|
||||
|
||||
func (m *RuleMatcher[PluginConfig]) ParseRuleConfig(config gjson.Result,
|
||||
parsePluginConfig func(gjson.Result, *PluginConfig) error) error {
|
||||
parsePluginConfig func(gjson.Result, *PluginConfig) error,
|
||||
parseOverrideConfig func(gjson.Result, PluginConfig, *PluginConfig) error) error {
|
||||
var rules []gjson.Result
|
||||
obj := config.Map()
|
||||
keyCount := len(obj)
|
||||
@@ -122,8 +123,15 @@ func (m *RuleMatcher[PluginConfig]) ParseRuleConfig(config gjson.Result,
|
||||
return fmt.Errorf("parse config failed, no valid rules; global config parse error:%v", globalConfigError)
|
||||
}
|
||||
for _, ruleJson := range rules {
|
||||
var rule RuleConfig[PluginConfig]
|
||||
err := parsePluginConfig(ruleJson, &rule.config)
|
||||
var (
|
||||
rule RuleConfig[PluginConfig]
|
||||
err error
|
||||
)
|
||||
if parseOverrideConfig != nil {
|
||||
err = parseOverrideConfig(ruleJson, m.globalConfig, &rule.config)
|
||||
} else {
|
||||
err = parsePluginConfig(ruleJson, &rule.config)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package matcher
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -221,7 +222,7 @@ func TestParseRuleConfig(t *testing.T) {
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual RuleMatcher[customConfig]
|
||||
err := actual.ParseRuleConfig(gjson.Parse(c.config), parseConfig)
|
||||
err := actual.ParseRuleConfig(gjson.Parse(c.config), parseConfig, nil)
|
||||
if err != nil {
|
||||
if c.errMsg == "" {
|
||||
t.Errorf("parse failed: %v", err)
|
||||
@@ -236,3 +237,96 @@ func TestParseRuleConfig(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type completeConfig struct {
|
||||
// global config
|
||||
consumers []string
|
||||
// rule config
|
||||
allow []string
|
||||
}
|
||||
|
||||
func parseGlobalConfig(json gjson.Result, global *completeConfig) error {
|
||||
if json.Get("consumers").Exists() && json.Get("allow").Exists() {
|
||||
return errors.New("consumers and allow should not be configured at the same level")
|
||||
}
|
||||
|
||||
for _, item := range json.Get("consumers").Array() {
|
||||
global.consumers = append(global.consumers, item.String())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseOverrideRuleConfig(json gjson.Result, global completeConfig, config *completeConfig) error {
|
||||
if json.Get("consumers").Exists() && json.Get("allow").Exists() {
|
||||
return errors.New("consumers and allow should not be configured at the same level")
|
||||
}
|
||||
|
||||
// override config via global
|
||||
*config = global
|
||||
|
||||
for _, item := range json.Get("allow").Array() {
|
||||
config.allow = append(config.allow, item.String())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestParseOverrideConfig(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
config string
|
||||
errMsg string
|
||||
expected RuleMatcher[completeConfig]
|
||||
}{
|
||||
{
|
||||
name: "override rule config",
|
||||
config: `{"consumers":["c1","c2","c3"],"_rules_":[{"_match_route_":["r1","r2"],"allow":["c1","c3"]}]}`,
|
||||
expected: RuleMatcher[completeConfig]{
|
||||
ruleConfig: []RuleConfig[completeConfig]{
|
||||
{
|
||||
category: Route,
|
||||
routes: map[string]struct{}{
|
||||
"r1": {},
|
||||
"r2": {},
|
||||
},
|
||||
config: completeConfig{
|
||||
consumers: []string{"c1", "c2", "c3"},
|
||||
allow: []string{"c1", "c3"},
|
||||
},
|
||||
},
|
||||
},
|
||||
globalConfig: completeConfig{
|
||||
consumers: []string{"c1", "c2", "c3"},
|
||||
},
|
||||
hasGlobalConfig: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid config",
|
||||
config: `{"consumers":["c1","c2","c3"],"allow":["c1"]}`,
|
||||
errMsg: "parse config failed, no valid rules; global config parse error:consumers and allow should not be configured at the same level",
|
||||
},
|
||||
{
|
||||
name: "invalid config",
|
||||
config: `{"_rules_":[{"_match_route_":["r1","r2"],"consumers":["c1","c2"],"allow":["c1"]}]}`,
|
||||
errMsg: "consumers and allow should not be configured at the same level",
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual RuleMatcher[completeConfig]
|
||||
err := actual.ParseRuleConfig(gjson.Parse(c.config), parseGlobalConfig, parseOverrideRuleConfig)
|
||||
if err != nil {
|
||||
if c.errMsg == "" {
|
||||
t.Errorf("parse failed: %v", err)
|
||||
}
|
||||
if err.Error() != c.errMsg {
|
||||
t.Errorf("expect err: %s, actual err: %s", c.errMsg, err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
assert.Equal(t, c.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,3 +131,23 @@ func (c DnsCluster) ClusterName() string {
|
||||
func (c DnsCluster) HostName() string {
|
||||
return c.Domain
|
||||
}
|
||||
|
||||
type ConsulCluster struct {
|
||||
ServiceName string
|
||||
Datacenter string
|
||||
Port int64
|
||||
Host string
|
||||
}
|
||||
|
||||
func (c ConsulCluster) ClusterName() string {
|
||||
tail := "consul"
|
||||
return fmt.Sprintf("outbound|%d||%s.%s.%s",
|
||||
c.Port, c.ServiceName, c.Datacenter, tail)
|
||||
}
|
||||
|
||||
func (c ConsulCluster) HostName() string {
|
||||
if c.Host != "" {
|
||||
return c.Host
|
||||
}
|
||||
return c.ServiceName
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ type HttpContext interface {
|
||||
}
|
||||
|
||||
type ParseConfigFunc[PluginConfig any] func(json gjson.Result, config *PluginConfig, log Log) error
|
||||
type ParseRuleConfigFunc[PluginConfig any] func(json gjson.Result, global PluginConfig, config *PluginConfig, log Log) error
|
||||
type onHttpHeadersFunc[PluginConfig any] func(context HttpContext, config PluginConfig, log Log) types.Action
|
||||
type onHttpBodyFunc[PluginConfig any] func(context HttpContext, config PluginConfig, body []byte, log Log) types.Action
|
||||
type onHttpStreamDoneFunc[PluginConfig any] func(context HttpContext, config PluginConfig, log Log)
|
||||
@@ -54,6 +55,7 @@ type CommonVmCtx[PluginConfig any] struct {
|
||||
log Log
|
||||
hasCustomConfig bool
|
||||
parseConfig ParseConfigFunc[PluginConfig]
|
||||
parseRuleConfig ParseRuleConfigFunc[PluginConfig]
|
||||
onHttpRequestHeaders onHttpHeadersFunc[PluginConfig]
|
||||
onHttpRequestBody onHttpBodyFunc[PluginConfig]
|
||||
onHttpResponseHeaders onHttpHeadersFunc[PluginConfig]
|
||||
@@ -73,6 +75,13 @@ func ParseConfigBy[PluginConfig any](f ParseConfigFunc[PluginConfig]) SetPluginF
|
||||
}
|
||||
}
|
||||
|
||||
func ParseOverrideConfigBy[PluginConfig any](f ParseConfigFunc[PluginConfig], g ParseRuleConfigFunc[PluginConfig]) SetPluginFunc[PluginConfig] {
|
||||
return func(ctx *CommonVmCtx[PluginConfig]) {
|
||||
ctx.parseConfig = f
|
||||
ctx.parseRuleConfig = g
|
||||
}
|
||||
}
|
||||
|
||||
func ProcessRequestHeadersBy[PluginConfig any](f onHttpHeadersFunc[PluginConfig]) SetPluginFunc[PluginConfig] {
|
||||
return func(ctx *CommonVmCtx[PluginConfig]) {
|
||||
ctx.onHttpRequestHeaders = f
|
||||
@@ -161,9 +170,19 @@ func (ctx *CommonPluginCtx[PluginConfig]) OnPluginStart(int) types.OnPluginStart
|
||||
}
|
||||
jsonData = gjson.ParseBytes(data)
|
||||
}
|
||||
err = ctx.ParseRuleConfig(jsonData, func(js gjson.Result, cfg *PluginConfig) error {
|
||||
return ctx.vm.parseConfig(js, cfg, ctx.vm.log)
|
||||
})
|
||||
|
||||
var parseOverrideConfig func(gjson.Result, PluginConfig, *PluginConfig) error
|
||||
if ctx.vm.parseRuleConfig != nil {
|
||||
parseOverrideConfig = func(js gjson.Result, global PluginConfig, cfg *PluginConfig) error {
|
||||
return ctx.vm.parseRuleConfig(js, global, cfg, ctx.vm.log)
|
||||
}
|
||||
}
|
||||
err = ctx.ParseRuleConfig(jsonData,
|
||||
func(js gjson.Result, cfg *PluginConfig) error {
|
||||
return ctx.vm.parseConfig(js, cfg, ctx.vm.log)
|
||||
},
|
||||
parseOverrideConfig,
|
||||
)
|
||||
if err != nil {
|
||||
ctx.vm.log.Warnf("parse rule config failed: %v", err)
|
||||
return types.OnPluginStartStatusFailed
|
||||
|
||||
@@ -58,7 +58,7 @@ func NewReconciler(serviceUpdate func(), client kube.Client, namespace string) *
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Reconciler) Reconcile(mcpbridge *v1.McpBridge) {
|
||||
func (r *Reconciler) Reconcile(mcpbridge *v1.McpBridge) error {
|
||||
newRegistries := make(map[string]*apiv1.RegistryConfig)
|
||||
if mcpbridge != nil {
|
||||
for _, registry := range mcpbridge.Spec.Registries {
|
||||
@@ -121,12 +121,12 @@ func (r *Reconciler) Reconcile(mcpbridge *v1.McpBridge) {
|
||||
r.registries[k] = v
|
||||
}
|
||||
if errHappened {
|
||||
log.Error("ReconcileRegistries failed, Init Watchers failed")
|
||||
return
|
||||
return errors.New("ReconcileRegistries failed, Init Watchers failed")
|
||||
}
|
||||
wg.Wait()
|
||||
r.Cache.PurgeStaleService()
|
||||
log.Infof("Registries is reconciled")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Reconciler) generateWatcherFromRegistryConfig(registry *apiv1.RegistryConfig, wg *sync.WaitGroup) (Watcher, error) {
|
||||
|
||||
@@ -9,11 +9,11 @@ Higress e2e tests are mainly focusing on two parts for now:
|
||||
|
||||
### Architecture
|
||||
|
||||

|
||||

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

|
||||

|
||||
|
||||
Higress provides make target to run ingress api conformance tests and wasmplugin tests,
|
||||
|
||||
|
||||
119
test/e2e/conformance/base/consul.yaml
Normal file
119
test/e2e/conformance/base/consul.yaml
Normal file
@@ -0,0 +1,119 @@
|
||||
# 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: Pod
|
||||
metadata:
|
||||
name: consul-standlone
|
||||
namespace: higress-conformance-app-backend
|
||||
labels:
|
||||
name: consul-standlone
|
||||
spec:
|
||||
containers:
|
||||
- name: consul
|
||||
image: docker.io/hashicorp/consul:1.16.0
|
||||
resources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
ports:
|
||||
- containerPort: 8500
|
||||
name: http
|
||||
protocol: TCP
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: consul-service
|
||||
namespace: higress-conformance-app-backend
|
||||
labels:
|
||||
name: consul-standlone
|
||||
spec:
|
||||
clusterIP: None
|
||||
ports:
|
||||
- name: http-query
|
||||
port: 8500
|
||||
protocol: TCP
|
||||
targetPort: 8500
|
||||
selector:
|
||||
name: consul-standlone
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: httpbin
|
||||
namespace: higress-conformance-app-backend
|
||||
spec:
|
||||
containers:
|
||||
- name: httpbin
|
||||
image: registry.cn-hangzhou.aliyuncs.com/2456868764/httpbin:1.0.2
|
||||
command:
|
||||
- /app/httpbin
|
||||
- --registry-type=consul
|
||||
- --consul-server-address=consul-service:8500
|
||||
- --server-port=8080
|
||||
- --service-tags=higress,httpbin
|
||||
env:
|
||||
- name: SERVICE_NAME
|
||||
value: httpbin
|
||||
- name: VERSION
|
||||
value: v1
|
||||
- name: POD_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.name
|
||||
- name: POD_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
- name: POD_IP
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: status.podIP
|
||||
- name: NODE_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: spec.nodeName
|
||||
- name: SERVICE_ACCOUNT
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: spec.serviceAccountName
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
name: http
|
||||
protocol: TCP
|
||||
readinessProbe:
|
||||
failureThreshold: 5
|
||||
httpGet:
|
||||
path: /ping
|
||||
port: 8080
|
||||
scheme: HTTP
|
||||
periodSeconds: 20
|
||||
successThreshold: 1
|
||||
timeoutSeconds: 1
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /ping
|
||||
port: 8080
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 20
|
||||
periodSeconds: 20
|
||||
39
test/e2e/conformance/base/dubbo.yaml
Normal file
39
test/e2e/conformance/base/dubbo.yaml
Normal 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.
|
||||
|
||||
# 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: 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
|
||||
125
test/e2e/conformance/base/eureka.yaml
Normal file
125
test/e2e/conformance/base/eureka.yaml
Normal file
@@ -0,0 +1,125 @@
|
||||
# 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: ConfigMap
|
||||
metadata:
|
||||
name: eureka-cm
|
||||
namespace: higress-conformance-app-backend
|
||||
data:
|
||||
# if you want to deploy n instances of eureka cluster,
|
||||
# you should set eureka_service_address: http://eureka-0.eureka:8761/eureka,...,http://eureka-(n-1).eureka:8761/eureka
|
||||
eureka_service_address: "http://eureka-0.eureka.higress-conformance-app-backend.svc:8761/eureka"
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: eureka
|
||||
namespace: higress-conformance-app-backend
|
||||
labels:
|
||||
app: eureka
|
||||
spec:
|
||||
clusterIP: None
|
||||
ports:
|
||||
- port: 8761
|
||||
name: eureka
|
||||
selector:
|
||||
app: eureka
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: eureka
|
||||
namespace: higress-conformance-app-backend
|
||||
spec:
|
||||
serviceName: eureka
|
||||
# n instances
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: eureka
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: eureka
|
||||
spec:
|
||||
containers:
|
||||
- name: eureka
|
||||
image: bitinit/eureka
|
||||
ports:
|
||||
- containerPort: 8761
|
||||
name: http
|
||||
protocol: TCP
|
||||
resources:
|
||||
requests:
|
||||
memory: "500Mi"
|
||||
cpu: "500m"
|
||||
env:
|
||||
- name: EUREKA_SERVER_ADDRESS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: eureka-cm
|
||||
key: eureka_service_address
|
||||
- name: ENVIRONMENT
|
||||
value: "prod"
|
||||
- name: JVM_OPTS
|
||||
value: "-Xms1g -Xmx1g"
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 8761
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 8761
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: eureka-registry-provider
|
||||
namespace: higress-conformance-app-backend
|
||||
spec:
|
||||
containers:
|
||||
- name: eureka-registry-provider
|
||||
image: charlie1380/eureka-registry-provider:v0.3.0
|
||||
env:
|
||||
- name: EUREKA_SERVER_ADDRESS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: eureka-cm
|
||||
key: eureka_service_address
|
||||
ports:
|
||||
- containerPort: 8888
|
||||
name: http
|
||||
protocol: TCP
|
||||
readinessProbe:
|
||||
failureThreshold: 5
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 8888
|
||||
scheme: HTTP
|
||||
periodSeconds: 1
|
||||
@@ -318,250 +318,3 @@ spec:
|
||||
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
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: consul-standlone
|
||||
namespace: higress-conformance-app-backend
|
||||
labels:
|
||||
name: consul-standlone
|
||||
spec:
|
||||
containers:
|
||||
- name: consul
|
||||
image: docker.io/hashicorp/consul:1.16.0
|
||||
resources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
ports:
|
||||
- containerPort: 8500
|
||||
name: http
|
||||
protocol: TCP
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: consul-service
|
||||
namespace: higress-conformance-app-backend
|
||||
labels:
|
||||
name: consul-standlone
|
||||
spec:
|
||||
clusterIP: None
|
||||
ports:
|
||||
- name: http-query
|
||||
port: 8500
|
||||
protocol: TCP
|
||||
targetPort: 8500
|
||||
selector:
|
||||
name: consul-standlone
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: httpbin
|
||||
namespace: higress-conformance-app-backend
|
||||
spec:
|
||||
containers:
|
||||
- name: httpbin
|
||||
image: registry.cn-hangzhou.aliyuncs.com/2456868764/httpbin:1.0.2
|
||||
command:
|
||||
- /app/httpbin
|
||||
- --registry-type=consul
|
||||
- --consul-server-address=consul-service:8500
|
||||
- --server-port=8080
|
||||
- --service-tags=higress,httpbin
|
||||
env:
|
||||
- name: SERVICE_NAME
|
||||
value: httpbin
|
||||
- name: VERSION
|
||||
value: v1
|
||||
- name: POD_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.name
|
||||
- name: POD_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
- name: POD_IP
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: status.podIP
|
||||
- name: NODE_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: spec.nodeName
|
||||
- name: SERVICE_ACCOUNT
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: spec.serviceAccountName
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
name: http
|
||||
protocol: TCP
|
||||
readinessProbe:
|
||||
failureThreshold: 5
|
||||
httpGet:
|
||||
path: /ping
|
||||
port: 8080
|
||||
scheme: HTTP
|
||||
periodSeconds: 20
|
||||
successThreshold: 1
|
||||
timeoutSeconds: 1
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /ping
|
||||
port: 8080
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 20
|
||||
periodSeconds: 20
|
||||
---
|
||||
# Eureka service registry
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: eureka-cm
|
||||
namespace: higress-conformance-app-backend
|
||||
data:
|
||||
# if you want to deploy n instances of eureka cluster,
|
||||
# you should set eureka_service_address: http://eureka-0.eureka:8761/eureka,...,http://eureka-(n-1).eureka:8761/eureka
|
||||
eureka_service_address: "http://eureka-0.eureka.higress-conformance-app-backend.svc:8761/eureka"
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: eureka
|
||||
namespace: higress-conformance-app-backend
|
||||
labels:
|
||||
app: eureka
|
||||
spec:
|
||||
clusterIP: None
|
||||
ports:
|
||||
- port: 8761
|
||||
name: eureka
|
||||
selector:
|
||||
app: eureka
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: eureka
|
||||
namespace: higress-conformance-app-backend
|
||||
spec:
|
||||
serviceName: eureka
|
||||
# n instances
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: eureka
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: eureka
|
||||
spec:
|
||||
containers:
|
||||
- name: eureka
|
||||
image: bitinit/eureka
|
||||
ports:
|
||||
- containerPort: 8761
|
||||
name: http
|
||||
protocol: TCP
|
||||
resources:
|
||||
requests:
|
||||
memory: "500Mi"
|
||||
cpu: "500m"
|
||||
env:
|
||||
- name: EUREKA_SERVER_ADDRESS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: eureka-cm
|
||||
key: eureka_service_address
|
||||
- name: ENVIRONMENT
|
||||
value: "prod"
|
||||
- name: JVM_OPTS
|
||||
value: "-Xms1g -Xmx1g"
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 8761
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 8761
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: eureka-registry-provider
|
||||
namespace: higress-conformance-app-backend
|
||||
spec:
|
||||
containers:
|
||||
- name: eureka-registry-provider
|
||||
image: charlie1380/eureka-registry-provider:v0.3.0
|
||||
env:
|
||||
- name: EUREKA_SERVER_ADDRESS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: eureka-cm
|
||||
key: eureka_service_address
|
||||
ports:
|
||||
- containerPort: 8888
|
||||
name: http
|
||||
protocol: TCP
|
||||
readinessProbe:
|
||||
failureThreshold: 5
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 8888
|
||||
scheme: HTTP
|
||||
periodSeconds: 1
|
||||
|
||||
|
||||
49
test/e2e/conformance/base/nacos.yaml
Normal file
49
test/e2e/conformance/base/nacos.yaml
Normal 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.
|
||||
|
||||
# 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: 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
|
||||
133
test/e2e/conformance/tests/cpp-wasm-basic-auth.go
Normal file
133
test/e2e/conformance/tests/cpp-wasm-basic-auth.go
Normal 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() {
|
||||
Register(CPPWasmPluginsBasicAuth)
|
||||
}
|
||||
|
||||
var CPPWasmPluginsBasicAuth = suite.ConformanceTest{
|
||||
ShortName: "CPPWasmPluginsBasicAuth",
|
||||
Description: "The Ingress in the higress-conformance-infra namespace test the CPP basic-auth WASM plugin.",
|
||||
Manifests: []string{"tests/cpp-wasm-basic-auth.yaml"},
|
||||
Features: []suite.SupportedFeature{suite.WASMCPPConformanceFeature},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testcases := []http.Assertion{
|
||||
{
|
||||
Meta: http.AssertionMeta{
|
||||
TestCaseName: "case 1: Successful authentication",
|
||||
TargetBackend: "infra-backend-v1",
|
||||
TargetNamespace: "higress-conformance-infra",
|
||||
},
|
||||
Request: http.AssertionRequest{
|
||||
ActualRequest: http.Request{
|
||||
Host: "foo.com",
|
||||
Path: "/foo",
|
||||
Headers: map[string]string{"Authorization": "Basic YWRtaW46MTIzNDU2"}, // base64("admin:123456")
|
||||
},
|
||||
ExpectedRequest: &http.ExpectedRequest{
|
||||
Request: http.Request{
|
||||
Host: "foo.com",
|
||||
Path: "/foo",
|
||||
Headers: map[string]string{"X-Mse-Consumer": "consumer1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Response: http.AssertionResponse{
|
||||
ExpectedResponse: http.Response{
|
||||
StatusCode: 200,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Meta: http.AssertionMeta{
|
||||
TestCaseName: "case 2: No Basic Authentication information found",
|
||||
TargetBackend: "infra-backend-v1",
|
||||
TargetNamespace: "higress-conformance-infra",
|
||||
},
|
||||
Request: http.AssertionRequest{
|
||||
ActualRequest: http.Request{
|
||||
Host: "foo.com",
|
||||
Path: "/foo",
|
||||
},
|
||||
},
|
||||
Response: http.AssertionResponse{
|
||||
ExpectedResponse: http.Response{
|
||||
StatusCode: 401,
|
||||
},
|
||||
AdditionalResponseHeaders: map[string]string{
|
||||
"WWW-Authenticate": "Basic realm=MSE Gateway",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Meta: http.AssertionMeta{
|
||||
TestCaseName: "case 3: Invalid username and/or password",
|
||||
TargetBackend: "infra-backend-v1",
|
||||
TargetNamespace: "higress-conformance-infra",
|
||||
},
|
||||
Request: http.AssertionRequest{
|
||||
ActualRequest: http.Request{
|
||||
Host: "foo.com",
|
||||
Path: "/foo",
|
||||
Headers: map[string]string{"Authorization": "Basic YWRtaW46cXdlcg=="}, // base64("admin:qwer")
|
||||
},
|
||||
},
|
||||
Response: http.AssertionResponse{
|
||||
ExpectedResponse: http.Response{
|
||||
StatusCode: 401,
|
||||
},
|
||||
AdditionalResponseHeaders: map[string]string{
|
||||
"WWW-Authenticate": "Basic realm=MSE Gateway",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Meta: http.AssertionMeta{
|
||||
TestCaseName: "case 4: Unauthorized consumer",
|
||||
TargetBackend: "infra-backend-v1",
|
||||
TargetNamespace: "higress-conformance-infra",
|
||||
},
|
||||
Request: http.AssertionRequest{
|
||||
ActualRequest: http.Request{
|
||||
Host: "foo.com",
|
||||
Path: "/foo",
|
||||
Headers: map[string]string{"Authorization": "Basic Z3Vlc3Q6YWJj"}, // base64("guest:abc")
|
||||
},
|
||||
},
|
||||
Response: http.AssertionResponse{
|
||||
ExpectedResponse: http.Response{
|
||||
StatusCode: 403,
|
||||
},
|
||||
AdditionalResponseHeaders: map[string]string{
|
||||
"WWW-Authenticate": "Basic realm=MSE Gateway",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
t.Run("WasmPlugins CPP basic-auth", func(t *testing.T) {
|
||||
for _, testcase := range testcases {
|
||||
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, testcase)
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
56
test/e2e/conformance/tests/cpp-wasm-basic-auth.yaml
Normal file
56
test/e2e/conformance/tests/cpp-wasm-basic-auth.yaml
Normal file
@@ -0,0 +1,56 @@
|
||||
# Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
name: wasmplugin-cpp-basic-auth
|
||||
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
|
||||
---
|
||||
apiVersion: extensions.higress.io/v1alpha1
|
||||
kind: WasmPlugin
|
||||
metadata:
|
||||
name: cpp-basic-auth
|
||||
namespace: higress-system
|
||||
spec:
|
||||
defaultConfig:
|
||||
consumers:
|
||||
- credential: admin:123456
|
||||
name: consumer1
|
||||
- credential: guest:abc
|
||||
name: consumer2
|
||||
global_auth: false
|
||||
defaultConfigDisable: false
|
||||
matchRules:
|
||||
- config:
|
||||
allow:
|
||||
- consumer1
|
||||
configDisable: false
|
||||
ingress:
|
||||
- higress-conformance-infra/wasmplugin-cpp-basic-auth
|
||||
url: file:///opt/plugins/wasm-cpp/extensions/basic-auth/plugin.wasm
|
||||
@@ -22,13 +22,14 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, CPPWasmPluginsKeyAuth)
|
||||
Register(CPPWasmPluginsKeyAuth)
|
||||
}
|
||||
|
||||
var CPPWasmPluginsKeyAuth = suite.ConformanceTest{
|
||||
ShortName: "CPPWasmPluginsKeyAuth",
|
||||
Description: "The Ingress in the higress-conformance-infra namespace test the CPP key_auth wasmplugins.",
|
||||
Manifests: []string{"tests/cpp-key_auth.yaml"},
|
||||
Manifests: []string{"tests/cpp-wasm-key-auth.yaml"},
|
||||
Features: []suite.SupportedFeature{suite.WASMCPPConformanceFeature},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testcases := []http.Assertion{
|
||||
{
|
||||
@@ -22,13 +22,14 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, CPPWasmPluginsRequestBlock)
|
||||
Register(CPPWasmPluginsRequestBlock)
|
||||
}
|
||||
|
||||
var CPPWasmPluginsRequestBlock = suite.ConformanceTest{
|
||||
ShortName: "CPPWasmPluginsRequestBlock",
|
||||
Description: "The Ingress in the higress-conformance-infra namespace test the cpp request-block wasmplugins.",
|
||||
Manifests: []string{"tests/cpp-request_block.yaml"},
|
||||
Manifests: []string{"tests/cpp-wasm-request-block.yaml"},
|
||||
Features: []suite.SupportedFeature{suite.WASMCPPConformanceFeature},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testcases := []http.Assertion{
|
||||
{
|
||||
133
test/e2e/conformance/tests/go-wasm-basic-auth.go
Normal file
133
test/e2e/conformance/tests/go-wasm-basic-auth.go
Normal 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() {
|
||||
Register(WasmPluginsBasicAuth)
|
||||
}
|
||||
|
||||
var WasmPluginsBasicAuth = suite.ConformanceTest{
|
||||
ShortName: "WasmPluginsBasicAuth",
|
||||
Description: "The Ingress in the higress-conformance-infra namespace test the basic-auth WASM plugin.",
|
||||
Manifests: []string{"tests/go-wasm-basic-auth.yaml"},
|
||||
Features: []suite.SupportedFeature{suite.WASMGoConformanceFeature},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testcases := []http.Assertion{
|
||||
{
|
||||
Meta: http.AssertionMeta{
|
||||
TestCaseName: "case 1: Successful authentication",
|
||||
TargetBackend: "infra-backend-v1",
|
||||
TargetNamespace: "higress-conformance-infra",
|
||||
},
|
||||
Request: http.AssertionRequest{
|
||||
ActualRequest: http.Request{
|
||||
Host: "foo.com",
|
||||
Path: "/foo",
|
||||
Headers: map[string]string{"Authorization": "Basic YWRtaW46MTIzNDU2"}, // base64("admin:123456")
|
||||
},
|
||||
ExpectedRequest: &http.ExpectedRequest{
|
||||
Request: http.Request{
|
||||
Host: "foo.com",
|
||||
Path: "/foo",
|
||||
Headers: map[string]string{"X-Mse-Consumer": "consumer1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Response: http.AssertionResponse{
|
||||
ExpectedResponse: http.Response{
|
||||
StatusCode: 200,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Meta: http.AssertionMeta{
|
||||
TestCaseName: "case 2: No Basic Authentication information found",
|
||||
TargetBackend: "infra-backend-v1",
|
||||
TargetNamespace: "higress-conformance-infra",
|
||||
},
|
||||
Request: http.AssertionRequest{
|
||||
ActualRequest: http.Request{
|
||||
Host: "foo.com",
|
||||
Path: "/foo",
|
||||
},
|
||||
},
|
||||
Response: http.AssertionResponse{
|
||||
ExpectedResponse: http.Response{
|
||||
StatusCode: 401,
|
||||
},
|
||||
AdditionalResponseHeaders: map[string]string{
|
||||
"WWW-Authenticate": "Basic realm=MSE Gateway",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Meta: http.AssertionMeta{
|
||||
TestCaseName: "case 3: Invalid username and/or password",
|
||||
TargetBackend: "infra-backend-v1",
|
||||
TargetNamespace: "higress-conformance-infra",
|
||||
},
|
||||
Request: http.AssertionRequest{
|
||||
ActualRequest: http.Request{
|
||||
Host: "foo.com",
|
||||
Path: "/foo",
|
||||
Headers: map[string]string{"Authorization": "Basic YWRtaW46cXdlcg=="}, // base64("admin:qwer")
|
||||
},
|
||||
},
|
||||
Response: http.AssertionResponse{
|
||||
ExpectedResponse: http.Response{
|
||||
StatusCode: 401,
|
||||
},
|
||||
AdditionalResponseHeaders: map[string]string{
|
||||
"WWW-Authenticate": "Basic realm=MSE Gateway",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Meta: http.AssertionMeta{
|
||||
TestCaseName: "case 4: Unauthorized consumer",
|
||||
TargetBackend: "infra-backend-v1",
|
||||
TargetNamespace: "higress-conformance-infra",
|
||||
},
|
||||
Request: http.AssertionRequest{
|
||||
ActualRequest: http.Request{
|
||||
Host: "foo.com",
|
||||
Path: "/foo",
|
||||
Headers: map[string]string{"Authorization": "Basic Z3Vlc3Q6YWJj"}, // base64("guest:abc")
|
||||
},
|
||||
},
|
||||
Response: http.AssertionResponse{
|
||||
ExpectedResponse: http.Response{
|
||||
StatusCode: 403,
|
||||
},
|
||||
AdditionalResponseHeaders: map[string]string{
|
||||
"WWW-Authenticate": "Basic realm=MSE Gateway",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
t.Run("WasmPlugins basic-auth", func(t *testing.T) {
|
||||
for _, testcase := range testcases {
|
||||
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, testcase)
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
56
test/e2e/conformance/tests/go-wasm-basic-auth.yaml
Normal file
56
test/e2e/conformance/tests/go-wasm-basic-auth.yaml
Normal file
@@ -0,0 +1,56 @@
|
||||
# Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
name: wasmplugin-basic-auth
|
||||
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
|
||||
---
|
||||
apiVersion: extensions.higress.io/v1alpha1
|
||||
kind: WasmPlugin
|
||||
metadata:
|
||||
name: basic-auth
|
||||
namespace: higress-system
|
||||
spec:
|
||||
defaultConfig:
|
||||
consumers:
|
||||
- credential: admin:123456
|
||||
name: consumer1
|
||||
- credential: guest:abc
|
||||
name: consumer2
|
||||
global_auth: false
|
||||
defaultConfigDisable: false
|
||||
matchRules:
|
||||
- config:
|
||||
allow:
|
||||
- consumer1
|
||||
configDisable: false
|
||||
ingress:
|
||||
- higress-conformance-infra/wasmplugin-basic-auth
|
||||
url: file:///opt/plugins/wasm-go/extensions/basic-auth/plugin.wasm
|
||||
60
test/e2e/conformance/tests/go-wasm-jwt-auth.go
Normal file
60
test/e2e/conformance/tests/go-wasm-jwt-auth.go
Normal file
@@ -0,0 +1,60 @@
|
||||
// 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() {
|
||||
Register(WasmPluginsJwtAuth)
|
||||
}
|
||||
|
||||
var WasmPluginsJwtAuth = suite.ConformanceTest{
|
||||
ShortName: "WasmPluginsJwtAuth",
|
||||
Description: "The Ingress in the higress-conformance-infra namespace test the jwt-auth wasmplugins.",
|
||||
Manifests: []string{"tests/go-wasm-jwt-auth.yaml"},
|
||||
Features: []suite.SupportedFeature{suite.WASMGoConformanceFeature},
|
||||
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: "/info",
|
||||
UnfollowRedirect: true,
|
||||
},
|
||||
},
|
||||
Response: http.AssertionResponse{
|
||||
ExpectedResponse: http.Response{
|
||||
StatusCode: 401,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
t.Run("WasmPlugins jwt-auth", func(t *testing.T) {
|
||||
for _, testcase := range testcases {
|
||||
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, testcase)
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
43
test/e2e/conformance/tests/go-wasm-jwt-auth.yaml
Normal file
43
test/e2e/conformance/tests/go-wasm-jwt-auth.yaml
Normal file
@@ -0,0 +1,43 @@
|
||||
# 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:
|
||||
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: jwt-auth
|
||||
namespace: higress-system
|
||||
spec:
|
||||
defaultConfig:
|
||||
token_headers: token
|
||||
token_secret_key: Dav7kfq3iA8S!JUj8&CUkdnQe72E@Cw6
|
||||
url: file:///opt/plugins/wasm-go/extensions/jwt-auth/plugin.wasm
|
||||
@@ -22,13 +22,14 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, WasmPluginsRequestBlock)
|
||||
Register(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"},
|
||||
Manifests: []string{"tests/go-wasm-request-block.yaml"},
|
||||
Features: []suite.SupportedFeature{suite.WASMGoConformanceFeature},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testcases := []http.Assertion{
|
||||
{
|
||||
@@ -23,13 +23,14 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteAppRoot)
|
||||
Register(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"},
|
||||
Features: []suite.SupportedFeature{suite.HTTPConformanceFeature},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testcases := []http.Assertion{
|
||||
{
|
||||
|
||||
@@ -22,13 +22,14 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteCanaryHeaderWithCustomizedHeader)
|
||||
Register(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"},
|
||||
Features: []suite.SupportedFeature{suite.HTTPConformanceFeature},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testcases := []http.Assertion{
|
||||
{
|
||||
|
||||
@@ -22,13 +22,14 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteCanaryHeader)
|
||||
Register(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"},
|
||||
Features: []suite.SupportedFeature{suite.HTTPConformanceFeature},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testcases := []http.Assertion{
|
||||
{
|
||||
|
||||
@@ -22,13 +22,14 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteCanaryWeight)
|
||||
Register(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"},
|
||||
Features: []suite.SupportedFeature{suite.HTTPConformanceFeature},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testcases := []http.Assertion{
|
||||
// test if the weight is 0
|
||||
|
||||
@@ -22,13 +22,14 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteConsulHttpBin)
|
||||
Register(HTTPRouteConsulHttpBin)
|
||||
}
|
||||
|
||||
var HTTPRouteConsulHttpBin = suite.ConformanceTest{
|
||||
ShortName: "HTTPRouteConsulHttpBin",
|
||||
Description: "The Ingress in the higress-conformance-infra namespace uses the consul service registry.",
|
||||
Manifests: []string{"tests/httproute-consul-httpbin.yaml"},
|
||||
Features: []suite.SupportedFeature{suite.ConsulConformanceFeature},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testcases := []http.Assertion{
|
||||
{
|
||||
|
||||
@@ -27,13 +27,14 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteDownstreamEncryption)
|
||||
Register(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"},
|
||||
Features: []suite.SupportedFeature{suite.HTTPConformanceFeature},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
// Prepare certificates and secrets for testcases
|
||||
caCertOut, _, caCert, caKey := cert.MustGenerateCaCert(t)
|
||||
|
||||
@@ -22,13 +22,14 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteEnableCors)
|
||||
Register(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"},
|
||||
Features: []suite.SupportedFeature{suite.HTTPConformanceFeature},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testcases := []http.Assertion{
|
||||
{
|
||||
|
||||
@@ -22,13 +22,14 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteEnableIgnoreCase)
|
||||
Register(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"},
|
||||
Features: []suite.SupportedFeature{suite.HTTPConformanceFeature},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testcases := []http.Assertion{
|
||||
{
|
||||
|
||||
@@ -23,13 +23,14 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteEurekaRegistry)
|
||||
Register(HTTPRouteEurekaRegistry)
|
||||
}
|
||||
|
||||
var HTTPRouteEurekaRegistry = suite.ConformanceTest{
|
||||
ShortName: "HTTPRouteEurekaRegistry",
|
||||
Description: "The Ingress in the higress-conformance-infra namespace uses the eureka service registry.",
|
||||
Manifests: []string{"tests/httproute-eureka-registry.yaml"},
|
||||
Features: []suite.SupportedFeature{suite.EurekaConformanceFeature},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testcases := []http.Assertion{
|
||||
{
|
||||
|
||||
@@ -23,13 +23,14 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, HttpForceRedirectHttps)
|
||||
Register(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"},
|
||||
Features: []suite.SupportedFeature{suite.HTTPConformanceFeature},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testcases := []http.Assertion{
|
||||
{
|
||||
|
||||
@@ -22,13 +22,14 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteFullPathRegex)
|
||||
Register(HTTPRouteFullPathRegex)
|
||||
}
|
||||
|
||||
var HTTPRouteFullPathRegex = suite.ConformanceTest{
|
||||
ShortName: "HTTPRouteFullPathRegex",
|
||||
Description: "test for 'higress.io/full-path-regex' annotation",
|
||||
Manifests: []string{"tests/httproute-full-path-regex.yaml"},
|
||||
Features: []suite.SupportedFeature{suite.HTTPConformanceFeature},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testCases := []http.Assertion{
|
||||
{
|
||||
|
||||
@@ -22,13 +22,14 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteHostNameSameNamespace)
|
||||
Register(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"},
|
||||
Features: []suite.SupportedFeature{suite.HTTPConformanceFeature},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testcases := []http.Assertion{
|
||||
{
|
||||
|
||||
@@ -22,13 +22,14 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteHttp2Rpc)
|
||||
Register(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"},
|
||||
Features: []suite.SupportedFeature{suite.DubboConformanceFeature, suite.NacosConformanceFeature},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testcases := []http.Assertion{
|
||||
{
|
||||
@@ -47,7 +48,7 @@ var HTTPRouteHttp2Rpc = suite.ConformanceTest{
|
||||
},
|
||||
},
|
||||
}
|
||||
t.Run("HTTPRoute app root", func(t *testing.T) {
|
||||
t.Run("HTTPRoute uses HTTP to RPC", func(t *testing.T) {
|
||||
for _, testcase := range testcases {
|
||||
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, testcase)
|
||||
}
|
||||
|
||||
@@ -22,13 +22,14 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteMatchHeaders)
|
||||
Register(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"},
|
||||
Features: []suite.SupportedFeature{suite.HTTPConformanceFeature},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testcases := []http.Assertion{
|
||||
{
|
||||
|
||||
@@ -22,13 +22,14 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteMatchMethods)
|
||||
Register(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"},
|
||||
Features: []suite.SupportedFeature{suite.HTTPConformanceFeature},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testcases := []http.Assertion{
|
||||
{
|
||||
|
||||
@@ -22,13 +22,14 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteMatchPath)
|
||||
Register(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"},
|
||||
Features: []suite.SupportedFeature{suite.HTTPConformanceFeature},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testcases := []http.Assertion{
|
||||
{
|
||||
|
||||
@@ -22,13 +22,14 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteMatchQueryParams)
|
||||
Register(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"},
|
||||
Features: []suite.SupportedFeature{suite.HTTPConformanceFeature},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testcases := []http.Assertion{
|
||||
{
|
||||
|
||||
@@ -23,13 +23,14 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, HTTPRoutePermanentRedirectCode)
|
||||
Register(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"},
|
||||
Features: []suite.SupportedFeature{suite.HTTPConformanceFeature},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testcases := []http.Assertion{
|
||||
{
|
||||
|
||||
@@ -23,13 +23,14 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, HTTPRoutePermanentRedirect)
|
||||
Register(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"},
|
||||
Features: []suite.SupportedFeature{suite.HTTPConformanceFeature},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testcases := []http.Assertion{
|
||||
{
|
||||
|
||||
@@ -23,13 +23,14 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, HttpRedirectAsHttps)
|
||||
Register(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"},
|
||||
Features: []suite.SupportedFeature{suite.HTTPConformanceFeature},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testcases := []http.Assertion{
|
||||
{
|
||||
|
||||
@@ -22,13 +22,14 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteRequestHeaderControl)
|
||||
Register(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"},
|
||||
Features: []suite.SupportedFeature{suite.HTTPConformanceFeature},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testcases := []http.Assertion{
|
||||
{
|
||||
|
||||
@@ -22,13 +22,14 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteRewriteHost)
|
||||
Register(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"},
|
||||
Features: []suite.SupportedFeature{suite.HTTPConformanceFeature},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testcases := []http.Assertion{
|
||||
{
|
||||
|
||||
@@ -22,13 +22,14 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteRewritePath)
|
||||
Register(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"},
|
||||
Features: []suite.SupportedFeature{suite.HTTPConformanceFeature},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testCases := []http.Assertion{
|
||||
{
|
||||
|
||||
@@ -22,13 +22,14 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteSameHostAndPath)
|
||||
Register(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"},
|
||||
Features: []suite.SupportedFeature{suite.HTTPConformanceFeature},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testcases := []http.Assertion{
|
||||
{
|
||||
|
||||
@@ -22,13 +22,14 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteSimpleSameNamespace)
|
||||
Register(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"},
|
||||
Features: []suite.SupportedFeature{suite.HTTPConformanceFeature},
|
||||
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{
|
||||
|
||||
@@ -23,13 +23,14 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteTemporalRedirect)
|
||||
Register(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"},
|
||||
Features: []suite.SupportedFeature{suite.HTTPConformanceFeature},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testcases := []http.Assertion{
|
||||
{
|
||||
|
||||
@@ -22,13 +22,14 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteWhitelistSourceRange)
|
||||
Register(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"},
|
||||
Features: []suite.SupportedFeature{suite.HTTPConformanceFeature},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testcases := []http.Assertion{
|
||||
{
|
||||
|
||||
@@ -16,4 +16,11 @@ package tests
|
||||
|
||||
import "github.com/alibaba/higress/test/e2e/conformance/utils/suite"
|
||||
|
||||
var HigressConformanceTests []suite.ConformanceTest
|
||||
func Register(testcase suite.ConformanceTest) {
|
||||
if len(testcase.Features) == 0 {
|
||||
panic("must set at least one feature of the test case")
|
||||
}
|
||||
ConformanceTests = append(ConformanceTests, testcase)
|
||||
}
|
||||
|
||||
var ConformanceTests []suite.ConformanceTest
|
||||
@@ -23,4 +23,7 @@ var (
|
||||
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")
|
||||
IsWasmPluginTest = flag.Bool("isWasmPluginTest", false, "Determine if run wasm plugin conformance test")
|
||||
WasmPluginType = flag.String("wasmPluginType", "GO", "Define wasm plugin type, currently supports GO, CPP")
|
||||
WasmPluginName = flag.String("wasmPluginName", "", "Define wasm plugin name")
|
||||
)
|
||||
|
||||
@@ -97,7 +97,7 @@ func (a Applier) MustApplyObjectsWithCleanup(t *testing.T, c client.Client, time
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeoutConfig.CreateTimeout)
|
||||
defer cancel()
|
||||
|
||||
t.Logf("Creating %s %s", resource.GetName(), resource.GetObjectKind().GroupVersionKind().Kind)
|
||||
t.Logf("🏗 Creating %s %s", resource.GetName(), resource.GetObjectKind().GroupVersionKind().Kind)
|
||||
|
||||
err := c.Create(ctx, resource)
|
||||
if err != nil {
|
||||
@@ -110,7 +110,7 @@ func (a Applier) MustApplyObjectsWithCleanup(t *testing.T, c client.Client, time
|
||||
t.Cleanup(func() {
|
||||
ctx, cancel = context.WithTimeout(context.Background(), timeoutConfig.DeleteTimeout)
|
||||
defer cancel()
|
||||
t.Logf("Deleting %s %s", resource.GetName(), resource.GetObjectKind().GroupVersionKind().Kind)
|
||||
t.Logf("🚮 Deleting %s %s", resource.GetName(), resource.GetObjectKind().GroupVersionKind().Kind)
|
||||
err = c.Delete(ctx, resource)
|
||||
require.NoErrorf(t, err, "error deleting resource")
|
||||
})
|
||||
@@ -129,7 +129,7 @@ func (a Applier) MustApplyWithCleanup(t *testing.T, c client.Client, timeoutConf
|
||||
|
||||
resources, err := a.prepareResources(t, decoder)
|
||||
if err != nil {
|
||||
t.Logf("manifest: %s", data.String())
|
||||
t.Logf("🧳 Manifest: %s", data.String())
|
||||
require.NoErrorf(t, err, "error parsing manifest")
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ func (a Applier) MustApplyWithCleanup(t *testing.T, c client.Client, timeoutConf
|
||||
if !apierrors.IsNotFound(err) {
|
||||
require.NoErrorf(t, err, "error getting resource")
|
||||
}
|
||||
t.Logf("Creating %s %s", uObj.GetName(), uObj.GetKind())
|
||||
t.Logf("🏗 Creating %s %s", uObj.GetName(), uObj.GetKind())
|
||||
err = c.Create(ctx, uObj)
|
||||
require.NoErrorf(t, err, "error creating resource")
|
||||
|
||||
@@ -154,7 +154,7 @@ func (a Applier) MustApplyWithCleanup(t *testing.T, c client.Client, timeoutConf
|
||||
t.Cleanup(func() {
|
||||
ctx, cancel = context.WithTimeout(context.Background(), timeoutConfig.DeleteTimeout)
|
||||
defer cancel()
|
||||
t.Logf("Deleting %s %s", uObj.GetName(), uObj.GetKind())
|
||||
t.Logf("🚮 Deleting %s %s", uObj.GetName(), uObj.GetKind())
|
||||
err = c.Delete(ctx, uObj)
|
||||
require.NoErrorf(t, err, "error deleting resource")
|
||||
})
|
||||
@@ -163,14 +163,14 @@ func (a Applier) MustApplyWithCleanup(t *testing.T, c client.Client, timeoutConf
|
||||
}
|
||||
|
||||
uObj.SetResourceVersion(fetchedObj.GetResourceVersion())
|
||||
t.Logf("Updating %s %s", uObj.GetName(), uObj.GetKind())
|
||||
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())
|
||||
t.Logf("🚮 Deleting %s %s", uObj.GetName(), uObj.GetKind())
|
||||
err = c.Delete(ctx, uObj)
|
||||
require.NoErrorf(t, err, "error deleting resource")
|
||||
})
|
||||
|
||||
@@ -54,7 +54,7 @@ func NamespacesMustBeAccepted(t *testing.T, c client.Client, timeoutConfig confi
|
||||
podList := &v1.PodList{}
|
||||
err := c.List(ctx, podList, client.InNamespace(ns))
|
||||
if err != nil {
|
||||
t.Errorf("Error listing Pods: %v", err)
|
||||
t.Errorf("❌ Error listing Pods: %v", err)
|
||||
}
|
||||
for _, pod := range podList.Items {
|
||||
if !FindPodConditionInList(t, pod.Status.Conditions, "Ready", "True") &&
|
||||
@@ -64,7 +64,7 @@ func NamespacesMustBeAccepted(t *testing.T, c client.Client, timeoutConfig confi
|
||||
}
|
||||
}
|
||||
}
|
||||
t.Logf("Gateways and Pods in %s namespaces ready", strings.Join(namespaces, ", "))
|
||||
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, ", "))
|
||||
@@ -72,7 +72,7 @@ func NamespacesMustBeAccepted(t *testing.T, c client.Client, timeoutConfig confi
|
||||
|
||||
func ConditionsMatch(t *testing.T, expected, actual []metav1.Condition) bool {
|
||||
if len(actual) < len(expected) {
|
||||
t.Logf("Expected more conditions to be present")
|
||||
t.Logf("⌛️ Expected more conditions to be present")
|
||||
return false
|
||||
}
|
||||
for _, condition := range expected {
|
||||
@@ -81,7 +81,7 @@ func ConditionsMatch(t *testing.T, expected, actual []metav1.Condition) bool {
|
||||
}
|
||||
}
|
||||
|
||||
t.Logf("Conditions matched expectations")
|
||||
t.Logf("✅ Conditions matched expectations")
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -95,14 +95,14 @@ func FindConditionInList(t *testing.T, conditions []metav1.Condition, condName,
|
||||
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 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 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)
|
||||
t.Logf("⌛️ %s was not in conditions list", condName)
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -112,10 +112,10 @@ func FindPodConditionInList(t *testing.T, conditions []v1.PodCondition, 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 condition set to %s, expected %s", condName, cond.Status, condValue)
|
||||
}
|
||||
}
|
||||
|
||||
t.Logf("%s was not in conditions list", condName)
|
||||
t.Logf("⌛️ %s was not in conditions list", condName)
|
||||
return false
|
||||
}
|
||||
|
||||
45
test/e2e/conformance/utils/suite/features.go
Normal file
45
test/e2e/conformance/utils/suite/features.go
Normal 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.
|
||||
|
||||
package suite
|
||||
|
||||
import "istio.io/istio/pilot/pkg/util/sets"
|
||||
|
||||
type SupportedFeature string
|
||||
|
||||
const (
|
||||
// core: http
|
||||
HTTPConformanceFeature SupportedFeature = "http"
|
||||
|
||||
// extended: extensibility
|
||||
WASMGoConformanceFeature SupportedFeature = "wasm-go"
|
||||
WASMCPPConformanceFeature SupportedFeature = "wasm-cpp"
|
||||
|
||||
// extended: service discovery
|
||||
DubboConformanceFeature SupportedFeature = "dubbo"
|
||||
EurekaConformanceFeature SupportedFeature = "eureka"
|
||||
ConsulConformanceFeature SupportedFeature = "consul"
|
||||
NacosConformanceFeature SupportedFeature = "nacos"
|
||||
)
|
||||
|
||||
var AllFeatures = sets.Set{}.
|
||||
Insert(string(HTTPConformanceFeature)).
|
||||
Insert(string(DubboConformanceFeature)).
|
||||
Insert(string(EurekaConformanceFeature)).
|
||||
Insert(string(ConsulConformanceFeature)).
|
||||
Insert(string(NacosConformanceFeature))
|
||||
|
||||
var ExperimentFeatures = sets.Set{}.
|
||||
Insert(string(WASMGoConformanceFeature)).
|
||||
Insert(string(WASMCPPConformanceFeature))
|
||||
@@ -20,32 +20,41 @@ import (
|
||||
"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"
|
||||
"istio.io/istio/pilot/pkg/util/sets"
|
||||
"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
|
||||
Client client.Client
|
||||
RoundTripper roundtripper.RoundTripper
|
||||
GatewayAddress string
|
||||
IngressClassName string
|
||||
Debug bool
|
||||
Cleanup bool
|
||||
BaseManifests []string
|
||||
Applier kubernetes.Applier
|
||||
SkipTests sets.Set
|
||||
TimeoutConfig config.TimeoutConfig
|
||||
SupportedFeatures sets.Set
|
||||
}
|
||||
|
||||
// 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
|
||||
SupportedFeatures sets.Set
|
||||
ExemptFeatures sets.Set
|
||||
|
||||
EnableAllSupportedFeatures bool
|
||||
Client client.Client
|
||||
GatewayAddress string
|
||||
IngressClassName string
|
||||
Debug bool
|
||||
RoundTripper roundtripper.RoundTripper
|
||||
BaseManifests []string
|
||||
NamespaceLabels map[string]string
|
||||
// Options for wasm extended features
|
||||
WASMOptions
|
||||
|
||||
// CleanupBaseResources indicates whether or not the base test
|
||||
// resources such as Gateways should be cleaned up after the run.
|
||||
@@ -53,6 +62,12 @@ type Options struct {
|
||||
TimeoutConfig config.TimeoutConfig
|
||||
}
|
||||
|
||||
type WASMOptions struct {
|
||||
IsWasmPluginTest bool
|
||||
WasmPluginType string
|
||||
WasmPluginName string
|
||||
}
|
||||
|
||||
// New returns a new ConformanceTestSuite.
|
||||
func New(s Options) *ConformanceTestSuite {
|
||||
config.SetupTimeoutConfig(&s.TimeoutConfig)
|
||||
@@ -62,14 +77,33 @@ func New(s Options) *ConformanceTestSuite {
|
||||
roundTripper = &roundtripper.DefaultRoundTripper{Debug: s.Debug, TimeoutConfig: s.TimeoutConfig}
|
||||
}
|
||||
|
||||
if s.SupportedFeatures == nil {
|
||||
s.SupportedFeatures = sets.Set{}
|
||||
}
|
||||
|
||||
if s.IsWasmPluginTest {
|
||||
if s.WasmPluginType == "CPP" {
|
||||
s.SupportedFeatures.Insert(string(WASMCPPConformanceFeature))
|
||||
} else {
|
||||
s.SupportedFeatures.Insert(string(WASMGoConformanceFeature))
|
||||
}
|
||||
} else if s.EnableAllSupportedFeatures {
|
||||
s.SupportedFeatures = AllFeatures
|
||||
}
|
||||
|
||||
for feature := range s.ExemptFeatures {
|
||||
s.SupportedFeatures.Delete(feature)
|
||||
}
|
||||
|
||||
suite := &ConformanceTestSuite{
|
||||
Client: s.Client,
|
||||
RoundTripper: roundTripper,
|
||||
IngressClassName: s.IngressClassName,
|
||||
Debug: s.Debug,
|
||||
Cleanup: s.CleanupBaseResources,
|
||||
BaseManifests: s.BaseManifests,
|
||||
GatewayAddress: s.GatewayAddress,
|
||||
Client: s.Client,
|
||||
RoundTripper: roundTripper,
|
||||
IngressClassName: s.IngressClassName,
|
||||
Debug: s.Debug,
|
||||
Cleanup: s.CleanupBaseResources,
|
||||
BaseManifests: s.BaseManifests,
|
||||
SupportedFeatures: s.SupportedFeatures,
|
||||
GatewayAddress: s.GatewayAddress,
|
||||
Applier: kubernetes.Applier{
|
||||
NamespaceLabels: s.NamespaceLabels,
|
||||
},
|
||||
@@ -77,8 +111,14 @@ func New(s Options) *ConformanceTestSuite {
|
||||
}
|
||||
|
||||
// apply defaults
|
||||
if suite.BaseManifests == "" {
|
||||
suite.BaseManifests = "base/manifests.yaml"
|
||||
if suite.BaseManifests == nil {
|
||||
suite.BaseManifests = []string{
|
||||
"base/manifests.yaml",
|
||||
"base/consul.yaml",
|
||||
"base/dubbo.yaml",
|
||||
"base/eureka.yaml",
|
||||
"base/nacos.yaml",
|
||||
}
|
||||
}
|
||||
|
||||
return suite
|
||||
@@ -87,34 +127,36 @@ func New(s Options) *ConformanceTestSuite {
|
||||
// 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")
|
||||
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 base manifests")
|
||||
|
||||
t.Logf("Test Setup: Applying programmatic resources")
|
||||
for _, baseManifest := range suite.BaseManifests {
|
||||
suite.Applier.MustApplyWithCleanup(t, suite.Client, suite.TimeoutConfig, baseManifest, 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")
|
||||
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)
|
||||
|
||||
t.Logf("🌱 Supported Features: %+v", suite.SupportedFeatures.UnsortedList())
|
||||
}
|
||||
|
||||
// Run runs the provided set of conformance tests.
|
||||
// RunWithTests 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))
|
||||
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)
|
||||
@@ -125,17 +167,20 @@ func (suite *ConformanceTestSuite) Run(t *testing.T, tests []ConformanceTest) {
|
||||
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)
|
||||
cases += fmt.Sprintf("🎯 CaseNum: %d\nCaseName: %s\nScenario: %s\nFeatures: %+v\n\n", index+1, test.ShortName, test.Description, test.Features)
|
||||
}
|
||||
|
||||
return cases
|
||||
}
|
||||
|
||||
type ConformanceTests []ConformanceTest
|
||||
|
||||
// ConformanceTest is used to define each individual conformance test.
|
||||
type ConformanceTest struct {
|
||||
ShortName string
|
||||
Description string
|
||||
Manifests []string
|
||||
Features []SupportedFeature
|
||||
Slow bool
|
||||
Parallel bool
|
||||
Test func(*testing.T, *ConformanceTestSuite)
|
||||
@@ -148,8 +193,23 @@ func (test *ConformanceTest) Run(t *testing.T, suite *ConformanceTestSuite) {
|
||||
t.Parallel()
|
||||
}
|
||||
|
||||
// Check that all features exercised by the test have been opted into by
|
||||
// the suite.
|
||||
for _, feature := range test.Features {
|
||||
if !suite.SupportedFeatures.Contains(string(feature)) {
|
||||
t.Skipf("🏊🏼 Skipping %s: suite does not support %s", test.ShortName, feature)
|
||||
}
|
||||
}
|
||||
|
||||
// check that the test should not be skipped
|
||||
if suite.SkipTests.Contains(test.ShortName) {
|
||||
t.Skipf("🏊🏼 Skipping %s: test explicitly skipped", test.ShortName)
|
||||
}
|
||||
|
||||
t.Logf("🔥 Running Conformance Test: %s", test.ShortName)
|
||||
|
||||
for _, manifestLocation := range test.Manifests {
|
||||
t.Logf("Applying %s", manifestLocation)
|
||||
t.Logf("🧳 Applying Manifests: %s", manifestLocation)
|
||||
suite.Applier.MustApplyWithCleanup(t, suite.Client, suite.TimeoutConfig, manifestLocation, true)
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ package test
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -29,10 +28,6 @@ import (
|
||||
"github.com/alibaba/higress/test/e2e/conformance/utils/suite"
|
||||
)
|
||||
|
||||
var isWasmPluginTest = flag.Bool("isWasmPluginTest", false, "")
|
||||
var wasmPluginType = flag.String("wasmPluginType", "GO", "")
|
||||
var wasmPluginName = flag.String("wasmPluginName", "", "")
|
||||
|
||||
func TestHigressConformanceTests(t *testing.T) {
|
||||
flag.Parse()
|
||||
|
||||
@@ -49,57 +44,15 @@ func TestHigressConformanceTests(t *testing.T) {
|
||||
IngressClassName: *flags.IngressClassName,
|
||||
Debug: *flags.ShowDebug,
|
||||
CleanupBaseResources: *flags.CleanupBaseResources,
|
||||
GatewayAddress: "localhost",
|
||||
WASMOptions: suite.WASMOptions{
|
||||
IsWasmPluginTest: *flags.IsWasmPluginTest,
|
||||
WasmPluginName: *flags.WasmPluginName,
|
||||
WasmPluginType: *flags.WasmPluginType,
|
||||
},
|
||||
GatewayAddress: "localhost",
|
||||
EnableAllSupportedFeatures: true,
|
||||
})
|
||||
|
||||
cSuite.Setup(t)
|
||||
var higressTests []suite.ConformanceTest
|
||||
|
||||
if *isWasmPluginTest {
|
||||
if strings.Compare(*wasmPluginType, "CPP") == 0 {
|
||||
m := make(map[string]suite.ConformanceTest)
|
||||
m["request_block"] = tests.CPPWasmPluginsRequestBlock
|
||||
m["key_auth"] = tests.CPPWasmPluginsKeyAuth
|
||||
|
||||
higressTests = []suite.ConformanceTest{
|
||||
m[*wasmPluginName],
|
||||
}
|
||||
} else {
|
||||
higressTests = []suite.ConformanceTest{
|
||||
tests.WasmPluginsRequestBlock,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
higressTests = []suite.ConformanceTest{
|
||||
tests.HTTPRouteSimpleSameNamespace,
|
||||
tests.HTTPRouteHostNameSameNamespace,
|
||||
tests.HTTPRouteRewritePath,
|
||||
tests.HTTPRouteRewriteHost,
|
||||
tests.HTTPRouteCanaryHeader,
|
||||
tests.HTTPRouteEnableCors,
|
||||
tests.HTTPRouteEnableIgnoreCase,
|
||||
tests.HTTPRouteMatchMethods,
|
||||
tests.HTTPRouteMatchQueryParams,
|
||||
tests.HTTPRouteMatchHeaders,
|
||||
tests.HTTPRouteAppRoot,
|
||||
tests.HTTPRoutePermanentRedirect,
|
||||
tests.HTTPRoutePermanentRedirectCode,
|
||||
tests.HTTPRouteTemporalRedirect,
|
||||
tests.HTTPRouteSameHostAndPath,
|
||||
tests.HTTPRouteCanaryHeaderWithCustomizedHeader,
|
||||
tests.HTTPRouteWhitelistSourceRange,
|
||||
tests.HTTPRouteCanaryWeight,
|
||||
tests.HTTPRouteMatchPath,
|
||||
tests.HttpForceRedirectHttps,
|
||||
tests.HttpRedirectAsHttps,
|
||||
tests.HTTPRouteRequestHeaderControl,
|
||||
tests.HTTPRouteDownstreamEncryption,
|
||||
tests.HTTPRouteFullPathRegex,
|
||||
tests.HTTPRouteHttp2Rpc,
|
||||
tests.HTTPRouteConsulHttpBin,
|
||||
tests.HTTPRouteEurekaRegistry,
|
||||
}
|
||||
}
|
||||
|
||||
cSuite.Run(t, higressTests)
|
||||
cSuite.Run(t, tests.ConformanceTests)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
export VERSION
|
||||
|
||||
MODE="install"
|
||||
|
||||
HAS_CURL="$(type "curl" &> /dev/null && echo true || echo false)"
|
||||
HAS_WGET="$(type "wget" &> /dev/null && echo true || echo false)"
|
||||
HAS_DOCKER="$(type "docker" &> /dev/null && echo true || echo false)"
|
||||
@@ -24,6 +26,7 @@ parseArgs() {
|
||||
CONFIG_ARGS=()
|
||||
|
||||
DESTINATION=""
|
||||
MODE="install"
|
||||
|
||||
if [[ $1 != "-"* ]]; then
|
||||
DESTINATION="$1"
|
||||
@@ -36,6 +39,10 @@ parseArgs() {
|
||||
outputUsage
|
||||
exit 0
|
||||
;;
|
||||
-u|--update)
|
||||
MODE="update"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
CONFIG_ARGS+=("$1")
|
||||
shift
|
||||
@@ -48,15 +55,19 @@ parseArgs() {
|
||||
|
||||
validateArgs() {
|
||||
if [ -d "$DESTINATION" ]; then
|
||||
[ "$(ls -A "$DESTINATION")" ] && echo "The target folder \"$DESTINATION\" is not empty." && exit 1
|
||||
if [ "$(ls -A "$DESTINATION")" -a "$MODE" != "update" ]; then
|
||||
echo "The target folder \"$DESTINATION\" is not empty. Add \"-u\" to update an existed Higress instance." && exit 1
|
||||
fi
|
||||
if [ ! -w "$DESTINATION" ]; then
|
||||
echo "The target folder \"$DESTINATION\" is not writeable."
|
||||
exit 1
|
||||
echo "The target folder \"$DESTINATION\" is not writeable." && exit 1
|
||||
fi
|
||||
else
|
||||
if [ "$MODE" == "update" ]; then
|
||||
echo "The target folder \"$DESTINATION\" for update doesn't exist." && exit 1
|
||||
fi
|
||||
mkdir -p "$DESTINATION"
|
||||
if [ $? -ne 0 ]; then
|
||||
exit -1
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -67,11 +78,11 @@ validateArgs() {
|
||||
|
||||
outputUsage() {
|
||||
echo "Usage: $(basename -- "$0") [DIR] [OPTIONS...]"
|
||||
echo 'Install Higress (standalone version) into the DIR (the current directory by default).'
|
||||
echo 'Install Higress (standalone version) into the DIR ("./higress" by default).'
|
||||
echo '
|
||||
-c, --config-url=URL URL of the config storage
|
||||
Use Nacos with format: nacos://192.168.0.1:8848
|
||||
Use local files with format: file://opt/higress/conf
|
||||
Use local files with format: file:///opt/higress/conf
|
||||
--use-builtin-nacos use the built-in Nacos service instead of
|
||||
an external one
|
||||
--nacos-ns=NACOS-NAMESPACE
|
||||
@@ -104,6 +115,8 @@ outputUsage() {
|
||||
--console-port=CONSOLE-PORT
|
||||
the port used to visit Higress Console
|
||||
default to 8080 if unspecified
|
||||
-u, --update update an existed Higress instance.
|
||||
no user configuration will be changed during update.
|
||||
-h, --help give this help list'
|
||||
}
|
||||
|
||||
@@ -143,7 +156,7 @@ runAsRoot() {
|
||||
# verifySupported checks that the os/arch combination is supported for
|
||||
# binary builds, as well whether or not necessary tools are present.
|
||||
verifySupported() {
|
||||
local supported="darwin-amd64\nlinux-amd64\nwindows-amd64\n"
|
||||
local supported="darwin-amd64\nlinux-amd64\nwindows-amd64\ndarwin-arm64\nlinux-arm64\nwindows-arm64\n"
|
||||
if ! echo "${supported}" | grep -q "${OS}-${ARCH}"; then
|
||||
echo "${OS}-${ARCH} platform isn't supported at the moment."
|
||||
echo "Stay tuned for updates on https://github.com/alibaba/higress."
|
||||
@@ -191,18 +204,46 @@ download() {
|
||||
|
||||
# install installs the product.
|
||||
install() {
|
||||
tar -zx --exclude="docs" --exclude="src" -f "$HIGRESS_TMP_FILE" -C "$DESTINATION" --strip-components=1
|
||||
tar -zx --exclude="docs" --exclude="src" --exclude="test" -f "$HIGRESS_TMP_FILE" -C "$DESTINATION" --strip-components=1
|
||||
echo -n "$VERSION" > "$DESTINATION/VERSION"
|
||||
bash "$DESTINATION/bin/configure.sh" --auto-start ${CONFIG_ARGS[@]}
|
||||
}
|
||||
|
||||
# update updates the product.
|
||||
update() {
|
||||
CURRENT_VERSION="0.0.0"
|
||||
if [ -f "$DESTINATION/VERSION" ]; then
|
||||
CURRENT_VERSION="$(cat "$DESTINATION/VERSION")"
|
||||
fi
|
||||
if [ "$CURRENT_VERSION" == "$VERSION" ]; then
|
||||
echo "Higress is already up-to-date."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
BACKUP_FOLDER="$(cd ${DESTINATION}/.. ; pwd)"
|
||||
BACKUP_FILE="${BACKUP_FOLDER}/higress_backup_$(date '+%Y%m%d%H%M%S').tar.gz"
|
||||
tar -zc -f "$BACKUP_FILE" -C "$DESTINATION" .
|
||||
echo "The current version is packed here: $BACKUP_FILE"
|
||||
echo ""
|
||||
|
||||
download
|
||||
echo ""
|
||||
|
||||
tar -zx --exclude="docs" --exclude="src" --exclude="test" --exclude="compose/.env" -f "$HIGRESS_TMP_FILE" -C "$DESTINATION" --strip-components=1
|
||||
tar -zx -f "$HIGRESS_TMP_FILE" -C "$DESTINATION" --transform='s/env/env_new/g' --strip-components=1 "higress-standalone-${VERSION#v}/compose/.env"
|
||||
bash "$DESTINATION/bin/update.sh"
|
||||
echo -n "$VERSION" > "$DESTINATION/VERSION"
|
||||
return
|
||||
}
|
||||
|
||||
# fail_trap is executed if an error occurs.
|
||||
fail_trap() {
|
||||
result=$?
|
||||
if [ "$result" != "0" ]; then
|
||||
if [ -n "$INPUT_ARGUMENTS" ]; then
|
||||
echo "Failed to install Higress with the arguments provided: $INPUT_ARGUMENTS"
|
||||
echo "Failed to ${MODE} Higress with the arguments provided: $INPUT_ARGUMENTS"
|
||||
else
|
||||
echo "Failed to install Higress"
|
||||
echo "Failed to ${MODE} Higress"
|
||||
fi
|
||||
echo -e "\tFor support, go to https://github.com/alibaba/higress."
|
||||
fi
|
||||
@@ -228,6 +269,13 @@ initOS
|
||||
verifySupported
|
||||
|
||||
checkDesiredVersion
|
||||
download
|
||||
install
|
||||
case "$MODE" in
|
||||
update)
|
||||
update
|
||||
;;
|
||||
*)
|
||||
download
|
||||
install
|
||||
;;
|
||||
esac
|
||||
cleanup
|
||||
@@ -20,37 +20,35 @@ set -euo pipefail
|
||||
TYPE=${PLUGIN_TYPE-""}
|
||||
INNER_PLUGIN_NAME=${PLUGIN_NAME-""}
|
||||
|
||||
if [ $TYPE == "CPP" ]
|
||||
if [ "$TYPE" == "CPP" ]
|
||||
then
|
||||
cd ./plugins/wasm-cpp/
|
||||
if [ ! -n "$INNER_PLUGIN_NAME" ]; then
|
||||
echo "you must specify which cpp plugin you want to compile"
|
||||
echo "You must specify which cpp plugin you want to compile"
|
||||
else
|
||||
echo "build wasmplugin-cpp name of $INNER_PLUGIN_NAME"
|
||||
echo "🚀 Build CPP WasmPlugin: $INNER_PLUGIN_NAME"
|
||||
PLUGIN_NAME=${INNER_PLUGIN_NAME} make build
|
||||
fi
|
||||
else
|
||||
echo "not specify plugin language, so just compile wasm-go as default"
|
||||
echo "Not specify plugin language, so just compile wasm-go as default"
|
||||
cd ./plugins/wasm-go/
|
||||
if [ ! -n "$INNER_PLUGIN_NAME" ]; then
|
||||
EXTENSIONS_DIR=$(pwd)"/extensions/"
|
||||
echo "build all wasmplugins-go under folder of $EXTENSIONS_DIR"
|
||||
echo "🚀 Build all Go WasmPlugins under folder of $EXTENSIONS_DIR"
|
||||
for file in `ls $EXTENSIONS_DIR`
|
||||
do
|
||||
# TODO: adjust waf build
|
||||
if [ $file == "waf" ]; then
|
||||
if [ "$file" == "waf" ]; then
|
||||
continue
|
||||
fi
|
||||
if [ -d $EXTENSIONS_DIR$file ]; then
|
||||
name=${file##*/}
|
||||
echo "build wasmplugin name of $name"
|
||||
echo "🚀 Build Go WasmPlugin: $name"
|
||||
PLUGIN_NAME=${name} make build
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo "build wasmplugin-go name of $INNER_PLUGIN_NAME"
|
||||
echo "🚀 Build Go WasmPlugin: $INNER_PLUGIN_NAME"
|
||||
PLUGIN_NAME=${INNER_PLUGIN_NAME} make build
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user