mirror of
https://github.com/alibaba/higress.git
synced 2026-02-26 05:30:50 +08:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f32e159e5 | ||
|
|
7fd3f43c0d | ||
|
|
c96ede21a5 | ||
|
|
87366aab49 | ||
|
|
1a711bd267 | ||
|
|
d0d03e0e36 | ||
|
|
755bcc2d58 | ||
|
|
42fddb6115 | ||
|
|
09e563cf9c | ||
|
|
ef6912e466 | ||
|
|
07ce165661 | ||
|
|
3844017bb9 | ||
|
|
406b890a2a | ||
|
|
af31d455ed | ||
|
|
4a9e5aafd0 | ||
|
|
9dfabee26a | ||
|
|
19acbb4647 | ||
|
|
96ada21174 |
5
.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md
vendored
5
.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md
vendored
@@ -1,6 +1,9 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: Suggest an idea for Higress
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
@@ -13,4 +16,4 @@ A clear and concise description of what you want to happen. You can explain more
|
||||
|
||||
|
||||
## Other related information
|
||||
Add any other context or screenshots about the feature request here.
|
||||
Add any other context or screenshots about the feature request here.
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
---
|
||||
name: Non-{crash,security} bug
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**If you are reporting *any* crash or *any* potential security issue, *do not*
|
||||
open an issue in this repo. Please report the issue via [ASRC](https://security.alibaba.com/)(Alibaba Security Response Center) where the issue will be triaged appropriately.**
|
||||
|
||||
|
||||
---
|
||||
name: Bug Report
|
||||
about: If you would like to report an issue to Higress, please use this template.
|
||||
@@ -35,4 +48,4 @@ Just paste your stack trace here!
|
||||
|
||||
- Higress version:
|
||||
- OS :
|
||||
- Others:
|
||||
- Others:
|
||||
@@ -1,7 +1,7 @@
|
||||
/api @johnlanni
|
||||
/envoy @gengleilei @johnlanni @Lynskylate
|
||||
/istio @SpecialYang @johnlanni
|
||||
/pkg @SpecialYang @johnlanni
|
||||
/pkg @SpecialYang @johnlanni @Charlie17Li
|
||||
/plugins @johnlanni
|
||||
/registry @NameHaibinZhang @johnlanni
|
||||
/test @Xunzhuo
|
||||
|
||||
@@ -193,7 +193,7 @@ kube-load-image: $(tools/kind) ## Install the EG image to a kind cluster using t
|
||||
run-ingress-e2e-test:
|
||||
@echo -e "\n\033[36mRunning higress conformance tests...\033[0m"
|
||||
@echo -e "\n\033[36mWaiting higress-controller to be ready...\033[0m\n"
|
||||
kubectl wait --timeout=5m -n higress-system deployment/higress-controller --for=condition=Available
|
||||
kubectl wait --timeout=10m -n higress-system deployment/higress-controller --for=condition=Available
|
||||
@echo -e "\n\033[36mWaiting higress-gateway to be ready...\033[0m\n"
|
||||
kubectl wait --timeout=5m -n higress-system deployment/higress-gateway --for=condition=Available
|
||||
kubectl wait --timeout=10m -n higress-system deployment/higress-gateway --for=condition=Available
|
||||
go test -v -tags conformance ./test/ingress/e2e_test.go --ingress-class=higress --debug=true
|
||||
|
||||
160
README.md
160
README.md
@@ -26,7 +26,7 @@ Higress 是基于阿里内部两年多的 Envoy Gateway 实践沉淀,以开源
|
||||
|
||||
- [**使用场景**](#使用场景)
|
||||
- [**核心优势**](#核心优势)
|
||||
- [**Quick Start**](#quick-start)
|
||||
- [**Quick Start**](https://higress.io/zh-cn/docs/user/quickstart)
|
||||
- [**社区**](#社区)
|
||||
|
||||
## 使用场景
|
||||
@@ -73,164 +73,6 @@ Higress 是基于阿里内部两年多的 Envoy Gateway 实践沉淀,以开源
|
||||
|
||||
插件支持热更新,变更插件逻辑和配置都对流量无损。
|
||||
|
||||
## Quick Start
|
||||
|
||||
- [**本地环境**](#本地环境)
|
||||
- [**生产环境**](#生产环境)
|
||||
|
||||
### 本地环境
|
||||
|
||||
#### 第一步、 安装 kubectl & kind
|
||||
|
||||
**MacOS:**
|
||||
|
||||
```bash
|
||||
curl -Lo ./kubectl https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/darwin/amd64/kubectl
|
||||
# for Intel Macs
|
||||
[ $(uname -m) = x86_64 ]&& curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.17.0/kind-darwin-amd64
|
||||
# for M1 / ARM Macs
|
||||
[ $(uname -m) = arm64 ] && curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.17.0/kind-darwin-arm64
|
||||
chmod +x ./kind ./kubectl
|
||||
mv ./kind ./kubectl /some-dir-in-your-PATH/
|
||||
```
|
||||
|
||||
**Windows 中使用 PowerShell:**
|
||||
|
||||
```bash
|
||||
curl.exe -Lo kubectl.exe https://storage.googleapis.com/kubernetes-release/release/$(curl.exe -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/windows/amd64/kubectl.exe
|
||||
curl.exe -Lo kind-windows-amd64.exe https://kind.sigs.k8s.io/dl/v0.17.0/kind-windows-amd64
|
||||
Move-Item .\kind-windows-amd64.exe c:\some-dir-in-your-PATH\kind.exe
|
||||
Move-Item .\kubectl.exe c:\some-dir-in-your-PATH\kubectl.exe
|
||||
```
|
||||
|
||||
**Linux:**
|
||||
|
||||
```bash
|
||||
curl -Lo ./kubectl https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl
|
||||
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.17.0/kind-linux-amd64
|
||||
chmod +x ./kind ./kubectl
|
||||
sudo mv ./kind ./kubectl /usr/local/bin/kind
|
||||
```
|
||||
|
||||
#### 第二步、 创建并启用 kind
|
||||
|
||||
首先创建一个集群配置文件: `cluster.conf`
|
||||
|
||||
```yaml
|
||||
# cluster.conf
|
||||
kind: Cluster
|
||||
apiVersion: kind.x-k8s.io/v1alpha4
|
||||
nodes:
|
||||
- role: control-plane
|
||||
kubeadmConfigPatches:
|
||||
- |
|
||||
kind: InitConfiguration
|
||||
nodeRegistration:
|
||||
kubeletExtraArgs:
|
||||
node-labels: "ingress-ready=true"
|
||||
extraPortMappings:
|
||||
- containerPort: 80
|
||||
hostPort: 80
|
||||
protocol: TCP
|
||||
- containerPort: 443
|
||||
hostPort: 443
|
||||
protocol: TCP
|
||||
```
|
||||
|
||||
Mac & Linux 系统执行:
|
||||
|
||||
```bash
|
||||
kind create cluster --name higress --config=cluster.conf
|
||||
kubectl config use-context kind-higress
|
||||
```
|
||||
|
||||
Windows 系统执行:
|
||||
|
||||
```bash
|
||||
kind.exe create cluster --name higress --config=cluster.conf
|
||||
kubectl.exe config use-context kind-higress
|
||||
```
|
||||
|
||||
#### 第三步、 安装 higress
|
||||
|
||||
```bash
|
||||
helm repo add higress.io https://higress.io/helm-charts
|
||||
helm install higress higress.io/higress -n higress-system --set global.kind=true --create-namespace --render-subchart-notes
|
||||
```
|
||||
|
||||
注:helm版本需升级至**v3.8.0**及以上
|
||||
|
||||
#### 第四步、 创建 Ingress 资源并测试
|
||||
|
||||
```bash
|
||||
kubectl apply -f https://higress.io/samples/quickstart.yaml
|
||||
```
|
||||
|
||||
测试 Ingress 生效:
|
||||
|
||||
```bash
|
||||
# should output "foo"
|
||||
curl localhost/foo
|
||||
# should output "bar"
|
||||
curl localhost/bar
|
||||
```
|
||||
|
||||
#### 卸载资源
|
||||
|
||||
```bash
|
||||
kubectl delete -f https://higress.io/samples/quickstart.yaml
|
||||
|
||||
helm uninstall higress -n higress-system
|
||||
|
||||
kubectl delete ns higress-system
|
||||
```
|
||||
|
||||
### 生产环境
|
||||
|
||||
#### 第一步、 安装 higress
|
||||
|
||||
```bash
|
||||
helm repo add higress.io https://higress.io/helm-charts
|
||||
helm install higress higress.io/higress -n higress-system --create-namespace --render-subchart-notes
|
||||
```
|
||||
|
||||
#### 第二步、 创建 Ingress 资源并测试
|
||||
|
||||
假设在 default 命名空间下已经部署了一个 test service,服务端口为 80 ,则创建下面这个 K8s Ingress
|
||||
|
||||
```yaml
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: simple-example
|
||||
spec:
|
||||
ingressClassName: higress
|
||||
rules:
|
||||
- host: foo.bar.com
|
||||
http:
|
||||
paths:
|
||||
- path: /foo
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: test
|
||||
port:
|
||||
number: 80
|
||||
```
|
||||
|
||||
测试能访问到该服务:
|
||||
|
||||
```bash
|
||||
curl "$(k get svc -n higress-system higress-gateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"/foo -H 'host: foo.bar.com'
|
||||
```
|
||||
|
||||
#### 卸载资源
|
||||
|
||||
```bash
|
||||
helm uninstall higress -n higress-system
|
||||
|
||||
kubectl delete ns higress-system
|
||||
```
|
||||
|
||||
## 社区
|
||||
|
||||
|
||||
169
README_EN.md
169
README_EN.md
@@ -21,7 +21,7 @@ Powered by [Istio](https://github.com/istio/istio) and [Envoy](https://github.co
|
||||
|
||||
- [**Use Cases**](#use-cases)
|
||||
- [**Higress Features**](#higress-features)
|
||||
- [**Quick Start**](#quick-start)
|
||||
- [**Quick Start**](https://higress.io/en-us/docs/user/quickstart)
|
||||
- [**Thanks**](#thanks)
|
||||
|
||||
## Use Cases
|
||||
@@ -44,162 +44,25 @@ Powered by [Istio](https://github.com/istio/istio) and [Envoy](https://github.co
|
||||
|
||||
## Higress Features
|
||||
|
||||
(TODO)
|
||||
- **Easy to use**
|
||||
|
||||
Provide one-stop gateway solutions for traffic scheduling, service management, and security protection, support Console, K8s Ingress, and Gateway API configuration methods, and also support HTTP to Dubbo protocol conversion, and easily complete protocol mapping configuration.
|
||||
|
||||
## Quick Start
|
||||
- **Easy to expand**
|
||||
|
||||
- [**Local Environment**](#local-environment)
|
||||
- [**Production Environment**](#production-environment)
|
||||
Provides Wasm, Lua, and out-of-process plug-in extension mechanisms, so that multi-language plug-in writing is no longer an obstacle. The granularity of plug-in effectiveness supports not only the global level, domain name level, but also fine-grained routing level
|
||||
|
||||
- **Dynamic hot update**
|
||||
|
||||
Get rid of the traffic jitter caused by reload at the bottom, the configuration change takes effect in milliseconds and the business is not affected, the Wasm plug-in is hot updated and the traffic is not damaged
|
||||
|
||||
- **Smooth upgrade**
|
||||
|
||||
### Local Environment
|
||||
Compatible with 80%+ usage scenarios of Nginx Ingress Annotation, and provides more feature-rich annotations, easy to handle Nginx Ingress migration in one step
|
||||
|
||||
- **Security**
|
||||
|
||||
#### step 1. install kubectl & kind
|
||||
|
||||
**On MacOS:**
|
||||
|
||||
```bash
|
||||
curl -Lo ./kubectl https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/darwin/amd64/kubectl
|
||||
# for Intel Macs
|
||||
[ $(uname -m) = x86_64 ]&& curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.17.0/kind-darwin-amd64
|
||||
# for M1 / ARM Macs
|
||||
[ $(uname -m) = arm64 ] && curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.17.0/kind-darwin-arm64
|
||||
chmod +x ./kind ./kubectl
|
||||
mv ./kind ./kubectl /some-dir-in-your-PATH/
|
||||
```
|
||||
|
||||
**On Windows in PowerShell:**
|
||||
|
||||
```bash
|
||||
curl.exe -Lo kubectl.exe https://storage.googleapis.com/kubernetes-release/release/$(curl.exe -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/windows/amd64/kubectl.exe
|
||||
curl.exe -Lo kind-windows-amd64.exe https://kind.sigs.k8s.io/dl/v0.17.0/kind-windows-amd64
|
||||
Move-Item .\kind-windows-amd64.exe c:\some-dir-in-your-PATH\kind.exe
|
||||
Move-Item .\kubectl.exe c:\some-dir-in-your-PATH\kubectl.exe
|
||||
```
|
||||
|
||||
**On Linux:**
|
||||
|
||||
```bash
|
||||
curl -Lo ./kubectl https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl
|
||||
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.17.0/kind-linux-amd64
|
||||
chmod +x ./kind ./kubectl
|
||||
sudo mv ./kind ./kubectl /usr/local/bin/kind
|
||||
```
|
||||
|
||||
#### step 2. create kind cluster
|
||||
|
||||
create a cluster config file: `cluster.conf`
|
||||
|
||||
```yaml
|
||||
# cluster.conf
|
||||
kind: Cluster
|
||||
apiVersion: kind.x-k8s.io/v1alpha4
|
||||
nodes:
|
||||
- role: control-plane
|
||||
kubeadmConfigPatches:
|
||||
- |
|
||||
kind: InitConfiguration
|
||||
nodeRegistration:
|
||||
kubeletExtraArgs:
|
||||
node-labels: "ingress-ready=true"
|
||||
extraPortMappings:
|
||||
- containerPort: 80
|
||||
hostPort: 80
|
||||
protocol: TCP
|
||||
- containerPort: 443
|
||||
hostPort: 443
|
||||
protocol: TCP
|
||||
```
|
||||
|
||||
Mac & Linux:
|
||||
|
||||
```bash
|
||||
kind create cluster --name higress --config=cluster.conf
|
||||
kubectl config use-context kind-higress
|
||||
```
|
||||
|
||||
Windows:
|
||||
|
||||
```bash
|
||||
kind.exe create cluster --name higress --config=cluster.conf
|
||||
kubectl.exe config use-context kind-higress
|
||||
```
|
||||
|
||||
#### step 3. install higress
|
||||
|
||||
```bash
|
||||
helm repo add higress.io https://higress.io/helm-charts
|
||||
helm install higress higress.io/higress -n higress-system --create-namespace --set global.kind=true
|
||||
```
|
||||
Note: The helm version needs to be upgraded to **v3.8.0** and above
|
||||
#### step 4. create the ingress and test it
|
||||
|
||||
```bash
|
||||
kubectl apply -f https://higress.io/samples/quickstart.yaml
|
||||
```
|
||||
|
||||
Now verify that the ingress works
|
||||
|
||||
```bash
|
||||
# should output "foo"
|
||||
curl localhost/foo
|
||||
# should output "bar"
|
||||
curl localhost/bar
|
||||
```
|
||||
|
||||
#### Clean-Up
|
||||
|
||||
```bash
|
||||
kubectl delete -f https://higress.io/samples/quickstart.yaml
|
||||
|
||||
helm uninstall higress -n higress-system
|
||||
|
||||
kubectl delete ns higress-system
|
||||
```
|
||||
|
||||
### Production Environment
|
||||
|
||||
#### step 1. install higress
|
||||
|
||||
```bash
|
||||
helm repo add higress.io https://higress.io/helm-charts
|
||||
helm install higress higress.io/higress -n higress-system --create-namespace
|
||||
```
|
||||
|
||||
#### step 2. create the ingress and test it
|
||||
|
||||
for example there is a service `test` in default namespace.
|
||||
|
||||
```yaml
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: simple-example
|
||||
spec:
|
||||
ingressClassName: higress
|
||||
rules:
|
||||
- host: foo.bar.com
|
||||
http:
|
||||
paths:
|
||||
- path: /foo
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: test
|
||||
port:
|
||||
number: 80
|
||||
```
|
||||
|
||||
```bash
|
||||
curl "$(k get svc -n higress-system higress-gateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"/foo -H 'host: foo.bar.com'
|
||||
```
|
||||
|
||||
#### Clean-Up
|
||||
|
||||
```bash
|
||||
helm uninstall higress -n higress-system
|
||||
|
||||
kubectl delete ns higress-system
|
||||
```
|
||||
Provides JWT, OIDC, custom authentication and authentication, deeply integrates open source web application firewall.
|
||||
|
||||
### Thanks
|
||||
|
||||
|
||||
14
SECURITY.md
Normal file
14
SECURITY.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 1.x.x | :white_check_mark: |
|
||||
| < 1.0.0 | :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Please report any security issue or Higress crash report to [ASRC](https://security.alibaba.com/)(Alibaba Security Response Center) where the issue will be triaged appropriately.
|
||||
|
||||
Thank you in advance for helping to keep Higress secure.
|
||||
@@ -170,10 +170,12 @@ type WasmPlugin struct {
|
||||
// Extended by Higress, the default configuration takes effect globally
|
||||
DefaultConfig *types.Struct `protobuf:"bytes,101,opt,name=default_config,json=defaultConfig,proto3" json:"default_config,omitempty"`
|
||||
// Extended by Higress, matching rules take effect
|
||||
MatchRules []*MatchRule `protobuf:"bytes,102,rep,name=match_rules,json=matchRules,proto3" json:"match_rules,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
MatchRules []*MatchRule `protobuf:"bytes,102,rep,name=match_rules,json=matchRules,proto3" json:"match_rules,omitempty"`
|
||||
// diable the default config
|
||||
DefaultConfigDisable bool `protobuf:"varint,103,opt,name=default_config_disable,json=defaultConfigDisable,proto3" json:"default_config_disable,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *WasmPlugin) Reset() { *m = WasmPlugin{} }
|
||||
@@ -286,11 +288,19 @@ func (m *WasmPlugin) GetMatchRules() []*MatchRule {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *WasmPlugin) GetDefaultConfigDisable() bool {
|
||||
if m != nil {
|
||||
return m.DefaultConfigDisable
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Extended by Higress
|
||||
type MatchRule struct {
|
||||
Ingress []string `protobuf:"bytes,1,rep,name=ingress,proto3" json:"ingress,omitempty"`
|
||||
Domain []string `protobuf:"bytes,2,rep,name=domain,proto3" json:"domain,omitempty"`
|
||||
Config *types.Struct `protobuf:"bytes,3,opt,name=config,proto3" json:"config,omitempty"`
|
||||
ConfigDisable bool `protobuf:"varint,4,opt,name=config_disable,json=configDisable,proto3" json:"config_disable,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
@@ -350,6 +360,13 @@ func (m *MatchRule) GetConfig() *types.Struct {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MatchRule) GetConfigDisable() bool {
|
||||
if m != nil {
|
||||
return m.ConfigDisable
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterEnum("higress.extensions.v1alpha1.PluginPhase", PluginPhase_name, PluginPhase_value)
|
||||
proto.RegisterEnum("higress.extensions.v1alpha1.PullPolicy", PullPolicy_name, PullPolicy_value)
|
||||
@@ -360,43 +377,46 @@ func init() {
|
||||
func init() { proto.RegisterFile("extensions/v1alpha1/wasm.proto", fileDescriptor_4d60b240916c4e18) }
|
||||
|
||||
var fileDescriptor_4d60b240916c4e18 = []byte{
|
||||
// 576 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x93, 0xdd, 0x6e, 0xd3, 0x30,
|
||||
0x14, 0xc7, 0x97, 0x76, 0xeb, 0xd6, 0xd3, 0x6d, 0x64, 0x96, 0x18, 0xd6, 0x86, 0x4a, 0xb5, 0x0b,
|
||||
0x28, 0xbb, 0x48, 0xb4, 0x02, 0xe3, 0x06, 0x4d, 0x74, 0xa3, 0xb0, 0x0a, 0x28, 0x51, 0xb2, 0x81,
|
||||
0xd8, 0x4d, 0xe5, 0x66, 0x6e, 0x6a, 0xe1, 0xc4, 0x91, 0xed, 0x6c, 0xf4, 0xf9, 0xb8, 0xe1, 0x92,
|
||||
0x47, 0x40, 0x7b, 0x12, 0x54, 0x27, 0xd9, 0x07, 0xa0, 0xde, 0x9d, 0x8f, 0xdf, 0x39, 0xf9, 0xff,
|
||||
0x8f, 0x1c, 0x68, 0xd2, 0xef, 0x9a, 0x26, 0x8a, 0x89, 0x44, 0xb9, 0x17, 0x7b, 0x84, 0xa7, 0x13,
|
||||
0xb2, 0xe7, 0x5e, 0x12, 0x15, 0x3b, 0xa9, 0x14, 0x5a, 0xa0, 0xed, 0x09, 0x8b, 0x24, 0x55, 0xca,
|
||||
0xb9, 0xe1, 0x9c, 0x92, 0xdb, 0x6a, 0x46, 0x42, 0x44, 0x9c, 0xba, 0x06, 0x1d, 0x65, 0x63, 0xf7,
|
||||
0x52, 0x92, 0x34, 0xa5, 0x52, 0xe5, 0xc3, 0x5b, 0x0f, 0xff, 0xee, 0x2b, 0x2d, 0xb3, 0x50, 0xe7,
|
||||
0xdd, 0x9d, 0x1f, 0x8b, 0x00, 0x5f, 0x88, 0x8a, 0x3d, 0x9e, 0x45, 0x2c, 0x41, 0x36, 0x54, 0x33,
|
||||
0xc9, 0x71, 0xa5, 0x65, 0xb5, 0xeb, 0xfe, 0x2c, 0x44, 0x9b, 0x50, 0x53, 0x13, 0xd2, 0x79, 0xb1,
|
||||
0x8f, 0xab, 0xa6, 0x58, 0x64, 0x28, 0x80, 0x0d, 0x16, 0x93, 0x88, 0x0e, 0xd3, 0x8c, 0xf3, 0x61,
|
||||
0x2a, 0x38, 0x0b, 0xa7, 0x78, 0xb1, 0x65, 0xb5, 0xd7, 0x3b, 0x4f, 0x9c, 0x39, 0x7a, 0x1d, 0x2f,
|
||||
0xe3, 0xdc, 0x33, 0xb8, 0x7f, 0xcf, 0x6c, 0xb8, 0x29, 0xa0, 0xdd, 0x3b, 0x4b, 0x15, 0x0d, 0x25,
|
||||
0xd5, 0x78, 0xc9, 0x7c, 0xf7, 0x86, 0x0d, 0x4c, 0x19, 0x3d, 0x05, 0xfb, 0x82, 0x4a, 0x36, 0x66,
|
||||
0x21, 0xd1, 0x4c, 0x24, 0xc3, 0x6f, 0x74, 0x8a, 0x6b, 0x39, 0x7a, 0xbb, 0xfe, 0x9e, 0x4e, 0xd1,
|
||||
0x2b, 0x58, 0x4b, 0x8d, 0xbf, 0x61, 0x28, 0x92, 0x31, 0x8b, 0xf0, 0x72, 0xcb, 0x6a, 0x37, 0x3a,
|
||||
0x0f, 0x9c, 0xfc, 0x34, 0x4e, 0x79, 0x1a, 0x27, 0x30, 0xa7, 0xf1, 0x57, 0x73, 0xfa, 0xc8, 0xc0,
|
||||
0xe8, 0x11, 0x34, 0x8a, 0xe9, 0x84, 0xc4, 0x14, 0xaf, 0x98, 0x6f, 0x40, 0x5e, 0x1a, 0x90, 0x98,
|
||||
0xa2, 0x03, 0x58, 0x4a, 0x27, 0x44, 0x51, 0x5c, 0x37, 0xf6, 0xdb, 0xf3, 0xed, 0x9b, 0x39, 0x6f,
|
||||
0xc6, 0xfb, 0xf9, 0x18, 0x7a, 0x09, 0x2b, 0xa9, 0x64, 0x42, 0x32, 0x3d, 0xc5, 0x60, 0x94, 0x6d,
|
||||
0xff, 0xa3, 0xac, 0x9f, 0xe8, 0xfd, 0xe7, 0x9f, 0x09, 0xcf, 0xa8, 0x7f, 0x0d, 0xa3, 0x03, 0x58,
|
||||
0x3f, 0xa7, 0x63, 0x92, 0x71, 0x5d, 0x1a, 0xa3, 0xf3, 0x8d, 0xad, 0x15, 0x78, 0xe1, 0xec, 0x1d,
|
||||
0x34, 0x62, 0xa2, 0xc3, 0xc9, 0x50, 0x66, 0x9c, 0x2a, 0x3c, 0x6e, 0x55, 0xdb, 0x8d, 0xce, 0xe3,
|
||||
0xb9, 0xf2, 0x3f, 0xce, 0x78, 0x3f, 0xe3, 0xd4, 0x87, 0xb8, 0x0c, 0xd5, 0x4e, 0x02, 0xf5, 0xeb,
|
||||
0x06, 0xc2, 0xb0, 0xcc, 0x12, 0xb3, 0x01, 0x5b, 0xad, 0x6a, 0xbb, 0xee, 0x97, 0xe9, 0xec, 0x2d,
|
||||
0x9d, 0x8b, 0x98, 0xb0, 0x04, 0x57, 0x4c, 0xa3, 0xc8, 0x90, 0x0b, 0xb5, 0x42, 0x7f, 0x75, 0xbe,
|
||||
0xfe, 0x02, 0xdb, 0xed, 0x41, 0xe3, 0xd6, 0x1d, 0xd1, 0x7d, 0xd8, 0x38, 0x1d, 0x04, 0x5e, 0xef,
|
||||
0xa8, 0xff, 0xb6, 0xdf, 0x7b, 0x33, 0xf4, 0x8e, 0xbb, 0x41, 0xcf, 0x5e, 0x40, 0x75, 0x58, 0xea,
|
||||
0x9e, 0x9e, 0x1c, 0x0f, 0x6c, 0xab, 0x0c, 0xcf, 0xec, 0xca, 0x2c, 0x0c, 0x4e, 0xba, 0x27, 0x81,
|
||||
0x5d, 0xdd, 0x3d, 0x04, 0xb8, 0xf5, 0xf8, 0x36, 0x01, 0xdd, 0xd9, 0xf2, 0xe9, 0x43, 0xff, 0xe8,
|
||||
0xab, 0xbd, 0x80, 0x6c, 0x58, 0xed, 0x8f, 0x07, 0x42, 0x7b, 0x92, 0x2a, 0x9a, 0x68, 0xdb, 0x42,
|
||||
0x00, 0xb5, 0x2e, 0xbf, 0x24, 0x53, 0x65, 0x57, 0x0e, 0x5f, 0xff, 0xbc, 0x6a, 0x5a, 0xbf, 0xae,
|
||||
0x9a, 0xd6, 0xef, 0xab, 0xa6, 0x75, 0xd6, 0x89, 0x98, 0x9e, 0x64, 0x23, 0x27, 0x14, 0xb1, 0x4b,
|
||||
0x38, 0x1b, 0x91, 0x11, 0x71, 0x8b, 0x73, 0xba, 0x24, 0x65, 0xee, 0x7f, 0x7e, 0xf4, 0x51, 0xcd,
|
||||
0xb8, 0x7c, 0xf6, 0x27, 0x00, 0x00, 0xff, 0xff, 0xb2, 0xe0, 0x3d, 0x06, 0x06, 0x04, 0x00, 0x00,
|
||||
// 617 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x94, 0xdf, 0x4e, 0x13, 0x41,
|
||||
0x14, 0xc6, 0xd9, 0x16, 0x0a, 0x3d, 0x05, 0x5c, 0x26, 0x8a, 0x13, 0x30, 0xb5, 0x21, 0x51, 0x57,
|
||||
0x2e, 0x76, 0x43, 0x45, 0xbc, 0x31, 0xc4, 0x02, 0x55, 0x1a, 0xb5, 0x6e, 0x76, 0x41, 0x23, 0x37,
|
||||
0x9b, 0xe9, 0x32, 0xdd, 0x4e, 0x9c, 0xfd, 0x93, 0x9d, 0x59, 0xb0, 0x0f, 0xe2, 0x3b, 0x79, 0xe9,
|
||||
0x23, 0x18, 0xde, 0xc2, 0x3b, 0xd3, 0xd9, 0x2d, 0x6d, 0xd1, 0xf4, 0x6e, 0xe6, 0x9c, 0xdf, 0x39,
|
||||
0xe7, 0xfb, 0xce, 0x4e, 0x16, 0xea, 0xf4, 0xbb, 0xa4, 0x91, 0x60, 0x71, 0x24, 0xac, 0xab, 0x3d,
|
||||
0xc2, 0x93, 0x01, 0xd9, 0xb3, 0xae, 0x89, 0x08, 0xcd, 0x24, 0x8d, 0x65, 0x8c, 0xb6, 0x07, 0x2c,
|
||||
0x48, 0xa9, 0x10, 0xe6, 0x84, 0x33, 0xc7, 0xdc, 0x56, 0x3d, 0x88, 0xe3, 0x80, 0x53, 0x4b, 0xa1,
|
||||
0xbd, 0xac, 0x6f, 0x5d, 0xa7, 0x24, 0x49, 0x68, 0x2a, 0xf2, 0xe2, 0xad, 0x47, 0x77, 0xf3, 0x42,
|
||||
0xa6, 0x99, 0x2f, 0xf3, 0xec, 0xce, 0x9f, 0x45, 0x80, 0x2f, 0x44, 0x84, 0x36, 0xcf, 0x02, 0x16,
|
||||
0x21, 0x1d, 0xca, 0x59, 0xca, 0x71, 0xa9, 0xa1, 0x19, 0x55, 0x67, 0x74, 0x44, 0x9b, 0x50, 0x11,
|
||||
0x03, 0xd2, 0x7c, 0x79, 0x80, 0xcb, 0x2a, 0x58, 0xdc, 0x90, 0x0b, 0x1b, 0x2c, 0x24, 0x01, 0xf5,
|
||||
0x92, 0x8c, 0x73, 0x2f, 0x89, 0x39, 0xf3, 0x87, 0x78, 0xb1, 0xa1, 0x19, 0xeb, 0xcd, 0x67, 0xe6,
|
||||
0x1c, 0xbd, 0xa6, 0x9d, 0x71, 0x6e, 0x2b, 0xdc, 0xb9, 0xa7, 0x3a, 0x4c, 0x02, 0x68, 0x77, 0xa6,
|
||||
0xa9, 0xa0, 0x7e, 0x4a, 0x25, 0x5e, 0x52, 0x73, 0x27, 0xac, 0xab, 0xc2, 0xe8, 0x39, 0xe8, 0x57,
|
||||
0x34, 0x65, 0x7d, 0xe6, 0x13, 0xc9, 0xe2, 0xc8, 0xfb, 0x46, 0x87, 0xb8, 0x92, 0xa3, 0xd3, 0xf1,
|
||||
0xf7, 0x74, 0x88, 0x5e, 0xc3, 0x5a, 0xa2, 0xfc, 0x79, 0x7e, 0x1c, 0xf5, 0x59, 0x80, 0x97, 0x1b,
|
||||
0x9a, 0x51, 0x6b, 0x3e, 0x34, 0xf3, 0xd5, 0x98, 0xe3, 0xd5, 0x98, 0xae, 0x5a, 0x8d, 0xb3, 0x9a,
|
||||
0xd3, 0xc7, 0x0a, 0x46, 0x8f, 0xa1, 0x56, 0x54, 0x47, 0x24, 0xa4, 0x78, 0x45, 0xcd, 0x80, 0x3c,
|
||||
0xd4, 0x25, 0x21, 0x45, 0x87, 0xb0, 0x94, 0x0c, 0x88, 0xa0, 0xb8, 0xaa, 0xec, 0x1b, 0xf3, 0xed,
|
||||
0xab, 0x3a, 0x7b, 0xc4, 0x3b, 0x79, 0x19, 0x7a, 0x05, 0x2b, 0x49, 0xca, 0xe2, 0x94, 0xc9, 0x21,
|
||||
0x06, 0xa5, 0x6c, 0xfb, 0x1f, 0x65, 0x9d, 0x48, 0x1e, 0xec, 0x7f, 0x26, 0x3c, 0xa3, 0xce, 0x2d,
|
||||
0x8c, 0x0e, 0x61, 0xfd, 0x92, 0xf6, 0x49, 0xc6, 0xe5, 0xd8, 0x18, 0x9d, 0x6f, 0x6c, 0xad, 0xc0,
|
||||
0x0b, 0x67, 0xef, 0xa0, 0x16, 0x12, 0xe9, 0x0f, 0xbc, 0x34, 0xe3, 0x54, 0xe0, 0x7e, 0xa3, 0x6c,
|
||||
0xd4, 0x9a, 0x4f, 0xe7, 0xca, 0xff, 0x38, 0xe2, 0x9d, 0x8c, 0x53, 0x07, 0xc2, 0xf1, 0x51, 0xa0,
|
||||
0x7d, 0xd8, 0x9c, 0x15, 0xe2, 0x5d, 0x32, 0x41, 0x7a, 0x9c, 0xe2, 0xa0, 0xa1, 0x19, 0x2b, 0xce,
|
||||
0xfd, 0x99, 0xb9, 0x27, 0x79, 0x6e, 0xe7, 0x87, 0x06, 0xd5, 0xdb, 0x7e, 0x08, 0xc3, 0x32, 0x8b,
|
||||
0xd4, 0x60, 0xac, 0x35, 0xca, 0x46, 0xd5, 0x19, 0x5f, 0x47, 0x4f, 0xf0, 0x32, 0x0e, 0x09, 0x8b,
|
||||
0x70, 0x49, 0x25, 0x8a, 0x1b, 0xb2, 0xa0, 0x52, 0xd8, 0x2e, 0xcf, 0xb7, 0x5d, 0x60, 0xe8, 0x09,
|
||||
0xac, 0xdf, 0x91, 0xb7, 0xa8, 0xe4, 0xad, 0xf9, 0xd3, 0xba, 0x76, 0xdb, 0x50, 0x9b, 0xfa, 0x4a,
|
||||
0xe8, 0x01, 0x6c, 0x9c, 0x77, 0x5d, 0xbb, 0x7d, 0xdc, 0x79, 0xdb, 0x69, 0x9f, 0x78, 0xf6, 0x69,
|
||||
0xcb, 0x6d, 0xeb, 0x0b, 0xa8, 0x0a, 0x4b, 0xad, 0xf3, 0xb3, 0xd3, 0xae, 0xae, 0x8d, 0x8f, 0x17,
|
||||
0x7a, 0x69, 0x74, 0x74, 0xcf, 0x5a, 0x67, 0xae, 0x5e, 0xde, 0x3d, 0x02, 0x98, 0x7a, 0xda, 0x9b,
|
||||
0x80, 0x66, 0xba, 0x7c, 0xfa, 0xd0, 0x39, 0xfe, 0xaa, 0x2f, 0x20, 0x1d, 0x56, 0x3b, 0xfd, 0x6e,
|
||||
0x2c, 0xed, 0x94, 0x0a, 0x1a, 0x49, 0x5d, 0x43, 0x00, 0x95, 0x16, 0xbf, 0x26, 0x43, 0xa1, 0x97,
|
||||
0x8e, 0xde, 0xfc, 0xbc, 0xa9, 0x6b, 0xbf, 0x6e, 0xea, 0xda, 0xef, 0x9b, 0xba, 0x76, 0xd1, 0x0c,
|
||||
0x98, 0x1c, 0x64, 0x3d, 0xd3, 0x8f, 0x43, 0x8b, 0x70, 0xd6, 0x23, 0x3d, 0x62, 0x15, 0x1f, 0xcb,
|
||||
0x22, 0x09, 0xb3, 0xfe, 0xf3, 0x1b, 0xe9, 0x55, 0xd4, 0x32, 0x5e, 0xfc, 0x0d, 0x00, 0x00, 0xff,
|
||||
0xff, 0x48, 0x74, 0xbe, 0xc1, 0x64, 0x04, 0x00, 0x00,
|
||||
}
|
||||
|
||||
func (m *WasmPlugin) Marshal() (dAtA []byte, err error) {
|
||||
@@ -423,6 +443,18 @@ func (m *WasmPlugin) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i -= len(m.XXX_unrecognized)
|
||||
copy(dAtA[i:], m.XXX_unrecognized)
|
||||
}
|
||||
if m.DefaultConfigDisable {
|
||||
i--
|
||||
if m.DefaultConfigDisable {
|
||||
dAtA[i] = 1
|
||||
} else {
|
||||
dAtA[i] = 0
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0x6
|
||||
i--
|
||||
dAtA[i] = 0xb8
|
||||
}
|
||||
if len(m.MatchRules) > 0 {
|
||||
for iNdEx := len(m.MatchRules) - 1; iNdEx >= 0; iNdEx-- {
|
||||
{
|
||||
@@ -549,6 +581,16 @@ func (m *MatchRule) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i -= len(m.XXX_unrecognized)
|
||||
copy(dAtA[i:], m.XXX_unrecognized)
|
||||
}
|
||||
if m.ConfigDisable {
|
||||
i--
|
||||
if m.ConfigDisable {
|
||||
dAtA[i] = 1
|
||||
} else {
|
||||
dAtA[i] = 0
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0x20
|
||||
}
|
||||
if m.Config != nil {
|
||||
{
|
||||
size, err := m.Config.MarshalToSizedBuffer(dAtA[:i])
|
||||
@@ -643,6 +685,9 @@ func (m *WasmPlugin) Size() (n int) {
|
||||
n += 2 + l + sovWasm(uint64(l))
|
||||
}
|
||||
}
|
||||
if m.DefaultConfigDisable {
|
||||
n += 3
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
n += len(m.XXX_unrecognized)
|
||||
}
|
||||
@@ -671,6 +716,9 @@ func (m *MatchRule) Size() (n int) {
|
||||
l = m.Config.Size()
|
||||
n += 1 + l + sovWasm(uint64(l))
|
||||
}
|
||||
if m.ConfigDisable {
|
||||
n += 2
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
n += len(m.XXX_unrecognized)
|
||||
}
|
||||
@@ -1052,6 +1100,26 @@ func (m *WasmPlugin) Unmarshal(dAtA []byte) error {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
case 103:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field DefaultConfigDisable", wireType)
|
||||
}
|
||||
var v int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowWasm
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
v |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
m.DefaultConfigDisable = bool(v != 0)
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipWasm(dAtA[iNdEx:])
|
||||
@@ -1203,6 +1271,26 @@ func (m *MatchRule) Unmarshal(dAtA []byte) error {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
case 4:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field ConfigDisable", wireType)
|
||||
}
|
||||
var v int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowWasm
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
v |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
m.ConfigDisable = bool(v != 0)
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipWasm(dAtA[iNdEx:])
|
||||
|
||||
@@ -104,6 +104,8 @@ message WasmPlugin {
|
||||
google.protobuf.Struct default_config = 101;
|
||||
// Extended by Higress, matching rules take effect
|
||||
repeated MatchRule match_rules = 102;
|
||||
// diable the default config
|
||||
bool default_config_disable = 103;
|
||||
}
|
||||
|
||||
// Extended by Higress
|
||||
@@ -111,6 +113,7 @@ message MatchRule {
|
||||
repeated string ingress = 1;
|
||||
repeated string domain = 2;
|
||||
google.protobuf.Struct config = 3;
|
||||
bool config_disable = 4;
|
||||
}
|
||||
|
||||
// The phase in the filter chain where the plugin will be injected.
|
||||
|
||||
@@ -35,6 +35,8 @@ spec:
|
||||
defaultConfig:
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
defaultConfigDisable:
|
||||
type: boolean
|
||||
imagePullPolicy:
|
||||
description: The pull behaviour to be applied when fetching an OCI
|
||||
image.
|
||||
@@ -52,6 +54,8 @@ spec:
|
||||
config:
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
configDisable:
|
||||
type: boolean
|
||||
domain:
|
||||
items:
|
||||
type: string
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
apiVersion: v2
|
||||
appVersion: 0.7.0
|
||||
appVersion: 0.7.1
|
||||
description: Helm chart for deploying higress gateways
|
||||
icon: https://higress.io/img/higress_logo_small.png
|
||||
keywords:
|
||||
@@ -9,4 +9,4 @@ name: higress-core
|
||||
sources:
|
||||
- http://github.com/alibaba/higress
|
||||
type: application
|
||||
version: 0.7.0
|
||||
version: 0.7.1
|
||||
|
||||
@@ -35,6 +35,8 @@ spec:
|
||||
defaultConfig:
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
defaultConfigDisable:
|
||||
type: boolean
|
||||
imagePullPolicy:
|
||||
description: The pull behaviour to be applied when fetching an OCI
|
||||
image.
|
||||
@@ -52,6 +54,8 @@ spec:
|
||||
config:
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
configDisable:
|
||||
type: boolean
|
||||
domain:
|
||||
items:
|
||||
type: string
|
||||
|
||||
@@ -86,4 +86,12 @@ higress: {{ include "controller.name" . }}
|
||||
{{- else }}
|
||||
{{- .Values.controller.serviceAccount.name | default "default" }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{- define "controller.jwtPolicy" -}}
|
||||
{{- if semverCompare ">=1.21-0" .Capabilities.KubeVersion.GitVersion }}
|
||||
{{- .Values.global.jwtPolicy | default "third-party-jwt" }}
|
||||
{{- else }}
|
||||
{{- print "first-party-jwt" }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
trustDomain: "cluster.local"
|
||||
accessLogEncoding: TEXT
|
||||
accessLogFile: "/dev/stdout"
|
||||
ingressControllerMode: "OFF"
|
||||
accessLogFormat: '{"authority":"%REQ(:AUTHORITY)%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","duration":"%DURATION%","istio_policy_status":"%DYNAMIC_METADATA(istio.mixer:status)%","method":"%REQ(:METHOD)%","path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","request_id":"%REQ(X-REQUEST-ID)%","requested_server_name":"%REQUESTED_SERVER_NAME%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","route_name":"%ROUTE_NAME%","start_time":"%START_TIME%","trace_id":"%REQ(X-B3-TRACEID)%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_host":"%UPSTREAM_HOST%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","upstream_service_time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","user_agent":"%REQ(USER-AGENT)%","x_forwarded_for":"%REQ(X-FORWARDED-FOR)%"}
|
||||
|
||||
'
|
||||
|
||||
@@ -80,7 +80,7 @@ spec:
|
||||
- name: REVISION
|
||||
value: "{{ .Values.revision | default `default` }}"
|
||||
- name: JWT_POLICY
|
||||
value: {{ .Values.global.jwtPolicy }}
|
||||
value: {{ include "controller.jwtPolicy" . }}
|
||||
- name: PILOT_CERT_PROVIDER
|
||||
value: "istiod"
|
||||
- name: POD_NAME
|
||||
@@ -147,7 +147,7 @@ spec:
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /etc/istio/config
|
||||
{{- if eq .Values.global.jwtPolicy "third-party-jwt" }}
|
||||
{{- if eq (include "controller.jwtPolicy" .) "third-party-jwt" }}
|
||||
- name: istio-token
|
||||
mountPath: /var/run/secrets/tokens
|
||||
readOnly: true
|
||||
@@ -241,7 +241,7 @@ spec:
|
||||
- emptyDir:
|
||||
medium: Memory
|
||||
name: local-certs
|
||||
{{- if eq .Values.global.jwtPolicy "third-party-jwt" }}
|
||||
{{- if eq (include "controller.jwtPolicy" .) "third-party-jwt" }}
|
||||
- name: istio-token
|
||||
projected:
|
||||
sources:
|
||||
@@ -249,6 +249,7 @@ spec:
|
||||
audience: {{ .Values.global.sds.token.aud }}
|
||||
expirationSeconds: 43200
|
||||
path: istio-token
|
||||
{{- end }}
|
||||
# Optional: user-generated root
|
||||
- name: cacerts
|
||||
secret:
|
||||
@@ -264,4 +265,3 @@ spec:
|
||||
name: pilot-jwks-extra-cacerts{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -129,7 +129,7 @@ spec:
|
||||
- name: ENABLE_INGRESS_GATEWAY_SDS
|
||||
value: "false"
|
||||
- name: JWT_POLICY
|
||||
value: {{ .Values.global.jwtPolicy }}
|
||||
value: {{ include "controller.jwtPolicy" . }}
|
||||
- name: ISTIO_META_HTTP10
|
||||
value: "1"
|
||||
- name: ISTIO_META_CLUSTER_ID
|
||||
@@ -177,7 +177,7 @@ spec:
|
||||
{{- toYaml .Values.gateway.resources | nindent 12 }}
|
||||
{{- end }}
|
||||
volumeMounts:
|
||||
{{- if eq .Values.global.jwtPolicy "third-party-jwt" }}
|
||||
{{- if eq (include "controller.jwtPolicy" .) "third-party-jwt" }}
|
||||
- name: istio-token
|
||||
mountPath: /var/run/secrets/tokens
|
||||
readOnly: true
|
||||
@@ -213,7 +213,7 @@ spec:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
volumes:
|
||||
{{- if eq .Values.global.jwtPolicy "third-party-jwt" }}
|
||||
{{- if eq (include "controller.jwtPolicy" .) "third-party-jwt" }}
|
||||
- name: istio-token
|
||||
projected:
|
||||
sources:
|
||||
|
||||
@@ -455,7 +455,7 @@ controller:
|
||||
name: "higress-controller"
|
||||
replicas: 1
|
||||
image: higress
|
||||
tag: "0.7.0"
|
||||
tag: "0.7.1"
|
||||
env: {}
|
||||
|
||||
labels: {}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
dependencies:
|
||||
- name: higress-core
|
||||
repository: file://../core
|
||||
version: 0.7.0
|
||||
version: 0.7.1
|
||||
- name: higress-console
|
||||
repository: https://higress.io/helm-charts/
|
||||
version: 0.1.0
|
||||
digest: sha256:3fd6cfb0fd10178927569d57e0cbff5250870dd677cbf30995c49ced83e38f7c
|
||||
generated: "2023-03-15T15:09:02.878072+08:00"
|
||||
version: 0.1.1
|
||||
digest: sha256:051fbd7b2916d1d0c26839d0e27653f6e42d20e9294bd9eed9628f24c5a7b228
|
||||
generated: "2023-04-03T13:42:23.705379+08:00"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
apiVersion: v2
|
||||
appVersion: 0.7.0
|
||||
appVersion: 0.7.1
|
||||
description: Helm chart for deploying higress gateways
|
||||
icon: https://higress.io/img/higress_logo_small.png
|
||||
keywords:
|
||||
@@ -11,9 +11,9 @@ sources:
|
||||
dependencies:
|
||||
- name: higress-core
|
||||
repository: "file://../core"
|
||||
version: 0.7.0
|
||||
version: 0.7.1
|
||||
- name: higress-console
|
||||
repository: "https://higress.io/helm-charts/"
|
||||
version: 0.1.0
|
||||
version: 0.1.1
|
||||
type: application
|
||||
version: 0.7.0
|
||||
version: 0.7.1
|
||||
|
||||
20
istio/1.12/patches/istio/20230319-imagefetcher-oci.patch
Normal file
20
istio/1.12/patches/istio/20230319-imagefetcher-oci.patch
Normal file
@@ -0,0 +1,20 @@
|
||||
diff --color -Naur external/istio/pkg/wasm/imagefetcher.go external/istio_new/pkg/wasm/imagefetcher.go
|
||||
--- istio/pkg/wasm/imagefetcher.go 2023-03-19 17:56:55.238354950 +0800
|
||||
+++ istio_new/pkg/wasm/imagefetcher.go 2023-03-19 17:56:40.630410241 +0800
|
||||
@@ -176,12 +176,12 @@
|
||||
return nil, fmt.Errorf("could not fetch layers: %v", err)
|
||||
}
|
||||
|
||||
- // The image must be single-layered.
|
||||
- if len(layers) != 1 {
|
||||
- return nil, fmt.Errorf("number of layers must be 1 but got %d", len(layers))
|
||||
+ // The image must have at least one layer.
|
||||
+ if len(layers) == 0 {
|
||||
+ return nil, fmt.Errorf("number of layers must be greater than zero")
|
||||
}
|
||||
|
||||
- layer := layers[0]
|
||||
+ layer := layers[len(layers)-1]
|
||||
mt, err := layer.MediaType()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get media type: %v", err)
|
||||
@@ -707,7 +707,10 @@ func (m *IngressConfig) convertIstioWasmPlugin(obj *higressext.WasmPlugin) (*ext
|
||||
if result.PluginConfig != nil {
|
||||
return result, nil
|
||||
}
|
||||
result.PluginConfig = obj.DefaultConfig
|
||||
if !obj.DefaultConfigDisable {
|
||||
result.PluginConfig = obj.DefaultConfig
|
||||
}
|
||||
hasValidRule := false
|
||||
if len(obj.MatchRules) > 0 {
|
||||
if result.PluginConfig == nil {
|
||||
result.PluginConfig = &types.Struct{
|
||||
@@ -716,6 +719,9 @@ func (m *IngressConfig) convertIstioWasmPlugin(obj *higressext.WasmPlugin) (*ext
|
||||
}
|
||||
var ruleValues []*types.Value
|
||||
for _, rule := range obj.MatchRules {
|
||||
if rule.ConfigDisable {
|
||||
continue
|
||||
}
|
||||
if rule.Config == nil {
|
||||
return nil, errors.New("invalid rule has no config")
|
||||
}
|
||||
@@ -764,14 +770,20 @@ func (m *IngressConfig) convertIstioWasmPlugin(obj *higressext.WasmPlugin) (*ext
|
||||
Kind: v,
|
||||
})
|
||||
}
|
||||
result.PluginConfig.Fields["_rules_"] = &types.Value{
|
||||
Kind: &types.Value_ListValue{
|
||||
ListValue: &types.ListValue{
|
||||
Values: ruleValues,
|
||||
if len(ruleValues) > 0 {
|
||||
hasValidRule = true
|
||||
result.PluginConfig.Fields["_rules_"] = &types.Value{
|
||||
Kind: &types.Value_ListValue{
|
||||
ListValue: &types.ListValue{
|
||||
Values: ruleValues,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
if !hasValidRule && obj.DefaultConfigDisable {
|
||||
return nil, nil
|
||||
}
|
||||
return result, nil
|
||||
|
||||
}
|
||||
@@ -802,6 +814,14 @@ func (m *IngressConfig) AddOrUpdateWasmPlugin(clusterNamespacedName util.Cluster
|
||||
IngressLog.Errorf("invalid wasmPlugin:%s, err:%v", clusterNamespacedName.Name, err)
|
||||
return
|
||||
}
|
||||
if istioWasmPlugin == nil {
|
||||
IngressLog.Infof("wasmPlugin:%s will not be transferred to istio since config disabled",
|
||||
clusterNamespacedName.Name)
|
||||
m.mutex.Lock()
|
||||
delete(m.wasmPlugins, clusterNamespacedName.Name)
|
||||
m.mutex.Unlock()
|
||||
return
|
||||
}
|
||||
IngressLog.Debugf("wasmPlugin:%s convert to istioWasmPlugin:%v", clusterNamespacedName.Name, istioWasmPlugin)
|
||||
m.mutex.Lock()
|
||||
m.wasmPlugins[clusterNamespacedName.Name] = istioWasmPlugin
|
||||
|
||||
@@ -67,6 +67,8 @@ type Ingress struct {
|
||||
IgnoreCase *IgnoreCaseConfig
|
||||
|
||||
Match *MatchConfig
|
||||
|
||||
HeaderControl *HeaderControlConfig
|
||||
}
|
||||
|
||||
func (i *Ingress) NeedRegexMatch() bool {
|
||||
@@ -138,6 +140,7 @@ func NewAnnotationHandlerManager() AnnotationHandler {
|
||||
destination{},
|
||||
ignoreCaseMatching{},
|
||||
match{},
|
||||
headerControl{},
|
||||
},
|
||||
gatewayHandlers: []GatewayHandler{
|
||||
downstreamTLS{},
|
||||
@@ -154,6 +157,7 @@ func NewAnnotationHandlerManager() AnnotationHandler {
|
||||
fallback{},
|
||||
ignoreCaseMatching{},
|
||||
match{},
|
||||
headerControl{},
|
||||
},
|
||||
trafficPolicyHandlers: []TrafficPolicyHandler{
|
||||
upstreamTLS{},
|
||||
|
||||
@@ -106,6 +106,8 @@ func ApplyByWeight(canary, route *networking.HTTPRoute, canaryIngress *Ingress)
|
||||
// We will process total weight in the end.
|
||||
route.Route = append(route.Route, canary.Route[0])
|
||||
|
||||
// canary route use the header control applied on itself.
|
||||
headerControl{}.ApplyRoute(canary, canaryIngress)
|
||||
// Move route level to destination level
|
||||
canary.Route[0].Headers = canary.Headers
|
||||
|
||||
@@ -166,6 +168,10 @@ func ApplyByHeader(canary, route *networking.HTTPRoute, canaryIngress *Ingress)
|
||||
}
|
||||
}
|
||||
|
||||
canary.Headers = nil
|
||||
// canary route use the header control applied on itself.
|
||||
headerControl{}.ApplyRoute(canary, canaryIngress)
|
||||
|
||||
// First add normal route cluster
|
||||
canary.Route[0].FallbackClusters = append(canary.Route[0].FallbackClusters,
|
||||
route.Route[0].Destination.DeepCopy())
|
||||
|
||||
@@ -73,10 +73,14 @@ func (a destination) Parse(annotations Annotations, config *Ingress, _ *GlobalCo
|
||||
}
|
||||
address := pairs[addrIndex]
|
||||
host := address
|
||||
var port string
|
||||
var port uint64
|
||||
colon := strings.LastIndex(address, ":")
|
||||
if colon != -1 {
|
||||
host, port = address[:colon], address[colon+1:]
|
||||
var err error
|
||||
port, err = strconv.ParseUint(address[colon+1:], 10, 32)
|
||||
if err == nil && port > 0 && port < 65536 {
|
||||
host = address[:colon]
|
||||
}
|
||||
}
|
||||
var subset string
|
||||
if len(pairs) >= addrIndex+2 {
|
||||
@@ -89,14 +93,9 @@ func (a destination) Parse(annotations Annotations, config *Ingress, _ *GlobalCo
|
||||
},
|
||||
Weight: int32(weight),
|
||||
}
|
||||
if port != "" {
|
||||
portNumber, err := strconv.ParseUint(port, 10, 32)
|
||||
if err != nil {
|
||||
IngressLog.Errorf("destination addr %s has invalid port %s within ingress %s/%s", address, port, config.Namespace, config.Name)
|
||||
return nil
|
||||
}
|
||||
if port > 0 {
|
||||
dest.Destination.Port = &networking.PortSelector{
|
||||
Number: uint32(portNumber),
|
||||
Number: uint32(port),
|
||||
}
|
||||
}
|
||||
IngressLog.Debugf("destination generated for ingress %s/%s: %v", config.Namespace, config.Name, dest)
|
||||
|
||||
@@ -84,6 +84,39 @@ func TestDestinationParse(t *testing.T) {
|
||||
WeightSum: 100,
|
||||
},
|
||||
},
|
||||
{
|
||||
input: Annotations{
|
||||
buildHigressAnnotationKey(destinationKey): "providers:com.alibaba.nacos.example.dubbo.service.DemoService:1.0.0:.DEFAULT-GROUP.public.nacos",
|
||||
},
|
||||
expect: &DestinationConfig{
|
||||
McpDestination: []*networking.HTTPRouteDestination{
|
||||
{
|
||||
Destination: &networking.Destination{
|
||||
Host: "providers:com.alibaba.nacos.example.dubbo.service.DemoService:1.0.0:.DEFAULT-GROUP.public.nacos",
|
||||
},
|
||||
Weight: 100,
|
||||
},
|
||||
},
|
||||
WeightSum: 100,
|
||||
},
|
||||
},
|
||||
{
|
||||
input: Annotations{
|
||||
buildHigressAnnotationKey(destinationKey): "providers:com.alibaba.nacos.example.dubbo.service.DemoService:1.0.0:.DEFAULT-GROUP.public.nacos:8080",
|
||||
},
|
||||
expect: &DestinationConfig{
|
||||
McpDestination: []*networking.HTTPRouteDestination{
|
||||
{
|
||||
Destination: &networking.Destination{
|
||||
Host: "providers:com.alibaba.nacos.example.dubbo.service.DemoService:1.0.0:.DEFAULT-GROUP.public.nacos",
|
||||
Port: &networking.PortSelector{Number: 8080},
|
||||
},
|
||||
Weight: 100,
|
||||
},
|
||||
},
|
||||
WeightSum: 100,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
|
||||
160
pkg/ingress/kube/annotations/header_control.go
Normal file
160
pkg/ingress/kube/annotations/header_control.go
Normal file
@@ -0,0 +1,160 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package annotations
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
networking "istio.io/api/networking/v1alpha3"
|
||||
|
||||
. "github.com/alibaba/higress/pkg/ingress/log"
|
||||
)
|
||||
|
||||
const (
|
||||
// request
|
||||
requestHeaderAdd = "request-header-control-add"
|
||||
requestHeaderUpdate = "request-header-control-update"
|
||||
requestHeaderRemove = "request-header-control-remove"
|
||||
|
||||
// response
|
||||
responseHeaderAdd = "response-header-control-add"
|
||||
responseHeaderUpdate = "response-header-control-update"
|
||||
responseHeaderRemove = "response-header-control-remove"
|
||||
)
|
||||
|
||||
var (
|
||||
_ Parser = headerControl{}
|
||||
_ RouteHandler = headerControl{}
|
||||
)
|
||||
|
||||
type HeaderOperation struct {
|
||||
Add map[string]string
|
||||
Update map[string]string
|
||||
Remove []string
|
||||
}
|
||||
|
||||
// HeaderControlConfig enforces header operations on route level.
|
||||
// Note: Canary route don't use header control applied on the normal route.
|
||||
type HeaderControlConfig struct {
|
||||
Request *HeaderOperation
|
||||
Response *HeaderOperation
|
||||
}
|
||||
|
||||
type headerControl struct{}
|
||||
|
||||
func (h headerControl) Parse(annotations Annotations, config *Ingress, _ *GlobalContext) error {
|
||||
if !needHeaderControlConfig(annotations) {
|
||||
return nil
|
||||
}
|
||||
|
||||
config.HeaderControl = &HeaderControlConfig{}
|
||||
|
||||
var requestAdd map[string]string
|
||||
var requestUpdate map[string]string
|
||||
var requestRemove []string
|
||||
if add, err := annotations.ParseStringForHigress(requestHeaderAdd); err == nil {
|
||||
requestAdd = convertAddOrUpdate(add)
|
||||
}
|
||||
if update, err := annotations.ParseStringForHigress(requestHeaderUpdate); err == nil {
|
||||
requestUpdate = convertAddOrUpdate(update)
|
||||
}
|
||||
if remove, err := annotations.ParseStringForHigress(requestHeaderRemove); err == nil {
|
||||
requestRemove = splitBySeparator(remove, ",")
|
||||
}
|
||||
if len(requestAdd) > 0 || len(requestUpdate) > 0 || len(requestRemove) > 0 {
|
||||
config.HeaderControl.Request = &HeaderOperation{
|
||||
Add: requestAdd,
|
||||
Update: requestUpdate,
|
||||
Remove: requestRemove,
|
||||
}
|
||||
}
|
||||
|
||||
var responseAdd map[string]string
|
||||
var responseUpdate map[string]string
|
||||
var responseRemove []string
|
||||
if add, err := annotations.ParseStringForHigress(responseHeaderAdd); err == nil {
|
||||
responseAdd = convertAddOrUpdate(add)
|
||||
}
|
||||
if update, err := annotations.ParseStringForHigress(responseHeaderUpdate); err == nil {
|
||||
responseUpdate = convertAddOrUpdate(update)
|
||||
}
|
||||
if remove, err := annotations.ParseStringForHigress(responseHeaderRemove); err == nil {
|
||||
responseRemove = splitBySeparator(remove, ",")
|
||||
}
|
||||
if len(responseAdd) > 0 || len(responseUpdate) > 0 || len(responseRemove) > 0 {
|
||||
config.HeaderControl.Response = &HeaderOperation{
|
||||
Add: responseAdd,
|
||||
Update: responseUpdate,
|
||||
Remove: responseRemove,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h headerControl) ApplyRoute(route *networking.HTTPRoute, config *Ingress) {
|
||||
headerControlConfig := config.HeaderControl
|
||||
if headerControlConfig == nil {
|
||||
return
|
||||
}
|
||||
|
||||
headers := &networking.Headers{
|
||||
Request: &networking.Headers_HeaderOperations{},
|
||||
Response: &networking.Headers_HeaderOperations{},
|
||||
}
|
||||
if headerControlConfig.Request != nil {
|
||||
headers.Request.Add = headerControlConfig.Request.Add
|
||||
headers.Request.Set = headerControlConfig.Request.Update
|
||||
headers.Request.Remove = headerControlConfig.Request.Remove
|
||||
}
|
||||
|
||||
if headerControlConfig.Response != nil {
|
||||
headers.Response.Add = headerControlConfig.Response.Add
|
||||
headers.Response.Set = headerControlConfig.Response.Update
|
||||
headers.Response.Remove = headerControlConfig.Response.Remove
|
||||
}
|
||||
|
||||
route.Headers = headers
|
||||
}
|
||||
|
||||
func needHeaderControlConfig(annotations Annotations) bool {
|
||||
return annotations.HasHigress(requestHeaderAdd) ||
|
||||
annotations.HasHigress(requestHeaderUpdate) ||
|
||||
annotations.HasHigress(requestHeaderRemove) ||
|
||||
annotations.HasHigress(responseHeaderAdd) ||
|
||||
annotations.HasHigress(responseHeaderUpdate) ||
|
||||
annotations.HasHigress(responseHeaderRemove)
|
||||
}
|
||||
|
||||
func convertAddOrUpdate(headers string) map[string]string {
|
||||
result := map[string]string{}
|
||||
parts := strings.Split(headers, "\n")
|
||||
for _, part := range parts {
|
||||
part = strings.TrimSpace(part)
|
||||
if part == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
keyValue := strings.Fields(part)
|
||||
if len(keyValue) != 2 {
|
||||
IngressLog.Errorf("Header format %s is invalid.", keyValue)
|
||||
continue
|
||||
}
|
||||
key := strings.TrimSpace(keyValue[0])
|
||||
value := strings.TrimSpace(keyValue[1])
|
||||
result[key] = value
|
||||
}
|
||||
return result
|
||||
}
|
||||
235
pkg/ingress/kube/annotations/header_control_test.go
Normal file
235
pkg/ingress/kube/annotations/header_control_test.go
Normal file
@@ -0,0 +1,235 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package annotations
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
networking "istio.io/api/networking/v1alpha3"
|
||||
)
|
||||
|
||||
func TestHeaderControlParse(t *testing.T) {
|
||||
headerControl := &headerControl{}
|
||||
inputCases := []struct {
|
||||
input map[string]string
|
||||
expect *HeaderControlConfig
|
||||
}{
|
||||
{},
|
||||
{
|
||||
input: map[string]string{
|
||||
buildHigressAnnotationKey(requestHeaderAdd): "one 1",
|
||||
buildHigressAnnotationKey(responseHeaderAdd): "A a",
|
||||
},
|
||||
expect: &HeaderControlConfig{
|
||||
Request: &HeaderOperation{
|
||||
Add: map[string]string{
|
||||
"one": "1",
|
||||
},
|
||||
},
|
||||
Response: &HeaderOperation{
|
||||
Add: map[string]string{
|
||||
"A": "a",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
input: map[string]string{
|
||||
buildHigressAnnotationKey(requestHeaderAdd): "one 1\n two 2\nthree 3 \n",
|
||||
buildHigressAnnotationKey(requestHeaderUpdate): "two 2",
|
||||
buildHigressAnnotationKey(requestHeaderRemove): "one, two,three\n",
|
||||
buildHigressAnnotationKey(responseHeaderAdd): "A a\nB b\n",
|
||||
buildHigressAnnotationKey(responseHeaderUpdate): "X x\nY y\n",
|
||||
buildHigressAnnotationKey(responseHeaderRemove): "x",
|
||||
},
|
||||
expect: &HeaderControlConfig{
|
||||
Request: &HeaderOperation{
|
||||
Add: map[string]string{
|
||||
"one": "1",
|
||||
"two": "2",
|
||||
"three": "3",
|
||||
},
|
||||
Update: map[string]string{
|
||||
"two": "2",
|
||||
},
|
||||
Remove: []string{"one", "two", "three"},
|
||||
},
|
||||
Response: &HeaderOperation{
|
||||
Add: map[string]string{
|
||||
"A": "a",
|
||||
"B": "b",
|
||||
},
|
||||
Update: map[string]string{
|
||||
"X": "x",
|
||||
"Y": "y",
|
||||
},
|
||||
Remove: []string{"x"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, inputCase := range inputCases {
|
||||
t.Run("", func(t *testing.T) {
|
||||
config := &Ingress{}
|
||||
_ = headerControl.Parse(inputCase.input, config, nil)
|
||||
if !reflect.DeepEqual(inputCase.expect, config.HeaderControl) {
|
||||
t.Fatal("Should be equal")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHeaderControlApplyRoute(t *testing.T) {
|
||||
headerControl := headerControl{}
|
||||
inputCases := []struct {
|
||||
config *Ingress
|
||||
input *networking.HTTPRoute
|
||||
expect *networking.HTTPRoute
|
||||
}{
|
||||
{
|
||||
config: &Ingress{},
|
||||
input: &networking.HTTPRoute{},
|
||||
expect: &networking.HTTPRoute{},
|
||||
},
|
||||
{
|
||||
config: &Ingress{
|
||||
HeaderControl: &HeaderControlConfig{},
|
||||
},
|
||||
input: &networking.HTTPRoute{},
|
||||
expect: &networking.HTTPRoute{
|
||||
Headers: &networking.Headers{
|
||||
Request: &networking.Headers_HeaderOperations{},
|
||||
Response: &networking.Headers_HeaderOperations{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
config: &Ingress{
|
||||
HeaderControl: &HeaderControlConfig{
|
||||
Request: &HeaderOperation{
|
||||
Add: map[string]string{
|
||||
"one": "1",
|
||||
"two": "2",
|
||||
"three": "3",
|
||||
},
|
||||
Update: map[string]string{
|
||||
"two": "2",
|
||||
},
|
||||
Remove: []string{"one", "two", "three"},
|
||||
},
|
||||
},
|
||||
},
|
||||
input: &networking.HTTPRoute{},
|
||||
expect: &networking.HTTPRoute{
|
||||
Headers: &networking.Headers{
|
||||
Request: &networking.Headers_HeaderOperations{
|
||||
Add: map[string]string{
|
||||
"one": "1",
|
||||
"two": "2",
|
||||
"three": "3",
|
||||
},
|
||||
Set: map[string]string{
|
||||
"two": "2",
|
||||
},
|
||||
Remove: []string{"one", "two", "three"},
|
||||
},
|
||||
Response: &networking.Headers_HeaderOperations{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
config: &Ingress{
|
||||
HeaderControl: &HeaderControlConfig{
|
||||
Response: &HeaderOperation{
|
||||
Add: map[string]string{
|
||||
"A": "a",
|
||||
"B": "b",
|
||||
},
|
||||
Update: map[string]string{
|
||||
"X": "x",
|
||||
"Y": "y",
|
||||
},
|
||||
Remove: []string{"x"},
|
||||
},
|
||||
},
|
||||
},
|
||||
input: &networking.HTTPRoute{},
|
||||
expect: &networking.HTTPRoute{
|
||||
Headers: &networking.Headers{
|
||||
Request: &networking.Headers_HeaderOperations{},
|
||||
Response: &networking.Headers_HeaderOperations{
|
||||
Add: map[string]string{
|
||||
"A": "a",
|
||||
"B": "b",
|
||||
},
|
||||
Set: map[string]string{
|
||||
"X": "x",
|
||||
"Y": "y",
|
||||
},
|
||||
Remove: []string{"x"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
config: &Ingress{
|
||||
HeaderControl: &HeaderControlConfig{
|
||||
Request: &HeaderOperation{
|
||||
Update: map[string]string{
|
||||
"two": "2",
|
||||
},
|
||||
Remove: []string{"one", "two", "three"},
|
||||
},
|
||||
Response: &HeaderOperation{
|
||||
Add: map[string]string{
|
||||
"A": "a",
|
||||
"B": "b",
|
||||
},
|
||||
Remove: []string{"x"},
|
||||
},
|
||||
},
|
||||
},
|
||||
input: &networking.HTTPRoute{},
|
||||
expect: &networking.HTTPRoute{
|
||||
Headers: &networking.Headers{
|
||||
Request: &networking.Headers_HeaderOperations{
|
||||
Set: map[string]string{
|
||||
"two": "2",
|
||||
},
|
||||
Remove: []string{"one", "two", "three"},
|
||||
},
|
||||
Response: &networking.Headers_HeaderOperations{
|
||||
Add: map[string]string{
|
||||
"A": "a",
|
||||
"B": "b",
|
||||
},
|
||||
Remove: []string{"x"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, inputCase := range inputCases {
|
||||
t.Run("", func(t *testing.T) {
|
||||
headerControl.ApplyRoute(inputCase.input, inputCase.config)
|
||||
if !reflect.DeepEqual(inputCase.input, inputCase.expect) {
|
||||
t.Fatal("Should be equal")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
<p>
|
||||
<a href="README_EN.md">English</a> | 中文
|
||||
</p>
|
||||
|
||||
# 功能说明
|
||||
`basic-auth`插件实现了基于 HTTP Basic Auth 标准进行认证鉴权的功能
|
||||
|
||||
|
||||
117
plugins/wasm-cpp/extensions/basic_auth/README_EN.md
Normal file
117
plugins/wasm-cpp/extensions/basic_auth/README_EN.md
Normal file
@@ -0,0 +1,117 @@
|
||||
<p>
|
||||
English | <a href="README.md">中文</a>
|
||||
</p>
|
||||
|
||||
# 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 |
|
||||
@@ -44,7 +44,7 @@ consumers:
|
||||
name: consumer2
|
||||
keys:
|
||||
- apikey
|
||||
- x-api-key
|
||||
in_query: true
|
||||
# 使用 _rules_ 字段进行细粒度规则配置
|
||||
_rules_:
|
||||
# 规则一:按路由名称匹配生效
|
||||
@@ -109,7 +109,7 @@ consumers:
|
||||
name: consumer2
|
||||
keys:
|
||||
- apikey
|
||||
- x-api-key
|
||||
in_query: true
|
||||
```
|
||||
|
||||
# 相关错误码
|
||||
|
||||
164
registry/direct/watcher.go
Normal file
164
registry/direct/watcher.go
Normal file
@@ -0,0 +1,164 @@
|
||||
// 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 direct
|
||||
|
||||
import (
|
||||
"net"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"istio.io/api/networking/v1alpha3"
|
||||
"istio.io/istio/pkg/config/protocol"
|
||||
"istio.io/pkg/log"
|
||||
|
||||
apiv1 "github.com/alibaba/higress/api/networking/v1"
|
||||
"github.com/alibaba/higress/pkg/common"
|
||||
"github.com/alibaba/higress/registry"
|
||||
provider "github.com/alibaba/higress/registry"
|
||||
"github.com/alibaba/higress/registry/memory"
|
||||
)
|
||||
|
||||
type watcher struct {
|
||||
provider.BaseWatcher
|
||||
apiv1.RegistryConfig
|
||||
cache memory.Cache
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
type WatcherOption func(w *watcher)
|
||||
|
||||
func NewWatcher(cache memory.Cache, opts ...WatcherOption) (provider.Watcher, error) {
|
||||
w := &watcher{
|
||||
cache: cache,
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(w)
|
||||
}
|
||||
return w, nil
|
||||
}
|
||||
|
||||
func WithType(t string) WatcherOption {
|
||||
return func(w *watcher) {
|
||||
w.Type = t
|
||||
}
|
||||
}
|
||||
|
||||
func WithName(name string) WatcherOption {
|
||||
return func(w *watcher) {
|
||||
w.Name = name
|
||||
}
|
||||
}
|
||||
|
||||
func WithDomain(domain string) WatcherOption {
|
||||
return func(w *watcher) {
|
||||
w.Domain = domain
|
||||
}
|
||||
}
|
||||
|
||||
func WithPort(port uint32) WatcherOption {
|
||||
return func(w *watcher) {
|
||||
w.Port = port
|
||||
}
|
||||
}
|
||||
|
||||
func (w *watcher) Run() {
|
||||
w.mutex.Lock()
|
||||
defer w.mutex.Unlock()
|
||||
host := strings.Join([]string{w.Name, w.Type}, common.DotSeparator)
|
||||
serviceEntry := w.generateServiceEntry(host)
|
||||
if serviceEntry != nil {
|
||||
w.cache.UpdateServiceEntryWrapper(host, &memory.ServiceEntryWrapper{
|
||||
ServiceName: w.Name,
|
||||
ServiceEntry: serviceEntry,
|
||||
Suffix: w.Type,
|
||||
RegistryType: w.Type,
|
||||
})
|
||||
w.UpdateService()
|
||||
}
|
||||
w.Ready(true)
|
||||
}
|
||||
|
||||
func (w *watcher) Stop() {
|
||||
w.mutex.Lock()
|
||||
defer w.mutex.Unlock()
|
||||
host := strings.Join([]string{w.Name, w.Type}, common.DotSeparator)
|
||||
w.cache.DeleteServiceEntryWrapper(host)
|
||||
w.Ready(false)
|
||||
}
|
||||
|
||||
var domainRegex = regexp.MustCompile(`^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,6}$`)
|
||||
|
||||
func (w *watcher) generateServiceEntry(host string) *v1alpha3.ServiceEntry {
|
||||
endpoints := make([]*v1alpha3.WorkloadEntry, 0)
|
||||
for _, ep := range strings.Split(w.Domain, common.CommaSeparator) {
|
||||
var endpoint *v1alpha3.WorkloadEntry
|
||||
if w.Type == string(registry.Static) {
|
||||
pair := strings.Split(ep, common.ColonSeparator)
|
||||
if len(pair) != 2 {
|
||||
log.Errorf("invalid endpoint:%s with static type", ep)
|
||||
return nil
|
||||
}
|
||||
port, err := strconv.ParseUint(pair[1], 10, 32)
|
||||
if err != nil {
|
||||
log.Errorf("invalid port:%s of endpoint:%s", pair[1], ep)
|
||||
return nil
|
||||
}
|
||||
if net.ParseIP(pair[0]) == nil {
|
||||
log.Errorf("invalid ip:%s of endpoint:%s", pair[0], ep)
|
||||
return nil
|
||||
}
|
||||
endpoint = &v1alpha3.WorkloadEntry{
|
||||
Address: pair[0],
|
||||
Ports: map[string]uint32{"http": uint32(port)},
|
||||
}
|
||||
} else if w.Type == string(registry.DNS) {
|
||||
if !domainRegex.MatchString(ep) {
|
||||
log.Errorf("invalid domain format:%s", ep)
|
||||
return nil
|
||||
}
|
||||
endpoint = &v1alpha3.WorkloadEntry{
|
||||
Address: ep,
|
||||
}
|
||||
} else {
|
||||
log.Errorf("unknown direct service type:%s", w.Type)
|
||||
return nil
|
||||
}
|
||||
endpoints = append(endpoints, endpoint)
|
||||
}
|
||||
if len(endpoints) == 0 {
|
||||
log.Errorf("empty endpoints will not be pushed, host:%s", host)
|
||||
return nil
|
||||
}
|
||||
var ports []*v1alpha3.Port
|
||||
ports = append(ports, &v1alpha3.Port{
|
||||
Number: w.Port,
|
||||
Name: "http",
|
||||
Protocol: string(protocol.HTTP),
|
||||
})
|
||||
se := &v1alpha3.ServiceEntry{
|
||||
Hosts: []string{host},
|
||||
Ports: ports,
|
||||
Location: v1alpha3.ServiceEntry_MESH_INTERNAL,
|
||||
Endpoints: endpoints,
|
||||
}
|
||||
if w.Type == string(registry.Static) {
|
||||
se.Resolution = v1alpha3.ServiceEntry_STATIC
|
||||
} else if w.Type == string(registry.DNS) {
|
||||
se.Resolution = v1alpha3.ServiceEntry_DNS
|
||||
}
|
||||
return se
|
||||
}
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
type Cache interface {
|
||||
UpdateServiceEntryWrapper(service string, data *ServiceEntryWrapper)
|
||||
DeleteServiceEntryWrapper(service string)
|
||||
PurgeStaleService()
|
||||
UpdateServiceEntryEnpointWrapper(service, ip, regionId, zoneId, protocol string, labels map[string]string)
|
||||
GetServiceByEndpoints(requestVersions, endpoints map[string]bool, versionKey string, protocol common.Protocol) map[string][]string
|
||||
GetAllServiceEntry() []*v1alpha3.ServiceEntry
|
||||
@@ -39,20 +40,22 @@ type Cache interface {
|
||||
|
||||
func NewCache() Cache {
|
||||
return &store{
|
||||
mux: &sync.RWMutex{},
|
||||
sew: make(map[string]*ServiceEntryWrapper),
|
||||
toBeUpdated: make([]*ServiceEntryWrapper, 0),
|
||||
toBeDeleted: make([]*ServiceEntryWrapper, 0),
|
||||
ip2services: make(map[string]map[string]bool),
|
||||
mux: &sync.RWMutex{},
|
||||
sew: make(map[string]*ServiceEntryWrapper),
|
||||
toBeUpdated: make([]*ServiceEntryWrapper, 0),
|
||||
toBeDeleted: make([]*ServiceEntryWrapper, 0),
|
||||
ip2services: make(map[string]map[string]bool),
|
||||
deferedDelete: make(map[string]struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
type store struct {
|
||||
mux *sync.RWMutex
|
||||
sew map[string]*ServiceEntryWrapper
|
||||
toBeUpdated []*ServiceEntryWrapper
|
||||
toBeDeleted []*ServiceEntryWrapper
|
||||
ip2services map[string]map[string]bool
|
||||
mux *sync.RWMutex
|
||||
sew map[string]*ServiceEntryWrapper
|
||||
toBeUpdated []*ServiceEntryWrapper
|
||||
toBeDeleted []*ServiceEntryWrapper
|
||||
ip2services map[string]map[string]bool
|
||||
deferedDelete map[string]struct{}
|
||||
}
|
||||
|
||||
func (s *store) UpdateServiceEntryEnpointWrapper(service, ip, regionId, zoneId, protocol string, labels map[string]string) {
|
||||
@@ -105,6 +108,12 @@ func (s *store) UpdateServiceEntryWrapper(service string, data *ServiceEntryWrap
|
||||
|
||||
s.toBeUpdated = append(s.toBeUpdated, data)
|
||||
s.sew[service] = data
|
||||
// service is updated, should not be deleted
|
||||
if _, ok := s.deferedDelete[service]; ok {
|
||||
delete(s.deferedDelete, service)
|
||||
log.Debugf("service in deferedDelete updated, host:%s", service)
|
||||
}
|
||||
log.Infof("ServiceEntry updated, host:%s", service)
|
||||
}
|
||||
|
||||
func (s *store) DeleteServiceEntryWrapper(service string) {
|
||||
@@ -114,7 +123,18 @@ func (s *store) DeleteServiceEntryWrapper(service string) {
|
||||
if data, exist := s.sew[service]; exist {
|
||||
s.toBeDeleted = append(s.toBeDeleted, data)
|
||||
}
|
||||
delete(s.sew, service)
|
||||
s.deferedDelete[service] = struct{}{}
|
||||
}
|
||||
|
||||
// should only be called when reconcile is done
|
||||
func (s *store) PurgeStaleService() {
|
||||
s.mux.Lock()
|
||||
defer s.mux.Unlock()
|
||||
for service := range s.deferedDelete {
|
||||
delete(s.sew, service)
|
||||
delete(s.deferedDelete, service)
|
||||
log.Infof("ServiceEntry deleted, host:%s", service)
|
||||
}
|
||||
}
|
||||
|
||||
// GetServiceByEndpoints get the list of services of which "address:port" contained by the endpoints
|
||||
|
||||
@@ -60,8 +60,6 @@ type watcher struct {
|
||||
RegistryType provider.ServiceRegistryType `json:"registry_type"`
|
||||
Status provider.WatcherStatus `json:"status"`
|
||||
namingClient naming_client.INamingClient
|
||||
updateHandler provider.ServiceUpdateHandler
|
||||
readyHandler provider.ReadyHandler
|
||||
cache memory.Cache
|
||||
mutex *sync.Mutex
|
||||
stop chan struct{}
|
||||
@@ -234,7 +232,7 @@ func (w *watcher) Run() {
|
||||
if err != nil {
|
||||
log.Errorf("first fetch services failed, err:%v", err)
|
||||
} else {
|
||||
w.readyHandler(true)
|
||||
w.Ready(true)
|
||||
}
|
||||
for {
|
||||
select {
|
||||
@@ -243,7 +241,7 @@ func (w *watcher) Run() {
|
||||
if err != nil {
|
||||
log.Errorf("fetch services failed, err:%v", err)
|
||||
} else {
|
||||
w.readyHandler(true)
|
||||
w.Ready(true)
|
||||
}
|
||||
case <-w.stop:
|
||||
return
|
||||
@@ -399,7 +397,7 @@ func (w *watcher) getSubscribeCallback(groupName string, serviceName string) fun
|
||||
host := strings.Join([]string{serviceName, suffix}, common.DotSeparator)
|
||||
|
||||
return func(services []model.Instance, err error) {
|
||||
defer w.updateHandler()
|
||||
defer w.UpdateService()
|
||||
|
||||
//log.Info("callback", "serviceName", serviceName, "suffix", suffix, "details", services)
|
||||
|
||||
@@ -484,8 +482,8 @@ func (w *watcher) Stop() {
|
||||
}
|
||||
|
||||
w.isStop = true
|
||||
w.stop <- struct{}{}
|
||||
w.readyHandler(false)
|
||||
close(w.stop)
|
||||
w.Ready(false)
|
||||
}
|
||||
|
||||
func (w *watcher) IsHealthy() bool {
|
||||
@@ -496,14 +494,6 @@ func (w *watcher) GetRegistryType() string {
|
||||
return w.RegistryType.String()
|
||||
}
|
||||
|
||||
func (w *watcher) AppendServiceUpdateHandler(f func()) {
|
||||
w.updateHandler = f
|
||||
}
|
||||
|
||||
func (w *watcher) ReadyHandler(f func(bool)) {
|
||||
w.readyHandler = f
|
||||
}
|
||||
|
||||
func shouldSubscribe(serviceName string) bool {
|
||||
prefixFilters := []string{"consumers:"}
|
||||
fullFilters := []string{""}
|
||||
|
||||
@@ -58,8 +58,6 @@ type watcher struct {
|
||||
RegistryType provider.ServiceRegistryType `json:"registry_type"`
|
||||
Status provider.WatcherStatus `json:"status"`
|
||||
namingClient naming_client.INamingClient
|
||||
updateHandler provider.ServiceUpdateHandler
|
||||
readyHandler provider.ReadyHandler
|
||||
cache memory.Cache
|
||||
mutex *sync.Mutex
|
||||
stop chan struct{}
|
||||
@@ -200,7 +198,7 @@ func (w *watcher) Run() {
|
||||
defer ticker.Stop()
|
||||
w.Status = provider.ProbeWatcherStatus(w.Domain, strconv.FormatUint(uint64(w.Port), 10))
|
||||
w.fetchAllServices()
|
||||
w.readyHandler(true)
|
||||
w.Ready(true)
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
@@ -218,7 +216,6 @@ func (w *watcher) fetchAllServices() error {
|
||||
return nil
|
||||
}
|
||||
fetchedServices := make(map[string]bool)
|
||||
|
||||
for _, groupName := range w.NacosGroups {
|
||||
for page := 1; ; page++ {
|
||||
ss, err := w.namingClient.GetAllServicesInfo(vo.GetAllServiceInfoParam{
|
||||
@@ -305,7 +302,7 @@ func (w *watcher) getSubscribeCallback(groupName string, serviceName string) fun
|
||||
host := strings.Join([]string{serviceName, suffix}, common.DotSeparator)
|
||||
|
||||
return func(services []model.SubscribeService, err error) {
|
||||
defer w.updateHandler()
|
||||
defer w.UpdateService()
|
||||
|
||||
//log.Info("callback", "serviceName", serviceName, "suffix", suffix, "details", services)
|
||||
|
||||
@@ -388,8 +385,8 @@ func (w *watcher) Stop() {
|
||||
w.cache.DeleteServiceEntryWrapper(host)
|
||||
}
|
||||
w.isStop = true
|
||||
w.stop <- struct{}{}
|
||||
w.readyHandler(false)
|
||||
close(w.stop)
|
||||
w.Ready(false)
|
||||
}
|
||||
|
||||
func (w *watcher) IsHealthy() bool {
|
||||
@@ -400,14 +397,6 @@ func (w *watcher) GetRegistryType() string {
|
||||
return w.RegistryType.String()
|
||||
}
|
||||
|
||||
func (w *watcher) AppendServiceUpdateHandler(f func()) {
|
||||
w.updateHandler = f
|
||||
}
|
||||
|
||||
func (w *watcher) ReadyHandler(f func(bool)) {
|
||||
w.readyHandler = f
|
||||
}
|
||||
|
||||
func shouldSubscribe(serviceName string) bool {
|
||||
prefixFilters := []string{"consumers:"}
|
||||
fullFilters := []string{""}
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
apiv1 "github.com/alibaba/higress/api/networking/v1"
|
||||
v1 "github.com/alibaba/higress/client/pkg/apis/networking/v1"
|
||||
. "github.com/alibaba/higress/registry"
|
||||
"github.com/alibaba/higress/registry/direct"
|
||||
"github.com/alibaba/higress/registry/memory"
|
||||
"github.com/alibaba/higress/registry/nacos"
|
||||
nacosv2 "github.com/alibaba/higress/registry/nacos/v2"
|
||||
@@ -77,6 +78,26 @@ func (r *Reconciler) Reconcile(mcpbridge *v1.McpBridge) {
|
||||
errHappened := false
|
||||
log.Infof("ReconcileRegistries, toBeCreated: %d, toBeUpdated: %d, toBeDeleted: %d",
|
||||
len(toBeCreated), len(toBeUpdated), len(toBeDeleted))
|
||||
for k := range toBeDeleted {
|
||||
r.watchers[k].Stop()
|
||||
delete(r.registries, k)
|
||||
delete(r.watchers, k)
|
||||
}
|
||||
for k, v := range toBeUpdated {
|
||||
r.watchers[k].Stop()
|
||||
delete(r.registries, k)
|
||||
delete(r.watchers, k)
|
||||
watcher, err := r.generateWatcherFromRegistryConfig(v, &wg)
|
||||
if err != nil {
|
||||
errHappened = true
|
||||
log.Errorf("ReconcileRegistries failed, err:%v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
go watcher.Run()
|
||||
r.watchers[k] = watcher
|
||||
r.registries[k] = v
|
||||
}
|
||||
for k, v := range toBeCreated {
|
||||
watcher, err := r.generateWatcherFromRegistryConfig(v, &wg)
|
||||
if err != nil {
|
||||
@@ -89,31 +110,12 @@ func (r *Reconciler) Reconcile(mcpbridge *v1.McpBridge) {
|
||||
r.watchers[k] = watcher
|
||||
r.registries[k] = v
|
||||
}
|
||||
for k, v := range toBeUpdated {
|
||||
go r.watchers[k].Stop()
|
||||
delete(r.registries, k)
|
||||
delete(r.watchers, k)
|
||||
watcher, err := r.generateWatcherFromRegistryConfig(v, &wg)
|
||||
if err != nil {
|
||||
errHappened = true
|
||||
log.Errorf("ReconcileRegistries failed, err:%v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
go watcher.Run()
|
||||
r.watchers[k] = watcher
|
||||
r.registries[k] = v
|
||||
}
|
||||
for k := range toBeDeleted {
|
||||
go r.watchers[k].Stop()
|
||||
delete(r.registries, k)
|
||||
delete(r.watchers, k)
|
||||
}
|
||||
if errHappened {
|
||||
log.Error("ReconcileRegistries failed, Init Watchers failed")
|
||||
return
|
||||
}
|
||||
wg.Wait()
|
||||
r.Cache.PurgeStaleService()
|
||||
log.Infof("Registries is reconciled")
|
||||
}
|
||||
|
||||
@@ -158,6 +160,14 @@ func (r *Reconciler) generateWatcherFromRegistryConfig(registry *apiv1.RegistryC
|
||||
zookeeper.WithPort(registry.Port),
|
||||
zookeeper.WithZkServicesPath(registry.ZkServicesPath),
|
||||
)
|
||||
case string(Static), string(DNS):
|
||||
watcher, err = direct.NewWatcher(
|
||||
r.Cache,
|
||||
direct.WithType(registry.Type),
|
||||
direct.WithName(registry.Name),
|
||||
direct.WithDomain(registry.Domain),
|
||||
direct.WithPort(registry.Port),
|
||||
)
|
||||
default:
|
||||
return nil, errors.New("unsupported registry type:" + registry.Type)
|
||||
}
|
||||
@@ -172,7 +182,7 @@ func (r *Reconciler) generateWatcherFromRegistryConfig(registry *apiv1.RegistryC
|
||||
once.Do(func() {
|
||||
wg.Done()
|
||||
if ready {
|
||||
log.Infof("Registry Watcher is ready, type:%s, name:%s", registry.Type, registry.Name)
|
||||
log.Infof("Registry Watcher is ready, type:%s, name:%s", registry.Type, registry.Name)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -25,6 +25,8 @@ const (
|
||||
Consul ServiceRegistryType = "consul"
|
||||
Nacos ServiceRegistryType = "nacos"
|
||||
Nacos2 ServiceRegistryType = "nacos2"
|
||||
Static ServiceRegistryType = "static"
|
||||
DNS ServiceRegistryType = "dns"
|
||||
Healthy WatcherStatus = "healthy"
|
||||
UnHealthy WatcherStatus = "unhealthy"
|
||||
|
||||
@@ -52,14 +54,21 @@ type Watcher interface {
|
||||
ReadyHandler(f func(bool))
|
||||
}
|
||||
|
||||
type BaseWatcher struct{}
|
||||
type BaseWatcher struct {
|
||||
UpdateService ServiceUpdateHandler
|
||||
Ready ReadyHandler
|
||||
}
|
||||
|
||||
func (w *BaseWatcher) Run() {}
|
||||
func (w *BaseWatcher) Stop() {}
|
||||
func (w *BaseWatcher) IsHealthy() bool { return true }
|
||||
func (w *BaseWatcher) GetRegistryType() string { return "" }
|
||||
func (w *BaseWatcher) AppendServiceUpdateHandler(f func()) {}
|
||||
func (w *BaseWatcher) ReadyHandler(f func(bool)) {}
|
||||
func (w *BaseWatcher) Run() {}
|
||||
func (w *BaseWatcher) Stop() {}
|
||||
func (w *BaseWatcher) IsHealthy() bool { return true }
|
||||
func (w *BaseWatcher) GetRegistryType() string { return "" }
|
||||
func (w *BaseWatcher) AppendServiceUpdateHandler(f func()) {
|
||||
w.UpdateService = f
|
||||
}
|
||||
func (w *BaseWatcher) ReadyHandler(f func(bool)) {
|
||||
w.Ready = f
|
||||
}
|
||||
|
||||
type ServiceUpdateHandler func()
|
||||
type ReadyHandler func(bool)
|
||||
|
||||
@@ -50,8 +50,6 @@ type watcher struct {
|
||||
RegistryType provider.ServiceRegistryType `json:"registry_type"`
|
||||
Status provider.WatcherStatus `json:"status"`
|
||||
serviceRemaind *atomic.Int32
|
||||
updateHandler provider.ServiceUpdateHandler
|
||||
readyHandler provider.ReadyHandler
|
||||
cache memory.Cache
|
||||
mutex *sync.Mutex
|
||||
stop chan struct{}
|
||||
@@ -103,27 +101,6 @@ func NewWatcher(cache memory.Cache, opts ...WatcherOption) (provider.Watcher, er
|
||||
log.Info("connect zk error")
|
||||
return nil, errors.New("connect zk error")
|
||||
}
|
||||
connectEvent := make(chan zk.Event, 2)
|
||||
newClient.RegisterEvent("", connectEvent)
|
||||
connectTimer := time.NewTimer(timeout)
|
||||
connectTimout := false
|
||||
FOR:
|
||||
for {
|
||||
select {
|
||||
case ev := <-connectEvent:
|
||||
if ev.State == zk.StateConnected {
|
||||
break FOR
|
||||
}
|
||||
case <-connectTimer.C:
|
||||
connectTimout = true
|
||||
break FOR
|
||||
}
|
||||
}
|
||||
if connectTimout {
|
||||
return nil, errors.New("connect zk timeout")
|
||||
}
|
||||
log.Info("zk connected")
|
||||
newClient.UnregisterEvent("", connectEvent)
|
||||
w.reconnectCh = newClient.Reconnect()
|
||||
w.zkClient = newClient
|
||||
go func() {
|
||||
@@ -360,7 +337,7 @@ func (w *watcher) DataChange(eventType Event) bool {
|
||||
Suffix: "zookeeper",
|
||||
RegistryType: w.Type,
|
||||
})
|
||||
w.updateHandler()
|
||||
w.UpdateService()
|
||||
} else if eventType.Action == EventTypeDel {
|
||||
w.seMux.Lock()
|
||||
value, ok := w.serviceEntry[host]
|
||||
@@ -391,7 +368,7 @@ func (w *watcher) DataChange(eventType Event) bool {
|
||||
RegistryType: w.Type,
|
||||
})
|
||||
}
|
||||
w.updateHandler()
|
||||
w.UpdateService()
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -601,7 +578,7 @@ func (w *watcher) ChildToServiceEntry(children []string, interfaceName, zkPath s
|
||||
}
|
||||
}
|
||||
w.seMux.Unlock()
|
||||
w.updateHandler()
|
||||
w.UpdateService()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -702,7 +679,7 @@ func (w *watcher) Run() {
|
||||
case <-ticker.C:
|
||||
var needNewFetch bool
|
||||
if w.IsReady() {
|
||||
w.readyHandler(true)
|
||||
w.Ready(true)
|
||||
needNewFetch = true
|
||||
}
|
||||
if firstFetchErr != nil || needNewFetch {
|
||||
@@ -733,15 +710,13 @@ func (w *watcher) Stop() {
|
||||
for key := range w.serviceEntry {
|
||||
w.cache.DeleteServiceEntryWrapper(key)
|
||||
}
|
||||
w.updateHandler()
|
||||
w.UpdateService()
|
||||
w.seMux.Unlock()
|
||||
|
||||
w.stop <- struct{}{}
|
||||
w.Done <- struct{}{}
|
||||
close(w.stop)
|
||||
close(w.Done)
|
||||
w.zkClient.Close()
|
||||
w.readyHandler(false)
|
||||
w.Ready(false)
|
||||
}
|
||||
|
||||
func (w *watcher) IsHealthy() bool {
|
||||
@@ -752,14 +727,6 @@ func (w *watcher) GetRegistryType() string {
|
||||
return w.RegistryType.String()
|
||||
}
|
||||
|
||||
func (w *watcher) AppendServiceUpdateHandler(f func()) {
|
||||
w.updateHandler = f
|
||||
}
|
||||
|
||||
func (w *watcher) ReadyHandler(f func(bool)) {
|
||||
w.readyHandler = f
|
||||
}
|
||||
|
||||
func (w *watcher) IsReady() bool {
|
||||
if w.serviceRemaind == nil {
|
||||
return true
|
||||
|
||||
78
test/ingress/conformance/tests/httproute-canary-weight.go
Normal file
78
test/ingress/conformance/tests/httproute-canary-weight.go
Normal file
@@ -0,0 +1,78 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/alibaba/higress/test/ingress/conformance/utils/http"
|
||||
"github.com/alibaba/higress/test/ingress/conformance/utils/suite"
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteCanaryWeight)
|
||||
}
|
||||
|
||||
var HTTPRouteCanaryWeight = suite.ConformanceTest{
|
||||
ShortName: "HTTPRouteCanaryWeight",
|
||||
Description: "The Ingress in the higress-conformance-infra namespace uses the canary weight traffic split.",
|
||||
Manifests: []string{"tests/httproute-canary-weight.yaml"},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testcases := []http.Assertion{
|
||||
// test if the weight is 0
|
||||
{
|
||||
Meta: http.AssertionMeta{
|
||||
TargetBackend: "infra-backend-v1",
|
||||
TargetNamespace: "higress-conformance-infra",
|
||||
},
|
||||
Request: http.AssertionRequest{
|
||||
ActualRequest: http.Request{
|
||||
Path: "/weight-0",
|
||||
Host: "canary.higress.io",
|
||||
},
|
||||
},
|
||||
Response: http.AssertionResponse{
|
||||
ExpectedResponse: http.Response{
|
||||
StatusCode: 200,
|
||||
},
|
||||
},
|
||||
},
|
||||
// test if the weight is 100
|
||||
{
|
||||
Meta: http.AssertionMeta{
|
||||
TargetBackend: "infra-backend-v2",
|
||||
TargetNamespace: "higress-conformance-infra",
|
||||
},
|
||||
Request: http.AssertionRequest{
|
||||
ActualRequest: http.Request{
|
||||
Path: "/weight-100",
|
||||
Host: "canary.higress.io",
|
||||
},
|
||||
},
|
||||
Response: http.AssertionResponse{
|
||||
ExpectedResponse: http.Response{
|
||||
StatusCode: 200,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("Canary HTTPRoute Traffic Split", func(t *testing.T) {
|
||||
for _, testcase := range testcases {
|
||||
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, testcase)
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
97
test/ingress/conformance/tests/httproute-canary-weight.yaml
Normal file
97
test/ingress/conformance/tests/httproute-canary-weight.yaml
Normal file
@@ -0,0 +1,97 @@
|
||||
# Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Test if the weight is 0
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/canary: "true"
|
||||
nginx.ingress.kubernetes.io/canary-weight: "0"
|
||||
name: ingress-echo-canary-weight-0
|
||||
namespace: higress-conformance-infra
|
||||
spec:
|
||||
ingressClassName: higress
|
||||
rules:
|
||||
- host: canary.higress.io
|
||||
http:
|
||||
paths:
|
||||
- path: /weight-0
|
||||
pathType: Exact
|
||||
backend:
|
||||
service:
|
||||
name: infra-backend-v3
|
||||
port:
|
||||
number: 8080
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: ingress-echo-weight-0
|
||||
namespace: higress-conformance-infra
|
||||
spec:
|
||||
ingressClassName: higress
|
||||
rules:
|
||||
- host: canary.higress.io
|
||||
http:
|
||||
paths:
|
||||
- path: /weight-0
|
||||
pathType: Exact
|
||||
backend:
|
||||
service:
|
||||
name: infra-backend-v1
|
||||
port:
|
||||
number: 8080
|
||||
---
|
||||
# Test if the weight is 100
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/canary: "true"
|
||||
nginx.ingress.kubernetes.io/canary-weight: "100"
|
||||
name: ingress-echo-canary-weight-100
|
||||
namespace: higress-conformance-infra
|
||||
spec:
|
||||
ingressClassName: higress
|
||||
rules:
|
||||
- host: canary.higress.io
|
||||
http:
|
||||
paths:
|
||||
- path: /weight-100
|
||||
pathType: Exact
|
||||
backend:
|
||||
service:
|
||||
name: infra-backend-v2
|
||||
port:
|
||||
number: 8080
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: ingress-echo-weight-100
|
||||
namespace: higress-conformance-infra
|
||||
spec:
|
||||
ingressClassName: higress
|
||||
rules:
|
||||
- host: canary.higress.io
|
||||
http:
|
||||
paths:
|
||||
- path: /weight-100
|
||||
pathType: Exact
|
||||
backend:
|
||||
service:
|
||||
name: infra-backend-v1
|
||||
port:
|
||||
number: 8080
|
||||
@@ -0,0 +1,130 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/alibaba/higress/test/ingress/conformance/utils/http"
|
||||
"github.com/alibaba/higress/test/ingress/conformance/utils/suite"
|
||||
)
|
||||
|
||||
func init() {
|
||||
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteRequestHeaderControl)
|
||||
}
|
||||
|
||||
var HTTPRouteRequestHeaderControl = suite.ConformanceTest{
|
||||
ShortName: "HTTPRouteRequestHeaderControl",
|
||||
Description: "A single Ingress in the higress-conformance-infra namespace controls the request header.",
|
||||
Manifests: []string{"tests/httproute-request-header-control.yaml"},
|
||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||
testcases := []http.Assertion{
|
||||
{
|
||||
Request: http.AssertionRequest{
|
||||
ActualRequest: http.Request{
|
||||
Path: "/foo1",
|
||||
Host: "foo.com",
|
||||
},
|
||||
ExpectedRequest: &http.ExpectedRequest{
|
||||
Request: http.Request{
|
||||
Headers: map[string]string{
|
||||
"stage": "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Response: http.AssertionResponse{
|
||||
ExpectedResponse: http.Response{
|
||||
StatusCode: 200,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Request: http.AssertionRequest{
|
||||
ActualRequest: http.Request{
|
||||
Path: "/foo2",
|
||||
Host: "foo.com",
|
||||
},
|
||||
ExpectedRequest: &http.ExpectedRequest{
|
||||
Request: http.Request{
|
||||
Headers: map[string]string{
|
||||
"stage": "test",
|
||||
"canary": "true",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Response: http.AssertionResponse{
|
||||
ExpectedResponse: http.Response{
|
||||
StatusCode: 200,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Request: http.AssertionRequest{
|
||||
ActualRequest: http.Request{
|
||||
Path: "/foo3",
|
||||
Host: "foo.com",
|
||||
Headers: map[string]string{
|
||||
"stage": "test",
|
||||
},
|
||||
},
|
||||
ExpectedRequest: &http.ExpectedRequest{
|
||||
Request: http.Request{
|
||||
Headers: map[string]string{
|
||||
"stage": "pro",
|
||||
"canary": "true",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Response: http.AssertionResponse{
|
||||
ExpectedResponse: http.Response{
|
||||
StatusCode: 200,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Request: http.AssertionRequest{
|
||||
ActualRequest: http.Request{
|
||||
Path: "/foo4",
|
||||
Host: "foo.com",
|
||||
Headers: map[string]string{
|
||||
"stage": "test",
|
||||
},
|
||||
},
|
||||
ExpectedRequest: &http.ExpectedRequest{
|
||||
AbsentHeaders: []string{"stage"},
|
||||
},
|
||||
},
|
||||
|
||||
Response: http.AssertionResponse{
|
||||
ExpectedResponse: http.Response{
|
||||
StatusCode: 200,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("Request header control", func(t *testing.T) {
|
||||
for _, testcase := range testcases {
|
||||
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, testcase)
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
# Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
higress.io/request-header-control-add: stage test
|
||||
name: httproute-request-header-control-add-one
|
||||
namespace: higress-conformance-infra
|
||||
spec:
|
||||
ingressClassName: higress
|
||||
rules:
|
||||
- host: "foo.com"
|
||||
http:
|
||||
paths:
|
||||
- pathType: Exact
|
||||
path: "/foo1"
|
||||
backend:
|
||||
service:
|
||||
name: infra-backend-v1
|
||||
port:
|
||||
number: 8080
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
higress.io/request-header-control-add: |
|
||||
stage test
|
||||
canary true
|
||||
name: httproute-request-header-control-add-more
|
||||
namespace: higress-conformance-infra
|
||||
spec:
|
||||
ingressClassName: higress
|
||||
rules:
|
||||
- host: "foo.com"
|
||||
http:
|
||||
paths:
|
||||
- pathType: Exact
|
||||
path: "/foo2"
|
||||
backend:
|
||||
service:
|
||||
name: infra-backend-v1
|
||||
port:
|
||||
number: 8080
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
higress.io/request-header-control-update: stage pro
|
||||
name: httproute-request-header-control-update
|
||||
namespace: higress-conformance-infra
|
||||
spec:
|
||||
ingressClassName: higress
|
||||
rules:
|
||||
- host: "foo.com"
|
||||
http:
|
||||
paths:
|
||||
- pathType: Exact
|
||||
path: "/foo3"
|
||||
backend:
|
||||
service:
|
||||
name: infra-backend-v1
|
||||
port:
|
||||
number: 8080
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
higress.io/request-header-control-remove: stage
|
||||
name: httproute-request-header-control-remove
|
||||
namespace: higress-conformance-infra
|
||||
spec:
|
||||
ingressClassName: higress
|
||||
rules:
|
||||
- host: "foo.com"
|
||||
http:
|
||||
paths:
|
||||
- pathType: Exact
|
||||
path: "/foo4"
|
||||
backend:
|
||||
service:
|
||||
name: infra-backend-v1
|
||||
port:
|
||||
number: 8080
|
||||
@@ -68,6 +68,7 @@ func TestHigressConformanceTests(t *testing.T) {
|
||||
tests.HTTPRouteSameHostAndPath,
|
||||
tests.HTTPRouteCanaryHeaderWithCustomizedHeader,
|
||||
tests.HTTPRouteWhitelistSourceRange,
|
||||
tests.HTTPRouteCanaryWeight,
|
||||
}
|
||||
|
||||
cSuite.Run(t, higressTests)
|
||||
|
||||
Reference in New Issue
Block a user