Compare commits

...

64 Commits

Author SHA1 Message Date
澄潭
8c76ae26bb update console version (#427) 2023-07-10 21:01:09 +08:00
Kent Dong
c1250aec2e fix: Fix the incompatibility issue with Windows using pipeline (#426) 2023-07-10 20:53:54 +08:00
Kent Dong
02bc319eef fix: Remove two debug logs (#424) 2023-07-10 20:16:05 +08:00
Kent Dong
28892cf3ae feat: Add a GitHub action to deploy standalone packages to OSS (#421) 2023-07-08 21:51:59 +08:00
澄潭
bee03a37a4 Release 1.1 (#412) 2023-07-07 17:53:44 +08:00
VIKI ZHAO
2a97921d2b AI proxy plugin (#420) 2023-07-07 15:30:04 +08:00
Jun
d4dbaba760 deepcopy tracing configuration and fix code format (#411) 2023-07-06 10:02:45 +08:00
纪卓志
d718870b65 feat: get_match_config returns with rule_id for stateful plugins (#406) 2023-07-04 19:59:43 +08:00
Jun
b65446fa25 add tracing in higress-config configmap (#409) 2023-07-04 19:59:11 +08:00
Xunzhuo
3fd37abab7 refactor: e2e infras (#407)
Signed-off-by: bitliu <bitliu@tencent.com>
2023-06-30 17:28:10 +08:00
澄潭
81e467b624 support gateway in other ns (#405) 2023-06-29 11:00:02 +08:00
Hinsteny Hisoka
736eea6cf9 feat: Support dubbo group for http2rpc (#404) 2023-06-29 10:55:34 +08:00
rinfx
c32e1ab69b Go WAF Plugin (#400) 2023-06-28 19:25:36 +08:00
纪卓志
fc05a3b256 feat: support rewrite config after parse_rule_config invoked (#401) 2023-06-28 14:22:28 +08:00
澄潭
9fc2760b7d support higress-cn helm index (#399) 2023-06-28 09:42:29 +08:00
纪卓志
be88647752 feat: extract boilerplate code of on_configure to function (DRY) (#396) 2023-06-28 09:42:19 +08:00
澄潭
a56172095a fix debug configz (#398) 2023-06-27 16:42:38 +08:00
Woa
f3270123ba fix: split REGISTRY into BUILDER_REGISTRY and REGISTRY (#395) 2023-06-25 19:52:17 +08:00
Jun
5e2d62406b fix cond unlock (#394) 2023-06-25 09:39:42 +08:00
Kent Dong
39cab9d724 fix: Add docker image URLs to output (#391) 2023-06-21 14:02:32 +08:00
Kent Dong
89865733f6 feat: Support building and pushing Higress docker images via GitHub actions (#388) 2023-06-21 10:18:23 +08:00
Hinsteny Hisoka
1ccf9195b2 Add e2e testing description for wasm-plugin-go (#389) 2023-06-21 10:17:03 +08:00
澄潭
7d2a05ef1c Update README.md 2023-06-19 10:48:48 +08:00
纪卓志
32c2acefda feat: add wasm-rust sdk with say-hello simple extension (#350) 2023-06-19 10:40:53 +08:00
Hinsteny Hisoka
ea7b581e26 Support HTTP/JSON to dubbo (#340) 2023-06-19 10:40:28 +08:00
Kent Dong
ac2f0a5545 feat: Pilot debug APIs support remote anonymous requests (#386) 2023-06-19 09:36:52 +08:00
Kent Dong
51d7124454 release: Prepare for releasing v1.0.1 (#367) 2023-06-16 18:23:17 +08:00
Hinsteny Hisoka
ec6a185adc feat: support only build one plugin to testing specified WasmPlugin (#371)
Co-authored-by: 澄潭 <zty98751@alibaba-inc.com>
2023-06-15 15:21:54 +08:00
Hinsteny Hisoka
f9ffda288b feature: e2e framework supports testing wasm plugins (#369) 2023-06-08 11:00:30 +08:00
Kent Dong
2dbe41324a fix: Fix some compatibility issues related to older versions of K8s (#363) 2023-06-06 18:02:47 +08:00
Qianglin Li
80d6ecfddb feat: support full path regex annotation (#286)
Signed-off-by: charlie <qianglin98@qq.com>
2023-06-02 18:56:22 +08:00
Ink33
b23fae7a12 ci: enable building files caching (#323)
Signed-off-by: Ink33 <Ink33@smlk.org>
Co-authored-by: Xunzhuo <bitliu@tencent.com>
2023-06-01 11:35:37 +08:00
WeixinX
2c19d97252 feat: Add e2e testcases for 'auth-tls-secret' and 'ssl-cipher' (#354) 2023-05-30 11:08:31 +08:00
Xunzhuo
efd7ccd5fe fix: e2e refers (#345) 2023-05-26 12:02:39 +08:00
Xunzhuo
81fd0d6386 fix: e2e refers (#345) 2023-05-26 12:01:50 +08:00
Jun
176ddc6963 Plugin cors (#349) 2023-05-25 19:21:21 +08:00
WeixinX
44637c2449 fix: Fix e2e 'request header control' and add some testcases (#352) 2023-05-25 15:51:39 +08:00
澄潭
3e68ae75d1 update version to 1.0.0 (#348) 2023-05-22 20:58:19 +08:00
Ffyyt
18ad817edb Add goproxy environment variable for build-image (#347) 2023-05-22 20:57:53 +08:00
Xunzhuo
d48e0ce773 e2e: rename some cases name to make it sync with others (#344) 2023-05-22 11:05:22 +08:00
Kent Dong
9734ffeb3e feat: Refactor skywalking configuration structure (#333) 2023-05-21 15:44:07 +08:00
Ffyyt
1421ce8667 e2e:add test for ssl redirect annotations (#341) 2023-05-21 15:20:00 +08:00
Jun
625c06e58f get upstream serviceSource from RouteCluster information and update docs (#337) 2023-05-19 10:40:24 +08:00
澄潭
e4a47dfb46 compatiable with tinygo 0.25 (#330) 2023-05-15 19:22:05 +08:00
WeixinX
6b483189ac docs: fix some plugin READMEs (#327) 2023-05-12 14:23:58 +08:00
刘晓瑞
74ad9a555a extend wasm go sdk with OnHttpStreamDone (#325) 2023-05-10 14:58:04 +08:00
Kent Dong
f6e181ecb6 fix: Sync Chart.lock file (#321) 2023-05-09 21:14:22 +08:00
Kent Dong
30a5b2ab2b feat: Upgrade Higress Console to v1.0.0-rc.2 (#320) 2023-05-09 17:24:15 +08:00
澄潭
91a23cc27e Update README_EN.md 2023-05-09 16:49:51 +08:00
澄潭
51e515d53e Update README.md 2023-05-09 16:49:10 +08:00
Kent Dong
67274bfa0d feat: Simplify Dockerfile for wasm-go-builder (#319) 2023-05-09 09:51:28 +08:00
Kent Dong
4f24979579 fix: Use tinygo 0.25.0 (#309) 2023-05-08 19:28:23 +08:00
Tom Kerkhove
1f4bf8e0b2 feat: Provide link to Higress in Helm chart (#318) 2023-05-08 17:03:00 +08:00
Kent Dong
05608128e2 feat: Use the 1.0.0-rc version of Higress Console (#317) 2023-05-08 10:47:11 +08:00
Kent Dong
fbdc301f94 feat: Add LICENSE and README.md to helm charts (#315) 2023-05-08 10:46:54 +08:00
Ffyyt
cf69234eff Add environment variable goproxy (#313) 2023-05-08 10:46:30 +08:00
Kent Dong
461f7ed675 fix: Fix the NEW ISSUE link in CONTRIBUTING pages (#314) 2023-05-06 13:59:01 +08:00
澄潭
7e358eb1db add commercial info (#310) 2023-05-04 15:57:05 +08:00
jiahao zhang
daffd18674 add plugin gw-error-format (#116)
Co-authored-by: 澄潭 <zty98751@alibaba-inc.com>
2023-05-04 09:42:41 +08:00
Kent Dong
48978e5135 feat: Use new wasm-go-builder with oras in the Makefile (#308) 2023-04-28 10:26:56 +08:00
Jun
311d5c21c2 Add Plugin de-graphql (#303) 2023-04-27 18:45:19 +08:00
Xunzhuo
e2b4a52c9e Add myself to pkg owners to take reviews of hgctl (#302)
Signed-off-by: bitliu <bitliu@tencent.com>
2023-04-27 18:39:30 +08:00
Kent Dong
51cd5e830e feat: Add oras binary into wasm-go-builder image for OCI image building (#304) 2023-04-27 18:39:12 +08:00
Ffyyt
10d2b41ad5 README translation of the hmac-auth plugin (#306) 2023-04-27 18:38:42 +08:00
274 changed files with 34802 additions and 628 deletions

View File

@@ -23,6 +23,28 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Setup Golang Caches
uses: actions/cache@v3
with:
path: |-
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ github.run_id }}
restore-keys: ${{ runner.os }}-go
- name: Setup Submodule Caches
uses: actions/cache@v3
with:
path: |-
envoy
istio
external
.git/modules
key: ${{ runner.os }}-submodules-${{ github.run_id }}
restore-keys: ${{ runner.os }}-submodules
- run: git stash # restore patch
# test
- name: Run Coverage Tests
run: GOPROXY="https://proxy.golang.org,direct" make go.test.coverage
@@ -38,15 +60,37 @@ jobs:
runs-on: ubuntu-latest
needs: [lint,coverage-test]
steps:
- name: "Checkout ${{ github.ref }}"
uses: actions/checkout@v3
with:
fetch-depth: 2
- name: "Setup Go"
uses: actions/setup-go@v3
with:
go-version: 1.19
- name: "checkout ${{ github.ref }}"
uses: actions/checkout@v3
- name: Setup Golang Caches
uses: actions/cache@v3
with:
fetch-depth: 2
path: |-
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ github.run_id }}
restore-keys: ${{ runner.os }}-go
- name: Setup Submodule Caches
uses: actions/cache@v3
with:
path: |-
envoy
istio
external
.git/modules
key: ${{ runner.os }}-submodules-${{ github.run_id }}
restore-keys: ${{ runner.os }}-submodules
- run: git stash # restore patch
- name: "Build Higress Binary"
run: GOPROXY="https://proxy.golang.org,direct" make build
@@ -63,20 +107,82 @@ jobs:
steps:
- uses: actions/checkout@v3
ingress-conformance-test:
higress-conformance-test:
runs-on: ubuntu-latest
needs: [build]
steps:
- uses: actions/checkout@v3
- name: "Setup Go"
uses: actions/setup-go@v3
with:
go-version: 1.19
- name: Setup Golang Caches
uses: actions/cache@v3
with:
path: |-
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ github.run_id }}
restore-keys: |
${{ runner.os }}-go
- name: Setup Submodule Caches
uses: actions/cache@v3
with:
path: |-
envoy
istio
external
.git/modules
key: ${{ runner.os }}-submodules-${{ github.run_id }}
restore-keys: ${{ runner.os }}-submodules
- run: git stash # restore patch
- name: "Run Higress E2E Conformance Tests"
run: GOPROXY="https://proxy.golang.org,direct" make higress-conformance-test
higress-wasmplugin-test:
runs-on: ubuntu-latest
needs: [build]
steps:
- uses: actions/checkout@v3
- name: "Run Ingress Conformance Tests"
run: GOPROXY="https://proxy.golang.org,direct" make ingress-conformance-test
- name: "Setup Go"
uses: actions/setup-go@v3
with:
go-version: 1.19
- name: Setup Golang Caches
uses: actions/cache@v3
with:
path: |-
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ github.run_id }}
restore-keys: |
${{ runner.os }}-go
- name: Setup Submodule Caches
uses: actions/cache@v3
with:
path: |-
envoy
istio
external
.git/modules
key: ${{ runner.os }}-submodules-${{ github.run_id }}
restore-keys: ${{ runner.os }}-submodules
- run: git stash # restore patch
- name: "Run Ingress WasmPlugins Tests"
run: GOPROXY="https://proxy.golang.org,direct" make higress-wasmplugin-test
publish:
runs-on: ubuntu-latest
needs: [ingress-conformance-test,gateway-conformance-test]
needs: [higress-conformance-test,gateway-conformance-test,higress-wasmplugin-test]
steps:
- uses: actions/checkout@v3

View File

@@ -0,0 +1,144 @@
name: Build Docker Images and Push to Image Registry
on:
push:
tags:
- "v*.*.*"
workflow_dispatch: ~
jobs:
build-controller-image:
runs-on: ubuntu-latest
environment:
name: image-registry-controller
env:
CONTROLLER_IMAGE_REGISTRY: ${{ vars.IMAGE_REGISTRY || 'higress-registry.cn-hangzhou.cr.aliyuncs.com' }}
CONTROLLER_IMAGE_NAME: ${{ vars.CONTROLLER_IMAGE_NAME || 'higress/higress' }}
steps:
- name: "Checkout ${{ github.ref }}"
uses: actions/checkout@v3
with:
fetch-depth: 1
- name: "Setup Go"
uses: actions/setup-go@v3
with:
go-version: 1.19
- name: Setup Golang Caches
uses: actions/cache@v3
with:
path: |-
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ github.run_id }}
restore-keys: ${{ runner.os }}-go
- name: Setup Submodule Caches
uses: actions/cache@v3
with:
path: |-
envoy
istio
external
.git/modules
key: ${{ runner.os }}-submodules-${{ github.run_id }}
restore-keys: ${{ runner.os }}-submodules
- name: Calculate Docker metadata
id: docker-meta
uses: docker/metadata-action@v4
with:
images: |
${{ env.CONTROLLER_IMAGE_REGISTRY }}/${{ env.CONTROLLER_IMAGE_NAME }}
tags: |
type=sha
type=ref,event=tag
type=semver,pattern={{version}}
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
- name: Login to Docker Registry
uses: docker/login-action@v2
with:
registry: ${{ env.CONTROLLER_IMAGE_REGISTRY }}
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Build Docker Image and Push
run: |
GOPROXY="https://proxy.golang.org,direct" make docker-build
BUILT_IMAGE="higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/higress"
readarray -t IMAGES <<< "${{ steps.docker-meta.outputs.tags }}"
for image in ${IMAGES[@]}; do
echo "Image: $image"
docker tag $BUILT_IMAGE:$GITHUB_SHA $image
docker push $image
done
build-pilot-image:
runs-on: ubuntu-latest
environment:
name: image-registry-pilot
env:
PILOT_IMAGE_REGISTRY: ${{ vars.IMAGE_REGISTRY || 'higress-registry.cn-hangzhou.cr.aliyuncs.com' }}
PILOT_IMAGE_NAME: ${{ vars.PILOT_IMAGE_NAME || 'higress/pilot' }}
steps:
- name: "Checkout ${{ github.ref }}"
uses: actions/checkout@v3
with:
fetch-depth: 1
- name: "Setup Go"
uses: actions/setup-go@v3
with:
go-version: 1.19
- name: Setup Golang Caches
uses: actions/cache@v3
with:
path: |-
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ github.run_id }}
restore-keys: ${{ runner.os }}-go
- name: Setup Submodule Caches
uses: actions/cache@v3
with:
path: |-
envoy
istio
external
.git/modules
key: ${{ runner.os }}-submodules-${{ github.run_id }}
restore-keys: ${{ runner.os }}-submodules
- name: Calculate Docker metadata
id: docker-meta
uses: docker/metadata-action@v4
with:
images: |
${{ env.PILOT_IMAGE_REGISTRY }}/${{ env.PILOT_IMAGE_NAME }}
tags: |
type=sha
type=ref,event=tag
type=semver,pattern={{version}}
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
- name: Login to Docker Registry
uses: docker/login-action@v2
with:
registry: ${{ env.PILOT_IMAGE_REGISTRY }}
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Build Docker Image and Push
run: |
sudo GOPROXY="https://proxy.golang.org,direct" make build-istio
BUILT_IMAGE="higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/pilot"
readarray -t IMAGES <<< "${{ steps.docker-meta.outputs.tags }}"
for image in ${IMAGES[@]}; do
echo "Image: $image"
docker tag $BUILT_IMAGE:$GITHUB_SHA $image
docker push $image
done

View File

@@ -0,0 +1,37 @@
name: Deploy Standalone to OSS
on:
push:
tags:
- "v*.*.*"
workflow_dispatch: ~
jobs:
deploy-to-oss:
runs-on: ubuntu-latest
environment:
name: oss
steps:
# Step 1
- name: Checkout
uses: actions/checkout@v3
# Step 2
- id: package
name: Prepare Standalone Package
run: |
mkdir ./artifact
cp ./tools/get-higress.sh ./artifact
LOCAL_RELEASE_URL="https://github.com/higress-group/higress-standalone/releases"
VERSION=$(curl -Ls $LOCAL_RELEASE_URL | grep 'href="/higress-group/higress-standalone/releases/tag/v[0-9]*.[0-9]*.[0-9]*\"' | sed -E 's/.*\/higress-group\/higress-standalone\/releases\/tag\/(v[0-9\.]+)".*/\1/g' | head -1)
DOWNLOAD_URL="https://github.com/higress-group/higress-standalone/archive/refs/tags/${VERSION}.tar.gz"
curl -SsL "$DOWNLOAD_URL" -o "./artifact/higress-${VERSION}.tar.gz"
echo -n "$VERSION" > ./artifact/VERSION
echo "Version=$VERSION"
# Step 3
- name: Upload to OSS
uses: doggycool/ossutil-github-action@master
with:
ossArgs: 'cp -r -u ./artifact/ oss://higress-website-cn-hongkong/standalone/'
accessKey: ${{ secrets.ACCESS_KEYID }}
accessSecret: ${{ secrets.ACCESS_KEYSECRET }}
endpoint: oss-cn-hongkong.aliyuncs.com

View File

@@ -4,10 +4,13 @@ on:
push:
tags:
- "v*.*.*"
workflow_dispatch: ~
jobs:
deploy-to-oss:
runs-on: ubuntu-latest
environment:
name: oss
steps:
# Step 1
- name: Checkout
@@ -38,6 +41,8 @@ jobs:
helmv3 dependency build helm/higress
helmv3 package helm/higress --debug --app-version ${{steps.calc-version.outputs.version}} --version ${{steps.calc-version.outputs.version}} -d ./artifact
helmv3 repo index --url https://higress.io/helm-charts/ --merge ./artifact/index.yaml ./artifact
cp ./artifact/index.yaml ./artifact/cn-index.yaml
sed -i 's/higress\.io/higress\.cn/g' ./artifact/cn-index.yaml
# Step 5
- name: Upload to OSS
uses: doggycool/ossutil-github-action@master

4
.gitignore vendored
View File

@@ -12,4 +12,6 @@ bazel-out
bazel-testlogs
bazel-wasm-cpp
tools/bin/
helm/**/charts/**.tgz
helm/**/charts/**.tgz
target/
tools/hack/cluster.conf

View File

@@ -1,7 +1,7 @@
/api @johnlanni
/envoy @gengleilei @johnlanni @Lynskylate
/istio @SpecialYang @johnlanni
/pkg @SpecialYang @johnlanni @Charlie17Li
/pkg @SpecialYang @johnlanni @Charlie17Li @Xunzhuo
/plugins @johnlanni
/registry @NameHaibinZhang @johnlanni
/test @Xunzhuo

View File

@@ -27,7 +27,7 @@
## 报告一般问题
老实说,我们把每一个 Higress 用户都视为非常善良的贡献者。在体验了 Higress 之后,您可能会对项目有一些反馈。然后随时通过 [NEW ISSUE](https://github. com/alibaba/higress/issues/new/choose)打开一个问题。
老实说,我们把每一个 Higress 用户都视为非常善良的贡献者。在体验了 Higress 之后,您可能会对项目有一些反馈。然后随时通过 [NEW ISSUE](https://github.com/alibaba/higress/issues/new/choose)打开一个问题。
因为我们在一个分布式的方式合作项目Higress我们欣赏写得很好的详细的准确的问题报告。为了让沟通更高效我们希望每个人都可以搜索您的问题是否在搜索列表中。如果您发现它存在请在现有问题下的评论中添加您的详细信息而不是打开一个全新的问题。

View File

@@ -28,8 +28,7 @@ Security issues are always treated seriously. As our usual principle, we discour
## Reporting general issues
To be honest, we regard every user of Higress as a very kind contributor. After experiencing Higress, you may have
some feedback for the project. Then feel free to open an issue via [NEW ISSUE](https://github.
com/alibaba/higress/issues/new/choose).
some feedback for the project. Then feel free to open an issue via [NEW ISSUE](https://github.com/alibaba/higress/issues/new/choose).
Since we collaborate project Higress in a distributed way, we appreciate **WELL-WRITTEN**, **DETAILED**, **EXPLICIT** issue reports. To make the communication more efficient, we wish everyone could search if your issue is an existing one in the searching list. If you find it existing, please add your details in comments under the existing issue instead of opening a brand new one.

View File

@@ -128,6 +128,9 @@ build-gateway: prebuild external/package/envoy.tar.gz
build-istio: prebuild
cd external/istio; rm -rf out; GOOS_LOCAL=linux TARGET_OS=linux TARGET_ARCH=amd64 BUILD_WITH_CONTAINER=1 DOCKER_BUILD_VARIANTS=default DOCKER_TARGETS="docker.pilot" make docker
build-wasmplugins:
./tools/hack/build-wasm-plugins.sh
pre-install:
cp api/kubernetes/customresourcedefinitions.gen.yaml helm/core/crds
@@ -139,12 +142,15 @@ install: pre-install
cd helm/higress; helm dependency build
helm install higress helm/higress -n higress-system --create-namespace --set 'global.local=true'
ENVOY_LATEST_IMAGE_TAG ?= 1.0.0-rc
ISTIO_LATEST_IMAGE_TAG ?= 1.0.0-rc
ENVOY_LATEST_IMAGE_TAG ?= 1.1.0
ISTIO_LATEST_IMAGE_TAG ?= 1.1.0
install-dev: pre-install
helm install higress helm/core -n higress-system --create-namespace --set 'controller.tag=$(TAG)' --set 'gateway.replicas=1' --set 'gateway.tag=$(ENVOY_LATEST_IMAGE_TAG)' --set 'global.local=true'
install-dev-wasmplugin: build-wasmplugins pre-install
helm install higress helm/core -n higress-system --create-namespace --set 'controller.tag=$(TAG)' --set 'gateway.replicas=1' --set 'gateway.tag=$(ENVOY_LATEST_IMAGE_TAG)' --set 'global.local=true' --set 'global.volumeWasmPlugins=true'
uninstall:
helm uninstall higress -n higress-system
@@ -193,9 +199,13 @@ include tools/lint.mk
.PHONY: gateway-conformance-test
gateway-conformance-test:
# ingress-conformance-test runs ingress api conformance tests.
.PHONY: ingress-conformance-test
ingress-conformance-test: $(tools/kind) delete-cluster create-cluster docker-build kube-load-image install-dev run-ingress-e2e-test delete-cluster
# higress-conformance-test runs ingress api conformance tests.
.PHONY: higress-conformance-test
higress-conformance-test: $(tools/kind) delete-cluster create-cluster docker-build kube-load-image install-dev run-higress-e2e-test delete-cluster
# higress-wasmplugin-test runs ingress wasmplugin tests.
.PHONY: higress-wasmplugin-test
higress-wasmplugin-test: $(tools/kind) delete-cluster create-cluster docker-build kube-load-image install-dev-wasmplugin run-higress-e2e-test-wasmplugin delete-cluster
# create-cluster creates a kube cluster with kind.
.PHONY: create-cluster
@@ -208,16 +218,33 @@ delete-cluster: $(tools/kind) ## Delete kind cluster.
$(tools/kind) delete cluster --name higress
# kube-load-image loads a local built docker image into kube cluster.
# dubbo-provider-demo和nacos-standlone-rc3的镜像已经上传到阿里云镜像库第一次需要先拉到本地
# docker pull registry.cn-hangzhou.aliyuncs.com/hinsteny/dubbo-provider-demo:0.0.1
# docker pull registry.cn-hangzhou.aliyuncs.com/hinsteny/nacos-standlone-rc3:1.0.0-RC3
.PHONY: kube-load-image
kube-load-image: $(tools/kind) ## Install the Higress image to a kind cluster using the provided $IMAGE and $TAG.
tools/hack/kind-load-image.sh higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/higress $(TAG)
tools/hack/docker-pull-image.sh registry.cn-hangzhou.aliyuncs.com/hinsteny/dubbo-provider-demo 0.0.1
tools/hack/docker-pull-image.sh registry.cn-hangzhou.aliyuncs.com/hinsteny/nacos-standlone-rc3 1.0.0-RC3
tools/hack/kind-load-image.sh registry.cn-hangzhou.aliyuncs.com/hinsteny/dubbo-provider-demo 0.0.1
tools/hack/kind-load-image.sh registry.cn-hangzhou.aliyuncs.com/hinsteny/nacos-standlone-rc3 1.0.0-RC3
# run-ingress-e2e-test starts to run ingress e2e tests.
.PHONY: run-ingress-e2e-test
run-ingress-e2e-test:
# run-higress-e2e-test starts to run ingress e2e tests.
.PHONY: run-higress-e2e-test
run-higress-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=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=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
go test -v -tags conformance ./test/e2e/e2e_test.go --ingress-class=higress --debug=true
# run-higress-e2e-test starts to run ingress e2e tests.
.PHONY: run-higress-e2e-test-wasmplugin
run-higress-e2e-test-wasmplugin:
@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=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=10m -n higress-system deployment/higress-gateway --for=condition=Available
go test -v -tags conformance ./test/e2e/e2e_test.go -isWasmPluginTest=true --ingress-class=higress --debug=true

View File

@@ -8,9 +8,10 @@
[![license](https://img.shields.io/github/license/alibaba/higress.svg)](https://www.apache.org/licenses/LICENSE-2.0.html)
[**官网**](https://higress.io/) &nbsp; |
&nbsp; [**文档**](https://higress.io/zh-cn/docs/overview/what-is-higress.html) &nbsp; |
&nbsp; [**博客**](https://higress.io/zh-cn/blog/index.html) &nbsp; |
&nbsp; [**开发指引**](https://higress.io/zh-cn/docs/dev/code.html) &nbsp;
&nbsp; [**文档**](https://higress.io/zh-cn/docs/overview/what-is-higress) &nbsp; |
&nbsp; [**博客**](https://higress.io/zh-cn/blog) &nbsp; |
&nbsp; [**开发指引**](https://higress.io/zh-cn/docs/developers/developers_dev) &nbsp; |
&nbsp; [**Higress 企业版**](https://www.aliyun.com/product/aliware/mse?spm=higress-website.topbar.0.0.0) &nbsp;
<p>
@@ -20,7 +21,7 @@
Higress 是基于阿里内部两年多的 Envoy Gateway 实践沉淀,以开源 [Istio](https://github.com/istio/istio) 与 [Envoy](https://github.com/envoyproxy/envoy) 为核心构建的下一代云原生网关。Higress 实现了安全防护网关、流量网关、微服务网关三层网关合一,可以显著降低网关的部署和运维成本。
![arch](https://img.alicdn.com/imgextra/i4/O1CN01OgGP1728t0xeRfRYJ_!!6000000007989-0-tps-1726-1366.jpg)
![arch](https://img.alicdn.com/imgextra/i1/O1CN01iO9ph825juHbOIg75_!!6000000007563-2-tps-2483-2024.png)
## Summary
@@ -124,7 +125,7 @@ Higress 是基于阿里内部两年多的 Envoy Gateway 实践沉淀,以开源
社区交流群:
![image](https://img.alicdn.com/imgextra/i1/O1CN01d7LmWu1rMB71rfRhA_!!6000000005616-2-tps-720-405.png)
![image](https://img.alicdn.com/imgextra/i1/O1CN01KWonlE1DkpqaYVTiC_!!6000000000255-0-tps-720-405.jpg)
开发者群:

View File

@@ -4,6 +4,16 @@
Next-generation Cloud Native Gateway
</h1>
[![Build Status](https://github.com/alibaba/higress/workflows/build%20and%20codecov/badge.svg?branch=main)](https://github.com/alibaba/higress/actions)
[![license](https://img.shields.io/github/license/alibaba/higress.svg)](https://www.apache.org/licenses/LICENSE-2.0.html)
[**Official Site**](https://higress.io/en-us/) &nbsp; |
&nbsp; [**Docs**](https://higress.io/en-us/docs/overview/what-is-higress) &nbsp; |
&nbsp; [**Blog**](https://higress.io/en-us/blog) &nbsp; |
&nbsp; [**Developer**](https://higress.io/en-us/docs/developers/developers_dev) &nbsp; |
&nbsp; [**Higress in Cloud**](https://www.alibabacloud.com/product/microservices-engine?spm=higress-website.topbar.0.0.0) &nbsp;
<p>
English | <a href="README.md">中文<a/>
</p>
@@ -13,7 +23,7 @@ Higress is a next-generation cloud-native gateway based on Alibaba's internal ga
Powered by [Istio](https://github.com/istio/istio) and [Envoy](https://github.com/envoyproxy/envoy), Higress realizes the integration of the triple gateway architecture of traffic gateway, microservice gateway and security gateway, thereby greatly reducing the costs of deployment, operation and maintenance.
<h1 align="center">
<img src="https://img.alicdn.com/imgextra/i1/O1CN01vnNawh26mU5C9py9w_!!6000000007704-0-tps-1726-1366.jpg" alt="Higress Architecture">
<img src="https://img.alicdn.com/imgextra/i1/O1CN01iO9ph825juHbOIg75_!!6000000007563-2-tps-2483-2024.png" alt="Higress Architecture">
</h1>

View File

@@ -1 +1 @@
v1.0.0-rc
v1.1.0

View File

@@ -104,6 +104,88 @@ spec:
subresources:
status: {}
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
"helm.sh/resource-policy": keep
name: http2rpcs.networking.higress.io
spec:
group: networking.higress.io
names:
categories:
- higress-io
kind: Http2Rpc
listKind: Http2RpcList
plural: http2rpcs
singular: http2rpc
scope: Namespaced
versions:
- name: v1
schema:
openAPIV3Schema:
properties:
spec:
oneOf:
- not:
anyOf:
- required:
- dubbo
- required:
- grpc
- required:
- dubbo
- required:
- grpc
properties:
dubbo:
properties:
group:
type: string
methods:
items:
properties:
headersAttach:
type: string
httpMethods:
items:
type: string
type: array
httpPath:
type: string
params:
items:
properties:
paramKey:
type: string
paramSource:
type: string
paramType:
type: string
type: object
type: array
serviceMethod:
type: string
type: object
type: array
service:
type: string
version:
type: string
type: object
grpc:
type: object
type: object
status:
type: object
x-kubernetes-preserve-unknown-fields: true
type: object
served: true
storage: true
subresources:
status: {}
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,74 @@
// 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.
syntax = "proto3";
import "google/api/field_behavior.proto";
// $schema: higress.networking.v1.Http2Rpc
// $title: Http2Rpc
// $description: Configuration affecting service discovery from multi registries
// $mode: none
package higress.networking.v1;
option go_package = "github.com/alibaba/higress/api/networking/v1";
// <!-- crd generation tags
// +cue-gen:Http2Rpc:groupName:networking.higress.io
// +cue-gen:Http2Rpc:version:v1
// +cue-gen:Http2Rpc:storageVersion
// +cue-gen:Http2Rpc:annotations:helm.sh/resource-policy=keep
// +cue-gen:Http2Rpc:subresource:status
// +cue-gen:Http2Rpc:scope:Namespaced
// +cue-gen:Http2Rpc:resource:categories=higress-io,plural=Http2Rpcs
// +cue-gen:Http2Rpc:preserveUnknownFields:false
// -->
//
// <!-- go code generation tags
// +kubetype-gen
// +kubetype-gen:groupVersion=networking.higress.io/v1
// +genclient
// +k8s:deepcopy-gen=true
// -->
message Http2Rpc {
oneof destination {
DubboService dubbo = 1;
GrpcService grpc = 2;
}
}
message DubboService {
string service = 1 [(google.api.field_behavior) = REQUIRED];
string version = 2 [(google.api.field_behavior) = REQUIRED];
string group = 3 [(google.api.field_behavior) = OPTIONAL];
repeated Method methods = 4 [(google.api.field_behavior) = REQUIRED];
}
message Method {
string service_method = 1 [(google.api.field_behavior) = REQUIRED];
string headers_attach = 2 [(google.api.field_behavior) = OPTIONAL];
string http_path = 3 [(google.api.field_behavior) = REQUIRED];
repeated string http_methods = 4 [(google.api.field_behavior) = REQUIRED];
repeated Param params = 5;
}
message Param {
string param_source = 1 [(google.api.field_behavior) = REQUIRED];
string param_key = 2 [(google.api.field_behavior) = REQUIRED];
string param_type = 3 [(google.api.field_behavior) = REQUIRED];
}
message GrpcService {
}

View File

@@ -0,0 +1,121 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: networking/v1/http_2_rpc.proto
package v1
import (
fmt "fmt"
proto "github.com/gogo/protobuf/proto"
_ "istio.io/gogo-genproto/googleapis/google/api"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// DeepCopyInto supports using Http2Rpc within kubernetes types, where deepcopy-gen is used.
func (in *Http2Rpc) DeepCopyInto(out *Http2Rpc) {
p := proto.Clone(in).(*Http2Rpc)
*out = *p
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Http2Rpc. Required by controller-gen.
func (in *Http2Rpc) DeepCopy() *Http2Rpc {
if in == nil {
return nil
}
out := new(Http2Rpc)
in.DeepCopyInto(out)
return out
}
// DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new Http2Rpc. Required by controller-gen.
func (in *Http2Rpc) DeepCopyInterface() interface{} {
return in.DeepCopy()
}
// DeepCopyInto supports using DubboService within kubernetes types, where deepcopy-gen is used.
func (in *DubboService) DeepCopyInto(out *DubboService) {
p := proto.Clone(in).(*DubboService)
*out = *p
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DubboService. Required by controller-gen.
func (in *DubboService) DeepCopy() *DubboService {
if in == nil {
return nil
}
out := new(DubboService)
in.DeepCopyInto(out)
return out
}
// DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new DubboService. Required by controller-gen.
func (in *DubboService) DeepCopyInterface() interface{} {
return in.DeepCopy()
}
// DeepCopyInto supports using Method within kubernetes types, where deepcopy-gen is used.
func (in *Method) DeepCopyInto(out *Method) {
p := proto.Clone(in).(*Method)
*out = *p
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Method. Required by controller-gen.
func (in *Method) DeepCopy() *Method {
if in == nil {
return nil
}
out := new(Method)
in.DeepCopyInto(out)
return out
}
// DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new Method. Required by controller-gen.
func (in *Method) DeepCopyInterface() interface{} {
return in.DeepCopy()
}
// DeepCopyInto supports using Param within kubernetes types, where deepcopy-gen is used.
func (in *Param) DeepCopyInto(out *Param) {
p := proto.Clone(in).(*Param)
*out = *p
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Param. Required by controller-gen.
func (in *Param) DeepCopy() *Param {
if in == nil {
return nil
}
out := new(Param)
in.DeepCopyInto(out)
return out
}
// DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new Param. Required by controller-gen.
func (in *Param) DeepCopyInterface() interface{} {
return in.DeepCopy()
}
// DeepCopyInto supports using GrpcService within kubernetes types, where deepcopy-gen is used.
func (in *GrpcService) DeepCopyInto(out *GrpcService) {
p := proto.Clone(in).(*GrpcService)
*out = *p
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GrpcService. Required by controller-gen.
func (in *GrpcService) DeepCopy() *GrpcService {
if in == nil {
return nil
}
out := new(GrpcService)
in.DeepCopyInto(out)
return out
}
// DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new GrpcService. Required by controller-gen.
func (in *GrpcService) DeepCopyInterface() interface{} {
return in.DeepCopy()
}

View File

@@ -0,0 +1,78 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: networking/v1/http_2_rpc.proto
package v1
import (
bytes "bytes"
fmt "fmt"
github_com_gogo_protobuf_jsonpb "github.com/gogo/protobuf/jsonpb"
proto "github.com/gogo/protobuf/proto"
_ "istio.io/gogo-genproto/googleapis/google/api"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// MarshalJSON is a custom marshaler for Http2Rpc
func (this *Http2Rpc) MarshalJSON() ([]byte, error) {
str, err := Http_2RpcMarshaler.MarshalToString(this)
return []byte(str), err
}
// UnmarshalJSON is a custom unmarshaler for Http2Rpc
func (this *Http2Rpc) UnmarshalJSON(b []byte) error {
return Http_2RpcUnmarshaler.Unmarshal(bytes.NewReader(b), this)
}
// MarshalJSON is a custom marshaler for DubboService
func (this *DubboService) MarshalJSON() ([]byte, error) {
str, err := Http_2RpcMarshaler.MarshalToString(this)
return []byte(str), err
}
// UnmarshalJSON is a custom unmarshaler for DubboService
func (this *DubboService) UnmarshalJSON(b []byte) error {
return Http_2RpcUnmarshaler.Unmarshal(bytes.NewReader(b), this)
}
// MarshalJSON is a custom marshaler for Method
func (this *Method) MarshalJSON() ([]byte, error) {
str, err := Http_2RpcMarshaler.MarshalToString(this)
return []byte(str), err
}
// UnmarshalJSON is a custom unmarshaler for Method
func (this *Method) UnmarshalJSON(b []byte) error {
return Http_2RpcUnmarshaler.Unmarshal(bytes.NewReader(b), this)
}
// MarshalJSON is a custom marshaler for Param
func (this *Param) MarshalJSON() ([]byte, error) {
str, err := Http_2RpcMarshaler.MarshalToString(this)
return []byte(str), err
}
// UnmarshalJSON is a custom unmarshaler for Param
func (this *Param) UnmarshalJSON(b []byte) error {
return Http_2RpcUnmarshaler.Unmarshal(bytes.NewReader(b), this)
}
// MarshalJSON is a custom marshaler for GrpcService
func (this *GrpcService) MarshalJSON() ([]byte, error) {
str, err := Http_2RpcMarshaler.MarshalToString(this)
return []byte(str), err
}
// UnmarshalJSON is a custom unmarshaler for GrpcService
func (this *GrpcService) UnmarshalJSON(b []byte) error {
return Http_2RpcUnmarshaler.Unmarshal(bytes.NewReader(b), this)
}
var (
Http_2RpcMarshaler = &github_com_gogo_protobuf_jsonpb.Marshaler{}
Http_2RpcUnmarshaler = &github_com_gogo_protobuf_jsonpb.Unmarshaler{AllowUnknownFields: true}
)

View File

@@ -41,6 +41,8 @@ func Resource(resource string) schema.GroupResource {
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&Http2Rpc{},
&Http2RpcList{},
&McpBridge{},
&McpBridgeList{},
)

View File

@@ -25,6 +25,48 @@ import (
// please upgrade the proto package
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// <!-- crd generation tags
// +cue-gen:Http2Rpc:groupName:networking.higress.io
// +cue-gen:Http2Rpc:version:v1
// +cue-gen:Http2Rpc:storageVersion
// +cue-gen:Http2Rpc:annotations:helm.sh/resource-policy=keep
// +cue-gen:Http2Rpc:subresource:status
// +cue-gen:Http2Rpc:scope:Namespaced
// +cue-gen:Http2Rpc:resource:categories=higress-io,plural=Http2Rpcs
// +cue-gen:Http2Rpc:preserveUnknownFields:false
// -->
//
// <!-- go code generation tags
// +kubetype-gen
// +kubetype-gen:groupVersion=networking.higress.io/v1
// +genclient
// +k8s:deepcopy-gen=true
// -->
type Http2Rpc struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// Spec defines the implementation of this definition.
// +optional
Spec networkingv1.Http2Rpc `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
Status v1alpha1.IstioStatus `json:"status"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// Http2RpcList is a collection of Http2Rpcs.
type Http2RpcList struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
Items []Http2Rpc `json:"items" protobuf:"bytes,2,rep,name=items"`
}
// please upgrade the proto package
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// <!-- crd generation tags
// +cue-gen:McpBridge:groupName:networking.higress.io
// +cue-gen:McpBridge:version:v1

View File

@@ -23,6 +23,67 @@ import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Http2Rpc) DeepCopyInto(out *Http2Rpc) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Http2Rpc.
func (in *Http2Rpc) DeepCopy() *Http2Rpc {
if in == nil {
return nil
}
out := new(Http2Rpc)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Http2Rpc) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Http2RpcList) DeepCopyInto(out *Http2RpcList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Http2Rpc, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Http2RpcList.
func (in *Http2RpcList) DeepCopy() *Http2RpcList {
if in == nil {
return nil
}
out := new(Http2RpcList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Http2RpcList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *McpBridge) DeepCopyInto(out *McpBridge) {
*out = *in

View File

@@ -0,0 +1,140 @@
// 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.
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
"context"
networkingv1 "github.com/alibaba/higress/client/pkg/apis/networking/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
)
// FakeHttp2Rpcs implements Http2RpcInterface
type FakeHttp2Rpcs struct {
Fake *FakeNetworkingV1
ns string
}
var http2rpcsResource = schema.GroupVersionResource{Group: "networking.higress.io", Version: "v1", Resource: "http2rpcs"}
var http2rpcsKind = schema.GroupVersionKind{Group: "networking.higress.io", Version: "v1", Kind: "Http2Rpc"}
// Get takes name of the http2Rpc, and returns the corresponding http2Rpc object, and an error if there is any.
func (c *FakeHttp2Rpcs) Get(ctx context.Context, name string, options v1.GetOptions) (result *networkingv1.Http2Rpc, err error) {
obj, err := c.Fake.
Invokes(testing.NewGetAction(http2rpcsResource, c.ns, name), &networkingv1.Http2Rpc{})
if obj == nil {
return nil, err
}
return obj.(*networkingv1.Http2Rpc), err
}
// List takes label and field selectors, and returns the list of Http2Rpcs that match those selectors.
func (c *FakeHttp2Rpcs) List(ctx context.Context, opts v1.ListOptions) (result *networkingv1.Http2RpcList, err error) {
obj, err := c.Fake.
Invokes(testing.NewListAction(http2rpcsResource, http2rpcsKind, c.ns, opts), &networkingv1.Http2RpcList{})
if obj == nil {
return nil, err
}
label, _, _ := testing.ExtractFromListOptions(opts)
if label == nil {
label = labels.Everything()
}
list := &networkingv1.Http2RpcList{ListMeta: obj.(*networkingv1.Http2RpcList).ListMeta}
for _, item := range obj.(*networkingv1.Http2RpcList).Items {
if label.Matches(labels.Set(item.Labels)) {
list.Items = append(list.Items, item)
}
}
return list, err
}
// Watch returns a watch.Interface that watches the requested http2Rpcs.
func (c *FakeHttp2Rpcs) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewWatchAction(http2rpcsResource, c.ns, opts))
}
// Create takes the representation of a http2Rpc and creates it. Returns the server's representation of the http2Rpc, and an error, if there is any.
func (c *FakeHttp2Rpcs) Create(ctx context.Context, http2Rpc *networkingv1.Http2Rpc, opts v1.CreateOptions) (result *networkingv1.Http2Rpc, err error) {
obj, err := c.Fake.
Invokes(testing.NewCreateAction(http2rpcsResource, c.ns, http2Rpc), &networkingv1.Http2Rpc{})
if obj == nil {
return nil, err
}
return obj.(*networkingv1.Http2Rpc), err
}
// Update takes the representation of a http2Rpc and updates it. Returns the server's representation of the http2Rpc, and an error, if there is any.
func (c *FakeHttp2Rpcs) Update(ctx context.Context, http2Rpc *networkingv1.Http2Rpc, opts v1.UpdateOptions) (result *networkingv1.Http2Rpc, err error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateAction(http2rpcsResource, c.ns, http2Rpc), &networkingv1.Http2Rpc{})
if obj == nil {
return nil, err
}
return obj.(*networkingv1.Http2Rpc), err
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *FakeHttp2Rpcs) UpdateStatus(ctx context.Context, http2Rpc *networkingv1.Http2Rpc, opts v1.UpdateOptions) (*networkingv1.Http2Rpc, error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateSubresourceAction(http2rpcsResource, "status", c.ns, http2Rpc), &networkingv1.Http2Rpc{})
if obj == nil {
return nil, err
}
return obj.(*networkingv1.Http2Rpc), err
}
// Delete takes name of the http2Rpc and deletes it. Returns an error if one occurs.
func (c *FakeHttp2Rpcs) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(http2rpcsResource, c.ns, name), &networkingv1.Http2Rpc{})
return err
}
// DeleteCollection deletes a collection of objects.
func (c *FakeHttp2Rpcs) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
action := testing.NewDeleteCollectionAction(http2rpcsResource, c.ns, listOpts)
_, err := c.Fake.Invokes(action, &networkingv1.Http2RpcList{})
return err
}
// Patch applies the patch and returns the patched http2Rpc.
func (c *FakeHttp2Rpcs) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *networkingv1.Http2Rpc, err error) {
obj, err := c.Fake.
Invokes(testing.NewPatchSubresourceAction(http2rpcsResource, c.ns, name, pt, data, subresources...), &networkingv1.Http2Rpc{})
if obj == nil {
return nil, err
}
return obj.(*networkingv1.Http2Rpc), err
}

View File

@@ -26,6 +26,10 @@ type FakeNetworkingV1 struct {
*testing.Fake
}
func (c *FakeNetworkingV1) Http2Rpcs(namespace string) v1.Http2RpcInterface {
return &FakeHttp2Rpcs{c, namespace}
}
func (c *FakeNetworkingV1) McpBridges(namespace string) v1.McpBridgeInterface {
return &FakeMcpBridges{c, namespace}
}

View File

@@ -16,4 +16,6 @@
package v1
type Http2RpcExpansion interface{}
type McpBridgeExpansion interface{}

View File

@@ -0,0 +1,193 @@
// 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.
// Code generated by client-gen. DO NOT EDIT.
package v1
import (
"context"
"time"
v1 "github.com/alibaba/higress/client/pkg/apis/networking/v1"
scheme "github.com/alibaba/higress/client/pkg/clientset/versioned/scheme"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
rest "k8s.io/client-go/rest"
)
// Http2RpcsGetter has a method to return a Http2RpcInterface.
// A group's client should implement this interface.
type Http2RpcsGetter interface {
Http2Rpcs(namespace string) Http2RpcInterface
}
// Http2RpcInterface has methods to work with Http2Rpc resources.
type Http2RpcInterface interface {
Create(ctx context.Context, http2Rpc *v1.Http2Rpc, opts metav1.CreateOptions) (*v1.Http2Rpc, error)
Update(ctx context.Context, http2Rpc *v1.Http2Rpc, opts metav1.UpdateOptions) (*v1.Http2Rpc, error)
UpdateStatus(ctx context.Context, http2Rpc *v1.Http2Rpc, opts metav1.UpdateOptions) (*v1.Http2Rpc, error)
Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error
DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error
Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.Http2Rpc, error)
List(ctx context.Context, opts metav1.ListOptions) (*v1.Http2RpcList, error)
Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error)
Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.Http2Rpc, err error)
Http2RpcExpansion
}
// http2Rpcs implements Http2RpcInterface
type http2Rpcs struct {
client rest.Interface
ns string
}
// newHttp2Rpcs returns a Http2Rpcs
func newHttp2Rpcs(c *NetworkingV1Client, namespace string) *http2Rpcs {
return &http2Rpcs{
client: c.RESTClient(),
ns: namespace,
}
}
// Get takes name of the http2Rpc, and returns the corresponding http2Rpc object, and an error if there is any.
func (c *http2Rpcs) Get(ctx context.Context, name string, options metav1.GetOptions) (result *v1.Http2Rpc, err error) {
result = &v1.Http2Rpc{}
err = c.client.Get().
Namespace(c.ns).
Resource("http2rpcs").
Name(name).
VersionedParams(&options, scheme.ParameterCodec).
Do(ctx).
Into(result)
return
}
// List takes label and field selectors, and returns the list of Http2Rpcs that match those selectors.
func (c *http2Rpcs) List(ctx context.Context, opts metav1.ListOptions) (result *v1.Http2RpcList, err error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
result = &v1.Http2RpcList{}
err = c.client.Get().
Namespace(c.ns).
Resource("http2rpcs").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Do(ctx).
Into(result)
return
}
// Watch returns a watch.Interface that watches the requested http2Rpcs.
func (c *http2Rpcs) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
opts.Watch = true
return c.client.Get().
Namespace(c.ns).
Resource("http2rpcs").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Watch(ctx)
}
// Create takes the representation of a http2Rpc and creates it. Returns the server's representation of the http2Rpc, and an error, if there is any.
func (c *http2Rpcs) Create(ctx context.Context, http2Rpc *v1.Http2Rpc, opts metav1.CreateOptions) (result *v1.Http2Rpc, err error) {
result = &v1.Http2Rpc{}
err = c.client.Post().
Namespace(c.ns).
Resource("http2rpcs").
VersionedParams(&opts, scheme.ParameterCodec).
Body(http2Rpc).
Do(ctx).
Into(result)
return
}
// Update takes the representation of a http2Rpc and updates it. Returns the server's representation of the http2Rpc, and an error, if there is any.
func (c *http2Rpcs) Update(ctx context.Context, http2Rpc *v1.Http2Rpc, opts metav1.UpdateOptions) (result *v1.Http2Rpc, err error) {
result = &v1.Http2Rpc{}
err = c.client.Put().
Namespace(c.ns).
Resource("http2rpcs").
Name(http2Rpc.Name).
VersionedParams(&opts, scheme.ParameterCodec).
Body(http2Rpc).
Do(ctx).
Into(result)
return
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *http2Rpcs) UpdateStatus(ctx context.Context, http2Rpc *v1.Http2Rpc, opts metav1.UpdateOptions) (result *v1.Http2Rpc, err error) {
result = &v1.Http2Rpc{}
err = c.client.Put().
Namespace(c.ns).
Resource("http2rpcs").
Name(http2Rpc.Name).
SubResource("status").
VersionedParams(&opts, scheme.ParameterCodec).
Body(http2Rpc).
Do(ctx).
Into(result)
return
}
// Delete takes name of the http2Rpc and deletes it. Returns an error if one occurs.
func (c *http2Rpcs) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error {
return c.client.Delete().
Namespace(c.ns).
Resource("http2rpcs").
Name(name).
Body(&opts).
Do(ctx).
Error()
}
// DeleteCollection deletes a collection of objects.
func (c *http2Rpcs) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error {
var timeout time.Duration
if listOpts.TimeoutSeconds != nil {
timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second
}
return c.client.Delete().
Namespace(c.ns).
Resource("http2rpcs").
VersionedParams(&listOpts, scheme.ParameterCodec).
Timeout(timeout).
Body(&opts).
Do(ctx).
Error()
}
// Patch applies the patch and returns the patched http2Rpc.
func (c *http2Rpcs) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.Http2Rpc, err error) {
result = &v1.Http2Rpc{}
err = c.client.Patch(pt).
Namespace(c.ns).
Resource("http2rpcs").
Name(name).
SubResource(subresources...).
VersionedParams(&opts, scheme.ParameterCodec).
Body(data).
Do(ctx).
Into(result)
return
}

View File

@@ -24,6 +24,7 @@ import (
type NetworkingV1Interface interface {
RESTClient() rest.Interface
Http2RpcsGetter
McpBridgesGetter
}
@@ -32,6 +33,10 @@ type NetworkingV1Client struct {
restClient rest.Interface
}
func (c *NetworkingV1Client) Http2Rpcs(namespace string) Http2RpcInterface {
return newHttp2Rpcs(c, namespace)
}
func (c *NetworkingV1Client) McpBridges(namespace string) McpBridgeInterface {
return newMcpBridges(c, namespace)
}

View File

@@ -56,6 +56,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource
return &genericInformer{resource: resource.GroupResource(), informer: f.Extensions().V1alpha1().WasmPlugins().Informer()}, nil
// Group=networking.higress.io, Version=v1
case v1.SchemeGroupVersion.WithResource("http2rpcs"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Networking().V1().Http2Rpcs().Informer()}, nil
case v1.SchemeGroupVersion.WithResource("mcpbridges"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Networking().V1().McpBridges().Informer()}, nil

View File

@@ -0,0 +1,88 @@
// 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.
// Code generated by informer-gen. DO NOT EDIT.
package v1
import (
"context"
time "time"
networkingv1 "github.com/alibaba/higress/client/pkg/apis/networking/v1"
versioned "github.com/alibaba/higress/client/pkg/clientset/versioned"
internalinterfaces "github.com/alibaba/higress/client/pkg/informers/externalversions/internalinterfaces"
v1 "github.com/alibaba/higress/client/pkg/listers/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
watch "k8s.io/apimachinery/pkg/watch"
cache "k8s.io/client-go/tools/cache"
)
// Http2RpcInformer provides access to a shared informer and lister for
// Http2Rpcs.
type Http2RpcInformer interface {
Informer() cache.SharedIndexInformer
Lister() v1.Http2RpcLister
}
type http2RpcInformer struct {
factory internalinterfaces.SharedInformerFactory
tweakListOptions internalinterfaces.TweakListOptionsFunc
namespace string
}
// NewHttp2RpcInformer constructs a new informer for Http2Rpc type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewHttp2RpcInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
return NewFilteredHttp2RpcInformer(client, namespace, resyncPeriod, indexers, nil)
}
// NewFilteredHttp2RpcInformer constructs a new informer for Http2Rpc type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewFilteredHttp2RpcInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
return cache.NewSharedIndexInformer(
&cache.ListWatch{
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.NetworkingV1().Http2Rpcs(namespace).List(context.TODO(), options)
},
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.NetworkingV1().Http2Rpcs(namespace).Watch(context.TODO(), options)
},
},
&networkingv1.Http2Rpc{},
resyncPeriod,
indexers,
)
}
func (f *http2RpcInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
return NewFilteredHttp2RpcInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
}
func (f *http2RpcInformer) Informer() cache.SharedIndexInformer {
return f.factory.InformerFor(&networkingv1.Http2Rpc{}, f.defaultInformer)
}
func (f *http2RpcInformer) Lister() v1.Http2RpcLister {
return v1.NewHttp2RpcLister(f.Informer().GetIndexer())
}

View File

@@ -22,6 +22,8 @@ import (
// Interface provides access to all the informers in this group version.
type Interface interface {
// Http2Rpcs returns a Http2RpcInformer.
Http2Rpcs() Http2RpcInformer
// McpBridges returns a McpBridgeInformer.
McpBridges() McpBridgeInformer
}
@@ -37,6 +39,11 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList
return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
}
// Http2Rpcs returns a Http2RpcInformer.
func (v *version) Http2Rpcs() Http2RpcInformer {
return &http2RpcInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
}
// McpBridges returns a McpBridgeInformer.
func (v *version) McpBridges() McpBridgeInformer {
return &mcpBridgeInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}

View File

@@ -16,6 +16,14 @@
package v1
// Http2RpcListerExpansion allows custom methods to be added to
// Http2RpcLister.
type Http2RpcListerExpansion interface{}
// Http2RpcNamespaceListerExpansion allows custom methods to be added to
// Http2RpcNamespaceLister.
type Http2RpcNamespaceListerExpansion interface{}
// McpBridgeListerExpansion allows custom methods to be added to
// McpBridgeLister.
type McpBridgeListerExpansion interface{}

View File

@@ -0,0 +1,92 @@
// 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.
// Code generated by lister-gen. DO NOT EDIT.
package v1
import (
v1 "github.com/alibaba/higress/client/pkg/apis/networking/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/tools/cache"
)
// Http2RpcLister helps list Http2Rpcs.
type Http2RpcLister interface {
// List lists all Http2Rpcs in the indexer.
List(selector labels.Selector) (ret []*v1.Http2Rpc, err error)
// Http2Rpcs returns an object that can list and get Http2Rpcs.
Http2Rpcs(namespace string) Http2RpcNamespaceLister
Http2RpcListerExpansion
}
// http2RpcLister implements the Http2RpcLister interface.
type http2RpcLister struct {
indexer cache.Indexer
}
// NewHttp2RpcLister returns a new Http2RpcLister.
func NewHttp2RpcLister(indexer cache.Indexer) Http2RpcLister {
return &http2RpcLister{indexer: indexer}
}
// List lists all Http2Rpcs in the indexer.
func (s *http2RpcLister) List(selector labels.Selector) (ret []*v1.Http2Rpc, err error) {
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
ret = append(ret, m.(*v1.Http2Rpc))
})
return ret, err
}
// Http2Rpcs returns an object that can list and get Http2Rpcs.
func (s *http2RpcLister) Http2Rpcs(namespace string) Http2RpcNamespaceLister {
return http2RpcNamespaceLister{indexer: s.indexer, namespace: namespace}
}
// Http2RpcNamespaceLister helps list and get Http2Rpcs.
type Http2RpcNamespaceLister interface {
// List lists all Http2Rpcs in the indexer for a given namespace.
List(selector labels.Selector) (ret []*v1.Http2Rpc, err error)
// Get retrieves the Http2Rpc from the indexer for a given namespace and name.
Get(name string) (*v1.Http2Rpc, error)
Http2RpcNamespaceListerExpansion
}
// http2RpcNamespaceLister implements the Http2RpcNamespaceLister
// interface.
type http2RpcNamespaceLister struct {
indexer cache.Indexer
namespace string
}
// List lists all Http2Rpcs in the indexer for a given namespace.
func (s http2RpcNamespaceLister) List(selector labels.Selector) (ret []*v1.Http2Rpc, err error) {
err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
ret = append(ret, m.(*v1.Http2Rpc))
})
return ret, err
}
// Get retrieves the Http2Rpc from the indexer for a given namespace and name.
func (s http2RpcNamespaceLister) Get(name string) (*v1.Http2Rpc, error) {
obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound(v1.Resource("http2rpc"), name)
}
return obj.(*v1.Http2Rpc), nil
}

1
helm/core/.helmignore Normal file
View File

@@ -0,0 +1 @@
crds/customresourcedefinitions.gen_lt1.16.yaml

View File

@@ -1,7 +1,8 @@
apiVersion: v2
appVersion: 1.0.0-rc
appVersion: 1.1.0
description: Helm chart for deploying higress gateways
icon: https://higress.io/img/higress_logo_small.png
home: http://higress.io/
keywords:
- higress
- gateways
@@ -9,4 +10,4 @@ name: higress-core
sources:
- http://github.com/alibaba/higress
type: application
version: 1.0.0-rc
version: 1.1.0

407
helm/core/LICENSE Normal file
View File

@@ -0,0 +1,407 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
========================================================================
Higress Subcomponents:
The Higress project contains subcomponents with separate copyright
notices and license terms. Your use of the source code for the these
subcomponents is subject to the terms and conditions of the following
licenses.
========================================================================
Apache-2.0 licenses
========================================================================
cloud.google.com/go v0.97.0 Apache-2.0
cloud.google.com/go/logging v1.4.2 Apache-2.0
contrib.go.opencensus.io/exporter/prometheus v0.4.0 Apache-2.0
github.com/Azure/go-autorest v14.2.0+incompatible Apache-2.0
github.com/Azure/go-autorest/autorest v0.11.20 Apache-2.0
github.com/Azure/go-autorest/autorest/adal v0.9.15 Apache-2.0
github.com/Azure/go-autorest/autorest/date v0.3.0 Apache-2.0
github.com/Azure/go-autorest/logger v0.2.1 Apache-2.0
github.com/Azure/go-autorest/tracing v0.6.0 Apache-2.0
github.com/Masterminds/goutils v1.1.1 Apache-2.0
github.com/aws/aws-sdk-go v1.41.7 Apache-2.0
github.com/census-instrumentation/opencensus-proto v0.3.0 Apache-2.0
github.com/cncf/xds/go v0.0.0-20220520190051-1e77728a1eaa Apache-2.0
github.com/containerd/continuity v0.1.0 Apache-2.0
github.com/docker/cli v20.10.7+incompatible Apache-2.0
github.com/docker/distribution v0.0.0-20191216044856-a8371794149d Apache-2.0
github.com/docker/go-units v0.4.0 Apache-2.0
github.com/envoyproxy/protoc-gen-validate v0.1.0 Apache-2.0
github.com/go-logr/logr v0.4.0 Apache-2.0
github.com/go-openapi/jsonpointer v0.19.5 Apache-2.0
github.com/go-openapi/jsonreference v0.19.5 Apache-2.0
github.com/go-openapi/swag v0.19.14 Apache-2.0
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da Apache-2.0
github.com/google/btree v1.0.1 Apache-2.0
github.com/google/go-containerregistry v0.6.0 Apache-2.0
github.com/google/gofuzz v1.2.0 Apache-2.0
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 Apache-2.0
github.com/googleapis/gnostic v0.5.5 Apache-2.0
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 Apache-2.0
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 Apache-2.0
github.com/inconshreveable/mousetrap v1.0.0 Apache-2.0
github.com/jmespath/go-jmespath v0.4.0 Apache-2.0
github.com/jonboulle/clockwork v0.2.2 Apache-2.0
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 Apache-2.0
github.com/moby/moby v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible Apache-2.0
github.com/moby/spdystream v0.2.0 Apache-2.0
github.com/moby/term v0.0.0-20210610120745-9d4ed1856297 Apache-2.0
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd Apache-2.0
github.com/modern-go/reflect2 v1.0.1 Apache-2.0
github.com/opencontainers/go-digest v1.0.0 Apache-2.0
github.com/opencontainers/image-spec v1.0.1 Apache-2.0
github.com/opencontainers/runc v1.0.2 Apache-2.0
github.com/openshift/api v0.0.0-20200713203337-b2494ecb17dd Apache-2.0
github.com/prometheus/client_golang v1.11.0 Apache-2.0
github.com/prometheus/client_model v0.2.0 Apache-2.0
github.com/prometheus/common v0.32.1 Apache-2.0
github.com/prometheus/procfs v0.6.0 Apache-2.0
github.com/prometheus/statsd_exporter v0.21.0 Apache-2.0
github.com/spf13/cobra v1.2.1 Apache-2.0
go.opencensus.io v0.23.0 Apache-2.0
go.opentelemetry.io/proto/otlp v0.7.0 Apache-2.0
gomodules.xyz/jsonpatch/v2 v2.2.0 Apache-2.0
gomodules.xyz/jsonpatch/v3 v3.0.1 Apache-2.0
google.golang.org/appengine v1.6.7 Apache-2.0
google.golang.org/genproto v0.0.0-20211020151524-b7c3a969101a Apache-2.0
google.golang.org/grpc v1.42.0 Apache-2.0
gopkg.in/square/go-jose.v2 v2.6.0 Apache-2.0
gopkg.in/yaml.v2 v2.4.0 Apache-2.0
istio.io/gogo-genproto v0.0.0-20211115195057-0e34bdd2be67 Apache-2.0
k8s.io/api v0.22.2 Apache-2.0
k8s.io/apiextensions-apiserver v0.22.2 Apache-2.0
k8s.io/apimachinery v0.22.2 Apache-2.0
k8s.io/cli-runtime v0.22.2 Apache-2.0
k8s.io/client-go v0.22.2 Apache-2.0
k8s.io/component-base v0.22.2 Apache-2.0
k8s.io/klog/v2 v2.10.0 Apache-2.0
k8s.io/kube-openapi v0.0.0-20211020163157-7327e2aaee2b Apache-2.0
k8s.io/kubectl v0.22.2 Apache-2.0
k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b Apache-2.0
sigs.k8s.io/controller-runtime v0.10.2 Apache-2.0
sigs.k8s.io/gateway-api v0.4.0 Apache-2.0
sigs.k8s.io/kustomize/api v0.8.11 Apache-2.0
sigs.k8s.io/kustomize/kyaml v0.11.0 Apache-2.0
sigs.k8s.io/mcs-api v0.1.0 Apache-2.0
sigs.k8s.io/structured-merge-diff/v4 v4.1.2 Apache-2.0
========================================================================
BSD-2-Clause licenses
========================================================================
github.com/pkg/errors v0.9.1 BSD-2-Clause
github.com/russross/blackfriday v1.5.2 BSD-2-Clause
========================================================================
BSD-3-Clause licenses
========================================================================
github.com/PuerkitoBio/purell v1.1.1 BSD-3-Clause
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 BSD-3-Clause
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 BSD-3-Clause
github.com/evanphx/json-patch v4.11.0+incompatible BSD-3-Clause
github.com/evanphx/json-patch/v5 v5.6.0 BSD-3-Clause
github.com/fsnotify/fsnotify v1.5.1 BSD-3-Clause
github.com/gogo/protobuf v1.3.2 BSD-3-Clause
github.com/golang/protobuf v1.5.2 BSD-3-Clause
github.com/google/go-cmp v0.5.6 BSD-3-Clause
github.com/google/uuid v1.3.0 BSD-3-Clause
github.com/googleapis/gax-go/v2 v2.1.1 BSD-3-Clause
github.com/imdario/mergo v0.3.5 BSD-3-Clause
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de BSD-3-Clause
github.com/pmezard/go-difflib v1.0.0 BSD-3-Clause
github.com/spaolacci/murmur3 v1.1.0 BSD-3-Clause
github.com/spf13/pflag v1.0.5 BSD-3-Clause
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 BSD-3-Clause
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 BSD-3-Clause
golang.org/x/net v0.0.0-20211020060615-d418f374d309 BSD-3-Clause
golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1 BSD-3-Clause
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c BSD-3-Clause
golang.org/x/sys v0.0.0-20211020174200-9d6173849985 BSD-3-Clause
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d BSD-3-Clause
golang.org/x/text v0.3.6 BSD-3-Clause
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac BSD-3-Clause
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 BSD-3-Clause
google.golang.org/api v0.59.0 BSD-3-Clause
google.golang.org/protobuf v1.27.1 BSD-3-Clause
gopkg.in/inf.v0 v0.9.1 BSD-3-Clause
========================================================================
ISC licenses
========================================================================
github.com/davecgh/go-spew v1.1.1 ISC
github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0 ISC
========================================================================
MIT licenses
========================================================================
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 MIT
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd MIT
github.com/Masterminds/semver/v3 v3.1.1 MIT
github.com/Masterminds/sprig/v3 v3.2.2 MIT
github.com/Microsoft/go-winio v0.5.0 MIT
github.com/Microsoft/hcsshim v0.8.21 MIT
github.com/beorn7/perks v1.0.1 MIT
github.com/cenkalti/backoff/v4 v4.1.1 MIT
github.com/cespare/xxhash/v2 v2.1.1 MIT
github.com/docker/docker-credential-helpers v0.6.3 MIT
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d MIT
github.com/fvbommel/sortorder v1.0.1 MIT
github.com/go-errors/errors v1.0.1 MIT
github.com/go-kit/log v0.1.0 MIT
github.com/go-logfmt/logfmt v0.5.0 MIT
github.com/goccy/go-json v0.4.8 MIT
github.com/golang-jwt/jwt/v4 v4.0.0 MIT
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 MIT
github.com/huandu/xstrings v1.3.2 MIT
github.com/josharian/intern v1.0.0 MIT
github.com/json-iterator/go v1.1.11 MIT
github.com/lestrrat-go/backoff/v2 v2.0.7 MIT
github.com/lestrrat-go/blackmagic v1.0.0 MIT
github.com/lestrrat-go/httpcc v1.0.0 MIT
github.com/lestrrat-go/iter v1.0.1 MIT
github.com/lestrrat-go/jwx v1.2.0 MIT
github.com/lestrrat-go/option v1.0.0 MIT
github.com/mailru/easyjson v0.7.6 MIT
github.com/mitchellh/copystructure v1.2.0 MIT
github.com/mitchellh/go-wordwrap v1.0.0 MIT
github.com/mitchellh/reflectwalk v1.0.2 MIT
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 MIT
github.com/natefinch/lumberjack v2.0.0+incompatible MIT
github.com/peterbourgon/diskv v2.0.1+incompatible MIT
github.com/shopspring/decimal v1.2.0 MIT
github.com/sirupsen/logrus v1.8.1 MIT
github.com/spf13/cast v1.3.1 MIT
github.com/stretchr/testify v1.7.0 MIT
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca MIT
github.com/yl2chen/cidranger v1.0.2 MIT
go.uber.org/atomic v1.9.0 MIT
go.uber.org/multierr v1.7.0 MIT
go.uber.org/zap v1.19.1 MIT
gomodules.xyz/orderedmap v0.1.0 MIT
========================================================================
MIT and Apache-2.0 licenses
========================================================================
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b MIT and Apache-2.0
========================================================================
MIT and BSD-3-Clause licenses
========================================================================
github.com/ghodss/yaml v1.0.0 MIT and BSD-3-Clause
sigs.k8s.io/yaml v1.3.0 MIT and BSD-3-Clause
========================================================================
MPL-2.0 licenses
========================================================================
github.com/hashicorp/errwrap v1.0.0 MPL-2.0
github.com/hashicorp/go-multierror v1.1.1 MPL-2.0
github.com/hashicorp/go-version v1.3.0 MPL-2.0
github.com/hashicorp/golang-lru v0.5.4 MPL-2.0

5
helm/core/README.md Normal file
View File

@@ -0,0 +1,5 @@
# Higress Core Helm Chart
Installs the core components of cloud-native gateway [Higress](http://higress.io/)
**Note:** It is highly recommended to install the whole package of Higress. Please visit https://higress.io/docs/user/quickstart/ for details.

View File

@@ -104,6 +104,88 @@ spec:
subresources:
status: {}
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
"helm.sh/resource-policy": keep
name: http2rpcs.networking.higress.io
spec:
group: networking.higress.io
names:
categories:
- higress-io
kind: Http2Rpc
listKind: Http2RpcList
plural: http2rpcs
singular: http2rpc
scope: Namespaced
versions:
- name: v1
schema:
openAPIV3Schema:
properties:
spec:
oneOf:
- not:
anyOf:
- required:
- dubbo
- required:
- grpc
- required:
- dubbo
- required:
- grpc
properties:
dubbo:
properties:
group:
type: string
methods:
items:
properties:
headersAttach:
type: string
httpMethods:
items:
type: string
type: array
httpPath:
type: string
params:
items:
properties:
paramKey:
type: string
paramSource:
type: string
paramType:
type: string
type: object
type: array
serviceMethod:
type: string
type: object
type: array
service:
type: string
version:
type: string
type: object
grpc:
type: object
type: object
status:
type: object
x-kubernetes-preserve-unknown-fields: true
type: object
served: true
storage: true
subresources:
status: {}
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition

View File

@@ -0,0 +1,176 @@
# DO NOT EDIT - Generated by Cue OpenAPI generator based on Istio APIs.
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
annotations:
"helm.sh/resource-policy": keep
name: wasmplugins.extensions.higress.io
spec:
group: extensions.higress.io
names:
categories:
- higress-io
- extensions-higress-io
kind: WasmPlugin
listKind: WasmPluginList
plural: wasmplugins
singular: wasmplugin
scope: Namespaced
additionalPrinterColumns:
- description: 'CreationTimestamp is a timestamp representing the server time
when this object was created. It is not guaranteed to be set in happens-before
order across separate operations. Clients may not set this value. It is represented
in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for
lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata'
JSONPath: .metadata.creationTimestamp
name: Age
type: date
versions:
- name: v1alpha1
served: true
storage: true
version: v1alpha1
validation:
openAPIV3Schema:
properties:
spec:
properties:
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.
enum:
- UNSPECIFIED_POLICY
- IfNotPresent
- Always
type: string
imagePullSecret:
description: Credentials to use for OCI image pulling.
type: string
matchRules:
items:
properties:
config:
type: object
x-kubernetes-preserve-unknown-fields: true
configDisable:
type: boolean
domain:
items:
type: string
type: array
ingress:
items:
type: string
type: array
type: object
type: array
phase:
description: Determines where in the filter chain this `WasmPlugin`
is to be injected.
enum:
- UNSPECIFIED_PHASE
- AUTHN
- AUTHZ
- STATS
type: string
pluginConfig:
description: The configuration that will be passed on to the plugin.
type: object
x-kubernetes-preserve-unknown-fields: true
pluginName:
type: string
priority:
description: Determines ordering of `WasmPlugins` in the same `phase`.
nullable: true
type: integer
sha256:
description: SHA256 checksum that will be used to verify Wasm module
or OCI container.
type: string
url:
description: URL of a Wasm module or OCI container.
type: string
verificationKey:
type: string
type: object
status:
type: object
x-kubernetes-preserve-unknown-fields: true
type: object
subresources:
status: {}
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
annotations:
"helm.sh/resource-policy": keep
name: mcpbridges.networking.higress.io
spec:
group: networking.higress.io
names:
categories:
- higress-io
kind: McpBridge
listKind: McpBridgeList
plural: mcpbridges
singular: mcpbridge
scope: Namespaced
versions:
- name: v1
served: true
storage: true
version: v1
validation:
openAPIV3Schema:
properties:
spec:
properties:
registries:
items:
properties:
consulNamespace:
type: string
domain:
type: string
nacosAccessKey:
type: string
nacosAddressServer:
type: string
nacosGroups:
items:
type: string
type: array
nacosNamespace:
type: string
nacosNamespaceId:
type: string
nacosRefreshInterval:
format: int64
type: integer
nacosSecretKey:
type: string
name:
type: string
port:
type: integer
type:
type: string
zkServicesPath:
items:
type: string
type: array
type: object
type: array
type: object
type: object
x-kubernetes-preserve-unknown-fields: true
type: object
subresources:
status: {}
---

View File

@@ -95,3 +95,9 @@ higress: {{ include "controller.name" . }}
{{- print "first-party-jwt" }}
{{- end }}
{{- end }}
{{- define "skywalking.enabled" -}}
{{- if and .Values.skywalking.enabled .Values.skywalking.service.address }}
true
{{- end }}
{{- end }}

View File

@@ -122,7 +122,7 @@ data:
{{- include "mesh" . }}
{{- end }}
---
{{- if .Values.enableSkywalking }}
{{- if include "skywalking.enabled" . }}
apiVersion: v1
kind: ConfigMap
metadata:
@@ -154,7 +154,6 @@ data:
"type": "LOGICAL_DNS",
"connect_timeout": "5s",
"http2_protocol_options": {
},
"dns_lookup_family": "V4_ONLY",
"lb_policy": "ROUND_ROBIN",
@@ -167,8 +166,8 @@ data:
"endpoint": {
"address": {
"socket_address": {
"address": "{{ .Values.Skywalking.address }}",
"port_value": "{{ .Values.Skywalking.port }}"
"address": "{{ .Values.skywalking.service.address }}",
"port_value": "{{ .Values.skywalking.service.port }}"
}
}
}

View File

@@ -46,6 +46,10 @@ rules:
resources: ["wasmplugins"]
verbs: ["get", "list", "watch"]
- apiGroups: ["networking.higress.io"]
resources: ["http2rpcs"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: [""]
resources: ["services"]
verbs: ["get", "watch", "list", "update", "patch", "create", "delete"]

View File

@@ -100,6 +100,10 @@ spec:
fieldPath: spec.serviceAccountName
- name: KUBECONFIG
value: /var/run/secrets/remote/config
- name: PRIORITIZED_LEADER_ELECTION
value: "false"
- name: INJECT_ENABLED
value: "false"
{{- if .Values.pilot.env }}
{{- range $key, $val := .Values.pilot.env }}
- name: {{ $key }}

View File

@@ -146,7 +146,7 @@ spec:
value: "{{ $.Values.clusterName | default `Kubernetes` }}"
- name: INSTANCE_NAME
value: "higress-gateway"
{{- if .Values.enableSkywalking }}
{{- if include "skywalking.enabled" . }}
- name: ISTIO_BOOTSTRAP_OVERRIDE
value: /etc/istio/custom-bootstrap/custom_bootstrap.json
{{- end }}
@@ -202,10 +202,14 @@ spec:
mountPath: /etc/istio/pod
- name: proxy-socket
mountPath: /etc/istio/proxy
{{- if .Values.enableSkywalking }}
{{- if include "skywalking.enabled" . }}
- mountPath: /etc/istio/custom-bootstrap
name: custom-bootstrap-volume
{{- end }}
{{- if .Values.global.volumeWasmPlugins }}
- mountPath: /opt/plugins
name: local-wasmplugins-volume
{{- end }}
{{- if .Values.gateway.hostNetwork }}
hostNetwork: {{ .Values.gateway.hostNetwork }}
dnsPolicy: ClusterFirstWithHostNet
@@ -242,7 +246,7 @@ spec:
- name: config
configMap:
name: higress-config
{{- if .Values.enableSkywalking }}
{{- if include "skywalking.enabled" . }}
- configMap:
defaultMode: 420
name: higress-custom-bootstrap
@@ -274,3 +278,9 @@ spec:
containerName: higress-gateway
divisor: 1m
resource: limits.cpu
{{- if .Values.global.volumeWasmPlugins }}
- name: local-wasmplugins-volume
hostPath:
path: /opt/plugins
type: Directory
{{- end }}

View File

@@ -45,7 +45,7 @@ global:
# Dev builds from prow are on gcr.io
hub: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress
# Default tag for Istio images.
tag: 1.0.0-rc
tag: 1.1.0
# Specify image pull policy if default behavior isn't desired.
# Default behavior: latest images will be Always else IfNotPresent.
@@ -369,7 +369,7 @@ gateway:
name: "higress-gateway"
replicas: 2
image: gateway
tag: "1.0.0-rc"
tag: "1.1.0"
# revision declares which revision this gateway is a part of
revision: ""
@@ -457,7 +457,7 @@ controller:
name: "higress-controller"
replicas: 1
image: higress
tag: "1.0.0-rc"
tag: "1.1.0"
env: {}
labels: {}
@@ -547,7 +547,7 @@ pilot:
rollingMaxUnavailable: 25%
hub: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress
tag: 1.0.0-rc
tag: 1.1.0
# Can be a full hub/image:tag
image: pilot
@@ -560,7 +560,7 @@ pilot:
memory: 2048Mi
env:
PILOT_SCOPE_GATEWAY_TO_NAMESPACE: "true"
PILOT_SCOPE_GATEWAY_TO_NAMESPACE: "false"
PILOT_ENABLE_METADATA_EXCHANGE: "false"
PILOT_ENABLE_CROSS_CLUSTER_WORKLOAD_ENTRY: "false"
VALIDATION_ENABLED: "false"
@@ -610,7 +610,8 @@ pilot:
# Skywalking config settings
enableSkywalking: false
Skywalking:
address: "skywalking-oap.higress-system.svc"
port: 11800
skywalking:
enabled: false
service:
address: ~
port: 11800

View File

@@ -1,9 +1,9 @@
dependencies:
- name: higress-core
repository: file://../core
version: 1.0.0-rc
version: 1.1.0
- name: higress-console
repository: https://higress.io/helm-charts/
version: 0.2.0
digest: sha256:0a34765ab2125ccf397e81566b4d81a8dc0742a2477d225aad77d9450e4add94
generated: "2023-04-08T23:17:37.193119+08:00"
version: 1.1.0
digest: sha256:ab013ed8a315faf016e4114f2315af3449df0a6aa62bb0b8b83704d346109722
generated: "2023-07-10T20:56:37.100196+08:00"

View File

@@ -1,7 +1,8 @@
apiVersion: v2
appVersion: 1.0.0-rc
description: Helm chart for deploying higress gateways
appVersion: 1.1.0
description: Helm chart for deploying Higress gateways
icon: https://higress.io/img/higress_logo_small.png
home: http://higress.io/
keywords:
- higress
- gateways
@@ -11,9 +12,9 @@ sources:
dependencies:
- name: higress-core
repository: "file://../core"
version: 1.0.0-rc
version: 1.1.0
- name: higress-console
repository: "https://higress.io/helm-charts/"
version: 0.2.0
version: 1.1.0
type: application
version: 1.0.0-rc
version: 1.1.0

407
helm/higress/LICENSE Normal file
View File

@@ -0,0 +1,407 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
========================================================================
Higress Subcomponents:
The Higress project contains subcomponents with separate copyright
notices and license terms. Your use of the source code for the these
subcomponents is subject to the terms and conditions of the following
licenses.
========================================================================
Apache-2.0 licenses
========================================================================
cloud.google.com/go v0.97.0 Apache-2.0
cloud.google.com/go/logging v1.4.2 Apache-2.0
contrib.go.opencensus.io/exporter/prometheus v0.4.0 Apache-2.0
github.com/Azure/go-autorest v14.2.0+incompatible Apache-2.0
github.com/Azure/go-autorest/autorest v0.11.20 Apache-2.0
github.com/Azure/go-autorest/autorest/adal v0.9.15 Apache-2.0
github.com/Azure/go-autorest/autorest/date v0.3.0 Apache-2.0
github.com/Azure/go-autorest/logger v0.2.1 Apache-2.0
github.com/Azure/go-autorest/tracing v0.6.0 Apache-2.0
github.com/Masterminds/goutils v1.1.1 Apache-2.0
github.com/aws/aws-sdk-go v1.41.7 Apache-2.0
github.com/census-instrumentation/opencensus-proto v0.3.0 Apache-2.0
github.com/cncf/xds/go v0.0.0-20220520190051-1e77728a1eaa Apache-2.0
github.com/containerd/continuity v0.1.0 Apache-2.0
github.com/docker/cli v20.10.7+incompatible Apache-2.0
github.com/docker/distribution v0.0.0-20191216044856-a8371794149d Apache-2.0
github.com/docker/go-units v0.4.0 Apache-2.0
github.com/envoyproxy/protoc-gen-validate v0.1.0 Apache-2.0
github.com/go-logr/logr v0.4.0 Apache-2.0
github.com/go-openapi/jsonpointer v0.19.5 Apache-2.0
github.com/go-openapi/jsonreference v0.19.5 Apache-2.0
github.com/go-openapi/swag v0.19.14 Apache-2.0
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da Apache-2.0
github.com/google/btree v1.0.1 Apache-2.0
github.com/google/go-containerregistry v0.6.0 Apache-2.0
github.com/google/gofuzz v1.2.0 Apache-2.0
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 Apache-2.0
github.com/googleapis/gnostic v0.5.5 Apache-2.0
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 Apache-2.0
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 Apache-2.0
github.com/inconshreveable/mousetrap v1.0.0 Apache-2.0
github.com/jmespath/go-jmespath v0.4.0 Apache-2.0
github.com/jonboulle/clockwork v0.2.2 Apache-2.0
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 Apache-2.0
github.com/moby/moby v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible Apache-2.0
github.com/moby/spdystream v0.2.0 Apache-2.0
github.com/moby/term v0.0.0-20210610120745-9d4ed1856297 Apache-2.0
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd Apache-2.0
github.com/modern-go/reflect2 v1.0.1 Apache-2.0
github.com/opencontainers/go-digest v1.0.0 Apache-2.0
github.com/opencontainers/image-spec v1.0.1 Apache-2.0
github.com/opencontainers/runc v1.0.2 Apache-2.0
github.com/openshift/api v0.0.0-20200713203337-b2494ecb17dd Apache-2.0
github.com/prometheus/client_golang v1.11.0 Apache-2.0
github.com/prometheus/client_model v0.2.0 Apache-2.0
github.com/prometheus/common v0.32.1 Apache-2.0
github.com/prometheus/procfs v0.6.0 Apache-2.0
github.com/prometheus/statsd_exporter v0.21.0 Apache-2.0
github.com/spf13/cobra v1.2.1 Apache-2.0
go.opencensus.io v0.23.0 Apache-2.0
go.opentelemetry.io/proto/otlp v0.7.0 Apache-2.0
gomodules.xyz/jsonpatch/v2 v2.2.0 Apache-2.0
gomodules.xyz/jsonpatch/v3 v3.0.1 Apache-2.0
google.golang.org/appengine v1.6.7 Apache-2.0
google.golang.org/genproto v0.0.0-20211020151524-b7c3a969101a Apache-2.0
google.golang.org/grpc v1.42.0 Apache-2.0
gopkg.in/square/go-jose.v2 v2.6.0 Apache-2.0
gopkg.in/yaml.v2 v2.4.0 Apache-2.0
istio.io/gogo-genproto v0.0.0-20211115195057-0e34bdd2be67 Apache-2.0
k8s.io/api v0.22.2 Apache-2.0
k8s.io/apiextensions-apiserver v0.22.2 Apache-2.0
k8s.io/apimachinery v0.22.2 Apache-2.0
k8s.io/cli-runtime v0.22.2 Apache-2.0
k8s.io/client-go v0.22.2 Apache-2.0
k8s.io/component-base v0.22.2 Apache-2.0
k8s.io/klog/v2 v2.10.0 Apache-2.0
k8s.io/kube-openapi v0.0.0-20211020163157-7327e2aaee2b Apache-2.0
k8s.io/kubectl v0.22.2 Apache-2.0
k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b Apache-2.0
sigs.k8s.io/controller-runtime v0.10.2 Apache-2.0
sigs.k8s.io/gateway-api v0.4.0 Apache-2.0
sigs.k8s.io/kustomize/api v0.8.11 Apache-2.0
sigs.k8s.io/kustomize/kyaml v0.11.0 Apache-2.0
sigs.k8s.io/mcs-api v0.1.0 Apache-2.0
sigs.k8s.io/structured-merge-diff/v4 v4.1.2 Apache-2.0
========================================================================
BSD-2-Clause licenses
========================================================================
github.com/pkg/errors v0.9.1 BSD-2-Clause
github.com/russross/blackfriday v1.5.2 BSD-2-Clause
========================================================================
BSD-3-Clause licenses
========================================================================
github.com/PuerkitoBio/purell v1.1.1 BSD-3-Clause
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 BSD-3-Clause
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 BSD-3-Clause
github.com/evanphx/json-patch v4.11.0+incompatible BSD-3-Clause
github.com/evanphx/json-patch/v5 v5.6.0 BSD-3-Clause
github.com/fsnotify/fsnotify v1.5.1 BSD-3-Clause
github.com/gogo/protobuf v1.3.2 BSD-3-Clause
github.com/golang/protobuf v1.5.2 BSD-3-Clause
github.com/google/go-cmp v0.5.6 BSD-3-Clause
github.com/google/uuid v1.3.0 BSD-3-Clause
github.com/googleapis/gax-go/v2 v2.1.1 BSD-3-Clause
github.com/imdario/mergo v0.3.5 BSD-3-Clause
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de BSD-3-Clause
github.com/pmezard/go-difflib v1.0.0 BSD-3-Clause
github.com/spaolacci/murmur3 v1.1.0 BSD-3-Clause
github.com/spf13/pflag v1.0.5 BSD-3-Clause
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 BSD-3-Clause
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 BSD-3-Clause
golang.org/x/net v0.0.0-20211020060615-d418f374d309 BSD-3-Clause
golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1 BSD-3-Clause
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c BSD-3-Clause
golang.org/x/sys v0.0.0-20211020174200-9d6173849985 BSD-3-Clause
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d BSD-3-Clause
golang.org/x/text v0.3.6 BSD-3-Clause
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac BSD-3-Clause
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 BSD-3-Clause
google.golang.org/api v0.59.0 BSD-3-Clause
google.golang.org/protobuf v1.27.1 BSD-3-Clause
gopkg.in/inf.v0 v0.9.1 BSD-3-Clause
========================================================================
ISC licenses
========================================================================
github.com/davecgh/go-spew v1.1.1 ISC
github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0 ISC
========================================================================
MIT licenses
========================================================================
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 MIT
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd MIT
github.com/Masterminds/semver/v3 v3.1.1 MIT
github.com/Masterminds/sprig/v3 v3.2.2 MIT
github.com/Microsoft/go-winio v0.5.0 MIT
github.com/Microsoft/hcsshim v0.8.21 MIT
github.com/beorn7/perks v1.0.1 MIT
github.com/cenkalti/backoff/v4 v4.1.1 MIT
github.com/cespare/xxhash/v2 v2.1.1 MIT
github.com/docker/docker-credential-helpers v0.6.3 MIT
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d MIT
github.com/fvbommel/sortorder v1.0.1 MIT
github.com/go-errors/errors v1.0.1 MIT
github.com/go-kit/log v0.1.0 MIT
github.com/go-logfmt/logfmt v0.5.0 MIT
github.com/goccy/go-json v0.4.8 MIT
github.com/golang-jwt/jwt/v4 v4.0.0 MIT
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 MIT
github.com/huandu/xstrings v1.3.2 MIT
github.com/josharian/intern v1.0.0 MIT
github.com/json-iterator/go v1.1.11 MIT
github.com/lestrrat-go/backoff/v2 v2.0.7 MIT
github.com/lestrrat-go/blackmagic v1.0.0 MIT
github.com/lestrrat-go/httpcc v1.0.0 MIT
github.com/lestrrat-go/iter v1.0.1 MIT
github.com/lestrrat-go/jwx v1.2.0 MIT
github.com/lestrrat-go/option v1.0.0 MIT
github.com/mailru/easyjson v0.7.6 MIT
github.com/mitchellh/copystructure v1.2.0 MIT
github.com/mitchellh/go-wordwrap v1.0.0 MIT
github.com/mitchellh/reflectwalk v1.0.2 MIT
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 MIT
github.com/natefinch/lumberjack v2.0.0+incompatible MIT
github.com/peterbourgon/diskv v2.0.1+incompatible MIT
github.com/shopspring/decimal v1.2.0 MIT
github.com/sirupsen/logrus v1.8.1 MIT
github.com/spf13/cast v1.3.1 MIT
github.com/stretchr/testify v1.7.0 MIT
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca MIT
github.com/yl2chen/cidranger v1.0.2 MIT
go.uber.org/atomic v1.9.0 MIT
go.uber.org/multierr v1.7.0 MIT
go.uber.org/zap v1.19.1 MIT
gomodules.xyz/orderedmap v0.1.0 MIT
========================================================================
MIT and Apache-2.0 licenses
========================================================================
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b MIT and Apache-2.0
========================================================================
MIT and BSD-3-Clause licenses
========================================================================
github.com/ghodss/yaml v1.0.0 MIT and BSD-3-Clause
sigs.k8s.io/yaml v1.3.0 MIT and BSD-3-Clause
========================================================================
MPL-2.0 licenses
========================================================================
github.com/hashicorp/errwrap v1.0.0 MPL-2.0
github.com/hashicorp/go-multierror v1.1.1 MPL-2.0
github.com/hashicorp/go-version v1.3.0 MPL-2.0
github.com/hashicorp/golang-lru v0.5.4 MPL-2.0

56
helm/higress/README.md Normal file
View File

@@ -0,0 +1,56 @@
# Higress Helm Chart
Installs the cloud-native gateway [Higress](http://higress.io/)
## Get Repo Info
```console
helm repo add higress.io https://higress.io/helm-charts
helm repo update
```
_See [helm repo](https://helm.sh/docs/helm/helm_repo/) for command documentation._
## Installing the Chart
To install the chart with the release name `higress`:
```console
helm install higress -n higress-system higress.io/higress --create-namespace --render-subchart-notes
```
## Uninstalling the Chart
To uninstall/delete the higress deployment:
```console
helm delete higress -n higress-system
```
The command removes all the Kubernetes components associated with the chart and deletes the release.
## Configuration
| **Parameter** | **Description** | **Default** |
|---|---|---|
| **Global Parameters** | | |
| global.local | Set to `true` if installing to a local K8s cluster (e.g.: Kind, Rancher Desktop, etc.) | false |
| global.ingressClass | [IngressClass](https://kubernetes.io/zh-cn/docs/concepts/services-networking/ingress/#ingress-class) which is used to filter Ingress resources Higress Controller watches.<br />If there are multiple gateway instances deployed in the cluster, this parameter can be used to distinguish the scope of each gateway instance.<br />There are some special cases for special IngressClass values:<br />1. If set to "nginx", Higress Controller will watch Ingress resources with the `nginx` IngressClass or without any Ingress class.<br />2. If set to empty, Higress Controller will watch all Ingress resources in the K8s cluster. | higress |
| global.watchNamespace | If not empty, Higress Controller will only watch resources in the specified namespace. When isolating different business systems using K8s namespace, if each namespace requires a standalone gateway instance, this parameter can be used to confine the Ingress watching of Higress within the given namespace. | "" |
| global.disableAlpnH2 | Whether to disable HTTP/2 in ALPN | true |
| global.enableStatus | If `true`, Higress Controller will update the `status` field of Ingress resources.<br />When migrating from Nginx Ingress, in order to avoid `status` field of Ingress objects being overwritten, this parameter needs to be set to false, so Higress won't write the entry IP to the `status` field of the corresponding Ingress object. | true |
| global.enableIstioAPI | If `true`, Higress Controller will monitor istio resources as well | false |
| global.istioNamespace | The namespace istio is installed to | istio-system |
| **Core Paramters** | | |
| higress-core.gateway.replicas | Number of Higress Gateway pods | 2 |
| higress-core.controller.replicas | Number of Higress Controller pods | 1 |
| **Console Paramters** | | |
| higress-console.replicaCount | Number of Higress Console pods | 1 |
| higress-console.service.type | K8s service type used by Higress Console | ClusterIP |
| higress-console.domain | Domain used to access Higress Console | console.higress.io |
| higress-console.tlsSecretName | Name of Secret resource used by TLS connections. | "" |
| higress-console.web.login.prompt | Prompt message to be displayed on the login page | "" |
| higress-console.admin.password.value | If not empty, the admin password will be configured to the specified value. | "" |
| higress-console.admin.password.length | The length of random admin password generated during installation. Only works when `higress-console.admin.password.value` is not set. | 8 |
| higress-console.o11y.enabled | If `true`, o11y suite (Grafana + Promethues) will be installed. | false |
| higress-console.pvc.rwxSupported | Set to `false` when installing to a standard K8s cluster and the target cluster doesn't support the ReadWriteMany access mode of PersistentVolumeClaim. | true |

View File

@@ -140,7 +140,7 @@ index 25479439b7..bd34442260 100755
# This script builds and version stamps the output
+export GOPROXY="https://proxy.golang.com.cn,direct"
+export GOPROXY=${GOPROXY:-"https://proxy.golang.com.cn,direct"}
+
VERBOSE=${VERBOSE:-"0"}
V=""
@@ -215,10 +215,11 @@ index 271fe77a2d..c9e625efdc 100755
--init \
--sig-proxy=true \
${DOCKER_SOCKET_MOUNT:--v /var/run/docker.sock:/var/run/docker.sock} \
@@ -55,7 +59,15 @@ read -ra DOCKER_RUN_OPTIONS <<< "${DOCKER_RUN_OPTIONS:-}"
@@ -55,7 +59,16 @@ read -ra DOCKER_RUN_OPTIONS <<< "${DOCKER_RUN_OPTIONS:-}"
--env-file <(env | grep -v ${ENV_BLOCKLIST}) \
-e IN_BUILD_CONTAINER=1 \
-e TZ="${TIMEZONE:-$TZ}" \
+ -e GOPROXY="${GOPROXY}" \
+ -e HUB="${HUB}" \
+ -e ENVOY_TAR_PATH="${ENVOY_TAR_PATH}" \
+ --mount "type=bind,source=${MOUNT_PACKAGE_SOURCE},destination=/home/package" \

View File

@@ -0,0 +1,30 @@
diff --color -Naur istio/pilot/pkg/features/pilot.go istio_new/pilot/pkg/features/pilot.go
--- istio/pilot/pkg/features/pilot.go 2023-06-18 20:13:57.715044832 +0800
+++ istio_new/pilot/pkg/features/pilot.go 2023-06-18 20:11:40.310406690 +0800
@@ -359,6 +359,9 @@
EnableUnsafeAdminEndpoints = env.RegisterBoolVar("UNSAFE_ENABLE_ADMIN_ENDPOINTS", false,
"If this is set to true, dangerous admin endpoints will be exposed on the debug interface. Not recommended for production.").Get()
+ DebugAuth = env.RegisterBoolVar("DEBUG_AUTH", true,
+ "If this is set to false, the debug interface will allow all anonymous request from any remote host, which is not recommended for production").Get()
+
XDSAuth = env.RegisterBoolVar("XDS_AUTH", true,
"If true, will authenticate XDS clients.").Get()
diff --color -Naur istio/pilot/pkg/xds/debug.go istio_new/pilot/pkg/xds/debug.go
--- istio/pilot/pkg/xds/debug.go 2023-06-18 20:13:57.695044739 +0800
+++ istio_new/pilot/pkg/xds/debug.go 2023-06-18 20:11:40.286406579 +0800
@@ -218,8 +218,12 @@
if internalMux != nil {
internalMux.HandleFunc(path, handler)
}
+ handlerFunc := http.HandlerFunc(handler)
+ if features.DebugAuth {
+ handlerFunc = s.allowAuthenticatedOrLocalhost(handlerFunc)
+ }
// Add handler with auth; this is expose on an HTTP server
- mux.HandleFunc(path, s.allowAuthenticatedOrLocalhost(http.HandlerFunc(handler)))
+ mux.HandleFunc(path, handlerFunc)
}
func (s *DiscoveryServer) allowAuthenticatedOrLocalhost(next http.Handler) http.HandlerFunc {

View File

@@ -0,0 +1,15 @@
diff -Naur istio/pilot/pkg/xds/debug.go istio-new/pilot/pkg/xds/debug.go
--- istio/pilot/pkg/xds/debug.go 2023-06-27 14:08:00.000000000 +0800
+++ istio-new/pilot/pkg/xds/debug.go 2023-06-27 14:07:04.000000000 +0800
@@ -469,6 +469,11 @@
s.Env.IstioConfigStore.Schemas().ForEach(func(schema collection.Schema) bool {
cfg, _ := s.Env.IstioConfigStore.List(schema.Resource().GroupVersionKind(), "")
// Added by ingress
+ copied := make([]config.Config, len(cfg))
+ for i := range copied {
+ copied[i] = cfg[i].DeepCopy()
+ }
+ cfg = copied
switch schema.Resource().GroupVersionKind().String() {
case gvk.Gateway.String():
cfg = model.GatewayFilter(cfg)

View File

@@ -15,6 +15,7 @@
package config
import (
"bytes"
"encoding/json"
"errors"
"fmt"
@@ -25,6 +26,7 @@ import (
wasm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/wasm/v3"
httppb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/wasm/v3"
"github.com/gogo/protobuf/jsonpb"
"github.com/gogo/protobuf/types"
"github.com/golang/protobuf/ptypes/wrappers"
"google.golang.org/protobuf/types/known/anypb"
@@ -42,10 +44,13 @@ import (
"k8s.io/client-go/tools/cache"
higressext "github.com/alibaba/higress/api/extensions/v1alpha1"
higressv1 "github.com/alibaba/higress/api/networking/v1"
extlisterv1 "github.com/alibaba/higress/client/pkg/listers/extensions/v1alpha1"
netlisterv1 "github.com/alibaba/higress/client/pkg/listers/networking/v1"
"github.com/alibaba/higress/pkg/ingress/kube/annotations"
"github.com/alibaba/higress/pkg/ingress/kube/common"
"github.com/alibaba/higress/pkg/ingress/kube/configmap"
"github.com/alibaba/higress/pkg/ingress/kube/http2rpc"
"github.com/alibaba/higress/pkg/ingress/kube/ingress"
"github.com/alibaba/higress/pkg/ingress/kube/ingressv1"
"github.com/alibaba/higress/pkg/ingress/kube/mcpbridge"
@@ -54,12 +59,30 @@ import (
"github.com/alibaba/higress/pkg/ingress/kube/wasmplugin"
. "github.com/alibaba/higress/pkg/ingress/log"
"github.com/alibaba/higress/pkg/kube"
"github.com/alibaba/higress/registry/memory"
"github.com/alibaba/higress/registry/reconcile"
)
var (
_ model.ConfigStoreCache = &IngressConfig{}
_ model.IngressStore = &IngressConfig{}
_ model.ConfigStoreCache = &IngressConfig{}
_ model.IngressStore = &IngressConfig{}
Http2RpcMethodMap = func() map[string]string {
return map[string]string{
"GET": "ALL_GET",
"POST": "ALL_POST",
"PUT": "ALL_PUT",
"DELETE": "ALL_DELETE",
"PATCH": "ALL_PATCH",
}
}
Http2RpcParamSourceMap = func() map[string]string {
return map[string]string{
"QUERY": "ALL_QUERY_PARAMETER",
"HEADER": "ALL_HEADER",
"PATH": "ALL_PATH",
"BODY": "ALL_BODY",
}
}
)
type IngressConfig struct {
@@ -98,6 +121,14 @@ type IngressConfig struct {
wasmPlugins map[string]*extensions.WasmPlugin
http2rpcController http2rpc.Http2RpcController
http2rpcLister netlisterv1.Http2RpcLister
http2rpcs map[string]*higressv1.Http2Rpc
configmapMgr *configmap.ConfigmapMgr
XDSUpdater model.XDSUpdater
annotationHandler annotations.AnnotationHandler
@@ -125,6 +156,7 @@ func NewIngressConfig(localKubeClient kube.Client, XDSUpdater model.XDSUpdater,
namespace: namespace,
mcpbridgeReconciled: true,
wasmPlugins: make(map[string]*extensions.WasmPlugin),
http2rpcs: make(map[string]*higressv1.Http2Rpc),
}
mcpbridgeController := mcpbridge.NewController(localKubeClient, clusterId)
mcpbridgeController.AddEventHandler(config.AddOrUpdateMcpBridge, config.DeleteMcpBridge)
@@ -135,6 +167,15 @@ func NewIngressConfig(localKubeClient kube.Client, XDSUpdater model.XDSUpdater,
wasmPluginController.AddEventHandler(config.AddOrUpdateWasmPlugin, config.DeleteWasmPlugin)
config.wasmPluginController = wasmPluginController
config.wasmPluginLister = wasmPluginController.Lister()
http2rpcController := http2rpc.NewController(localKubeClient, clusterId)
http2rpcController.AddEventHandler(config.AddOrUpdateHttp2Rpc, config.DeleteHttp2Rpc)
config.http2rpcController = http2rpcController
config.http2rpcLister = http2rpcController.Lister()
higressConfigController := configmap.NewController(localKubeClient, clusterId, namespace)
config.configmapMgr = configmap.NewConfigmapMgr(XDSUpdater, namespace, higressConfigController, higressConfigController.Lister())
return config
}
@@ -207,8 +248,24 @@ func (m *IngressConfig) List(typ config.GroupVersionKind, namespace string) ([]c
if typ == gvk.EnvoyFilter {
m.mutex.RLock()
defer m.mutex.RUnlock()
IngressLog.Infof("resource type %s, configs number %d", typ, len(m.cachedEnvoyFilters))
return m.cachedEnvoyFilters, nil
var envoyFilters []config.Config
// Build configmap envoy filters
configmapEnvoyFilters, err := m.configmapMgr.ConstructEnvoyFilters()
if err != nil {
IngressLog.Errorf("Construct configmap EnvoyFilters error %v", err)
} else {
for _, envoyFilter := range configmapEnvoyFilters {
envoyFilters = append(envoyFilters, *envoyFilter)
}
IngressLog.Infof("Append %d configmap EnvoyFilters", len(configmapEnvoyFilters))
}
if len(envoyFilters) == 0 {
IngressLog.Infof("resource type %s, configs number %d", typ, len(m.cachedEnvoyFilters))
return m.cachedEnvoyFilters, nil
}
envoyFilters = append(envoyFilters, m.cachedEnvoyFilters...)
IngressLog.Infof("resource type %s, configs number %d", typ, len(envoyFilters))
return envoyFilters, nil
}
var configs []config.Config
@@ -458,6 +515,18 @@ func (m *IngressConfig) convertEnvoyFilter(convertOptions *common.ConvertOptions
continue
}
http2rpc := route.WrapperConfig.AnnotationsConfig.Http2Rpc
if http2rpc != nil {
IngressLog.Infof("Found http2rpc for name %s", http2rpc.Name)
envoyFilter, err := m.constructHttp2RpcEnvoyFilter(http2rpc, route, m.namespace)
if err != nil {
IngressLog.Errorf("Construct http2rpc EnvoyFilter error %v", err)
} else {
IngressLog.Infof("Append http2rpc EnvoyFilter for name %s", http2rpc.Name)
envoyFilters = append(envoyFilters, *envoyFilter)
}
}
auth := route.WrapperConfig.AnnotationsConfig.Auth
if auth == nil {
continue
@@ -521,6 +590,7 @@ func (m *IngressConfig) convertServiceEntry([]common.WrapperConfig) []config.Con
return nil
}
serviceEntries := m.RegistryReconciler.GetAllServiceEntryWrapper()
IngressLog.Infof("Found http2rpc serviceEntries %s", serviceEntries)
out := make([]config.Config, 0, len(serviceEntries))
for _, se := range serviceEntries {
out = append(out, config.Config{
@@ -912,6 +982,38 @@ func (m *IngressConfig) DeleteMcpBridge(clusterNamespacedName util.ClusterNamesp
}
}
func (m *IngressConfig) AddOrUpdateHttp2Rpc(clusterNamespacedName util.ClusterNamespacedName) {
if clusterNamespacedName.Namespace != m.namespace {
return
}
http2rpc, err := m.http2rpcLister.Http2Rpcs(clusterNamespacedName.Namespace).Get(clusterNamespacedName.Name)
if err != nil {
IngressLog.Errorf("http2rpc is not found, namespace:%s, name:%s",
clusterNamespacedName.Namespace, clusterNamespacedName.Name)
return
}
m.mutex.Lock()
m.http2rpcs[clusterNamespacedName.Name] = &http2rpc.Spec
m.mutex.Unlock()
IngressLog.Infof("AddOrUpdateHttp2Rpc http2rpc ingress name %s", clusterNamespacedName.Name)
}
func (m *IngressConfig) DeleteHttp2Rpc(clusterNamespacedName util.ClusterNamespacedName) {
if clusterNamespacedName.Namespace != m.namespace {
return
}
var hit bool
m.mutex.Lock()
if _, ok := m.http2rpcs[clusterNamespacedName.Name]; ok {
delete(m.http2rpcs, clusterNamespacedName.Name)
hit = true
}
m.mutex.Unlock()
if hit {
IngressLog.Debugf("Http2Rpc triggerd deleted %s", clusterNamespacedName.Name)
}
}
func (m *IngressConfig) ReflectSecretChanges(clusterNamespacedName util.ClusterNamespacedName) {
var hit bool
m.mutex.RLock()
@@ -996,6 +1098,188 @@ func (m *IngressConfig) applyCanaryIngresses(convertOptions *common.ConvertOptio
}
}
func (m *IngressConfig) constructHttp2RpcEnvoyFilter(http2rpcConfig *annotations.Http2RpcConfig, route *common.WrapperHTTPRoute, namespace string) (*config.Config, error) {
mappings := m.http2rpcs
IngressLog.Infof("Found http2rpc mappings %v", mappings)
if _, exist := mappings[http2rpcConfig.Name]; !exist {
IngressLog.Errorf("Http2RpcConfig name %s, not found Http2Rpc CRD", http2rpcConfig.Name)
return nil, errors.New("invalid http2rpcConfig has no useable http2rpc")
}
http2rpcCRD := mappings[http2rpcConfig.Name]
if http2rpcCRD.GetDubbo() == nil {
IngressLog.Errorf("Http2RpcConfig name %s, only support Http2Rpc CRD Dubbo Service type", http2rpcConfig.Name)
return nil, errors.New("invalid http2rpcConfig has no useable http2rpc")
}
httpRoute := route.HTTPRoute
httpRouteDestination := httpRoute.Route[0]
typeStruct, err := m.constructHttp2RpcMethods(http2rpcCRD.GetDubbo())
if err != nil {
return nil, errors.New(err.Error())
}
return &config.Config{
Meta: config.Meta{
GroupVersionKind: gvk.EnvoyFilter,
Name: common.CreateConvertedName(constants.IstioIngressGatewayName, http2rpcConfig.Name),
Namespace: namespace,
},
Spec: &networking.EnvoyFilter{
ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{
{
ApplyTo: networking.EnvoyFilter_HTTP_FILTER,
Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{
Context: networking.EnvoyFilter_GATEWAY,
ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{
Listener: &networking.EnvoyFilter_ListenerMatch{
FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{
Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{
Name: "envoy.filters.network.http_connection_manager",
SubFilter: &networking.EnvoyFilter_ListenerMatch_SubFilterMatch{
Name: "envoy.filters.http.router",
},
},
},
},
},
},
Patch: &networking.EnvoyFilter_Patch{
Operation: networking.EnvoyFilter_Patch_INSERT_BEFORE,
Value: buildPatchStruct(`{
"name":"envoy.filters.http.http_dubbo_transcoder",
"typed_config":{
"@type":"type.googleapis.com/udpa.type.v1.TypedStruct",
"type_url":"type.googleapis.com/envoy.extensions.filters.http.http_dubbo_transcoder.v3.HttpDubboTranscoder"
}
}`),
},
},
{
ApplyTo: networking.EnvoyFilter_HTTP_ROUTE,
Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{
Context: networking.EnvoyFilter_GATEWAY,
ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_RouteConfiguration{
RouteConfiguration: &networking.EnvoyFilter_RouteConfigurationMatch{
Vhost: &networking.EnvoyFilter_RouteConfigurationMatch_VirtualHostMatch{
Route: &networking.EnvoyFilter_RouteConfigurationMatch_RouteMatch{
Name: httpRoute.Name,
},
},
},
},
},
Patch: &networking.EnvoyFilter_Patch{
Operation: networking.EnvoyFilter_Patch_MERGE,
Value: typeStruct,
},
},
{
ApplyTo: networking.EnvoyFilter_CLUSTER,
Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{
Context: networking.EnvoyFilter_GATEWAY,
ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Cluster{
Cluster: &networking.EnvoyFilter_ClusterMatch{
Service: httpRouteDestination.Destination.Host,
},
},
},
Patch: &networking.EnvoyFilter_Patch{
Operation: networking.EnvoyFilter_Patch_MERGE,
Value: buildPatchStruct(`{
"upstream_config": {
"name":"envoy.upstreams.http.dubbo_tcp",
"typed_config":{
"@type":"type.googleapis.com/udpa.type.v1.TypedStruct",
"type_url":"type.googleapis.com/envoy.extensions.upstreams.http.dubbo_tcp.v3.DubboTcpConnectionPoolProto"
}
}
}`),
},
},
},
},
}, nil
}
func (m *IngressConfig) constructHttp2RpcMethods(dubbo *higressv1.DubboService) (*types.Struct, error) {
httpRouterTemplate := `{
"route": {
"upgrade_configs": [
{
"connect_config": {
"allow_post": true
},
"upgrade_type": "CONNECT"
}
]
},
"typed_per_filter_config": {
"envoy.filters.http.http_dubbo_transcoder": {
"@type": "type.googleapis.com/udpa.type.v1.TypedStruct",
"type_url": "type.googleapis.com/envoy.extensions.filters.http.http_dubbo_transcoder.v3.HttpDubboTranscoder",
"value": {
"request_validation_options": {
"reject_unknown_method": true,
"reject_unknown_query_parameters": true
},
"services_mapping": %s,
"url_unescape_spec": "ALL_CHARACTERS_EXCEPT_RESERVED"
}
}
}
}`
var methods []interface{}
for _, serviceMethod := range dubbo.GetMethods() {
var method = make(map[string]interface{})
method["name"] = serviceMethod.GetServiceMethod()
var params []interface{}
for _, methodParam := range serviceMethod.GetParams() {
var param = make(map[string]interface{})
param["extract_key"] = methodParam.GetParamKey()
param["extract_key_spec"] = Http2RpcParamSourceMap()[methodParam.GetParamSource()]
param["mapping_type"] = methodParam.GetParamType()
params = append(params, param)
}
method["parameter_mapping"] = params
var path_matcher = make(map[string]interface{})
path_matcher["match_http_method_spec"] = Http2RpcMethodMap()[serviceMethod.HttpMethods[0]]
path_matcher["match_pattern"] = serviceMethod.GetHttpPath()
method["path_matcher"] = path_matcher
var passthrough_setting = make(map[string]interface{})
var headersAttach = serviceMethod.GetHeadersAttach()
if headersAttach == "" {
passthrough_setting["passthrough_all_headers"] = false
} else if headersAttach == "*" {
passthrough_setting["passthrough_all_headers"] = true
} else {
passthrough_setting["passthrough_headers"] = headersAttach
}
method["passthrough_setting"] = passthrough_setting
methods = append(methods, method)
}
var serviceMapping = make(map[string]interface{})
var dubboServiceGroup = dubbo.GetGroup()
if dubboServiceGroup != "" {
serviceMapping["group"] = dubboServiceGroup
}
serviceMapping["name"] = dubbo.GetService()
serviceMapping["version"] = dubbo.GetVersion()
serviceMapping["method_mapping"] = methods
strBuffer := new(bytes.Buffer)
serviceMappingJsonStr, _ := json.Marshal(serviceMapping)
fmt.Fprintf(strBuffer, httpRouterTemplate, string(serviceMappingJsonStr))
IngressLog.Infof("Found http2rpc buildHttp2RpcMethods %s", strBuffer.String())
result := buildPatchStruct(strBuffer.String())
return result, nil
}
func buildPatchStruct(config string) *types.Struct {
val := &types.Struct{}
_ = jsonpb.Unmarshal(strings.NewReader(config), val)
return val
}
func constructBasicAuthEnvoyFilter(rules *common.BasicAuthRules, namespace string) (*config.Config, error) {
rulesStr, err := json.Marshal(rules)
if err != nil {
@@ -1079,9 +1363,35 @@ func constructBasicAuthEnvoyFilter(rules *common.BasicAuthRules, namespace strin
}, nil
}
func QueryByName(serviceEntries []*memory.ServiceEntryWrapper, serviceName string) (*memory.ServiceEntryWrapper, error) {
IngressLog.Infof("Found http2rpc serviceEntries %s", serviceEntries)
for _, se := range serviceEntries {
if se.ServiceName == serviceName {
return se, nil
}
}
return nil, fmt.Errorf("can't find ServiceEntry by serviceName:%v", serviceName)
}
func QueryRpcServiceVersion(serviceEntry *memory.ServiceEntryWrapper, serviceName string) (string, error) {
IngressLog.Infof("Found http2rpc serviceEntry %s", serviceEntry)
IngressLog.Infof("Found http2rpc ServiceEntry %s", serviceEntry.ServiceEntry)
IngressLog.Infof("Found http2rpc WorkloadSelector %s", serviceEntry.ServiceEntry.WorkloadSelector)
IngressLog.Infof("Found http2rpc Labels %s", serviceEntry.ServiceEntry.WorkloadSelector.Labels)
labels := (*serviceEntry).ServiceEntry.WorkloadSelector.Labels
for key, value := range labels {
if key == "version" {
return value, nil
}
}
return "", fmt.Errorf("can't get RpcServiceVersion for serviceName:%v", serviceName)
}
func (m *IngressConfig) Run(stop <-chan struct{}) {
go m.mcpbridgeController.Run(stop)
go m.wasmPluginController.Run(stop)
go m.http2rpcController.Run(stop)
go m.configmapMgr.HigressConfigController.Run(stop)
}
func (m *IngressConfig) HasSynced() bool {
@@ -1098,6 +1408,12 @@ func (m *IngressConfig) HasSynced() bool {
if !m.wasmPluginController.HasSynced() {
return false
}
if !m.http2rpcController.HasSynced() {
return false
}
if !m.configmapMgr.HigressConfigController.HasSynced() {
return false
}
IngressLog.Info("Ingress config controller synced.")
return true
}

View File

@@ -69,6 +69,8 @@ type Ingress struct {
Match *MatchConfig
HeaderControl *HeaderControlConfig
Http2Rpc *Http2RpcConfig
}
func (i *Ingress) NeedRegexMatch() bool {
@@ -76,7 +78,15 @@ func (i *Ingress) NeedRegexMatch() bool {
return false
}
return i.Rewrite.RewriteTarget != "" || i.Rewrite.UseRegex
return i.Rewrite.RewriteTarget != "" || i.IsPrefixRegexMatch() || i.IsFullPathRegexMatch()
}
func (i *Ingress) IsPrefixRegexMatch() bool {
return i.Rewrite.UseRegex
}
func (i *Ingress) IsFullPathRegexMatch() bool {
return i.Rewrite.FullPathRegex
}
func (i *Ingress) IsCanary() bool {
@@ -141,6 +151,7 @@ func NewAnnotationHandlerManager() AnnotationHandler {
ignoreCaseMatching{},
match{},
headerControl{},
http2rpc{},
},
gatewayHandlers: []GatewayHandler{
downstreamTLS{},

View File

@@ -0,0 +1,53 @@
// Copyright (c) 2023 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 (
. "github.com/alibaba/higress/pkg/ingress/log"
)
const (
http2rpcKey = "http2rpc-name"
rpcDestinationName = "rpc-destination-name"
)
// help to conform http2rpc implements method of Parse
var _ Parser = http2rpc{}
type Http2RpcConfig struct {
Name string
}
type http2rpc struct{}
func (a http2rpc) Parse(annotations Annotations, config *Ingress, _ *GlobalContext) error {
if !needHttp2RpcConfig(annotations) {
return nil
}
value, err := annotations.ParseStringForHigress(rpcDestinationName)
IngressLog.Infof("Parse http2rpc ingress name %s", value)
if err != nil {
IngressLog.Errorf("parse http2rpc error %v within ingress %s/%s", err, config.Namespace, config.Name)
return nil
}
config.Http2Rpc = &Http2RpcConfig{
Name: value,
}
return nil
}
func needHttp2RpcConfig(annotations Annotations) bool {
return annotations.HasHigress(rpcDestinationName)
}

View File

@@ -0,0 +1,59 @@
// Copyright (c) 2022 Alibaba Group Holding Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package annotations
import (
"testing"
"github.com/google/go-cmp/cmp"
)
func TestHttp2RpcParse(t *testing.T) {
parser := http2rpc{}
testCases := []struct {
input Annotations
expect *Http2RpcConfig
}{
{
input: Annotations{},
expect: nil,
},
{
input: Annotations{
buildHigressAnnotationKey(rpcDestinationName): "",
},
expect: nil,
},
{
input: Annotations{
buildHigressAnnotationKey(rpcDestinationName): "http-dubbo-alibaba-nacos-example-DemoService",
},
expect: &Http2RpcConfig{
Name: "http-dubbo-alibaba-nacos-example-DemoService",
},
},
}
for _, testCase := range testCases {
t.Run("", func(t *testing.T) {
config := &Ingress{}
_ = parser.Parse(testCase.input, config, nil)
if diff := cmp.Diff(config.Http2Rpc, testCase.expect); diff != "" {
t.Fatalf("TestHttp2RpcParse() mismatch: (-want +got)\n%s", diff)
}
})
}
}

View File

@@ -25,6 +25,7 @@ const (
rewritePath = "rewrite-path"
rewriteTarget = "rewrite-target"
useRegex = "use-regex"
fullPathRegex = "full-path-regex"
upstreamVhost = "upstream-vhost"
re2Regex = "\\$[0-9]"
@@ -38,6 +39,7 @@ var (
type RewriteConfig struct {
RewriteTarget string
UseRegex bool
FullPathRegex bool
RewriteHost string
RewritePath string
}
@@ -52,13 +54,16 @@ func (r rewrite) Parse(annotations Annotations, config *Ingress, _ *GlobalContex
rewriteConfig := &RewriteConfig{}
rewriteConfig.RewriteTarget, _ = annotations.ParseStringASAP(rewriteTarget)
rewriteConfig.UseRegex, _ = annotations.ParseBoolASAP(useRegex)
rewriteConfig.FullPathRegex, _ = annotations.ParseBoolForHigress(fullPathRegex)
rewriteConfig.RewriteHost, _ = annotations.ParseStringASAP(upstreamVhost)
rewriteConfig.RewritePath, _ = annotations.ParseStringForHigress(rewritePath)
if rewriteConfig.RewritePath == "" && rewriteConfig.RewriteTarget != "" {
// When rewrite target is present and not empty,
// we will enforce regex match on all rules in this ingress.
rewriteConfig.UseRegex = true
if !rewriteConfig.UseRegex && !rewriteConfig.FullPathRegex {
rewriteConfig.UseRegex = true
}
// We should convert nginx regex rule to envoy regex rule.
rewriteConfig.RewriteTarget = convertToRE2(rewriteConfig.RewriteTarget)
@@ -108,12 +113,13 @@ func convertToRE2(target string) string {
func NeedRegexMatch(annotations map[string]string) bool {
target, _ := Annotations(annotations).ParseStringASAP(rewriteTarget)
regex, _ := Annotations(annotations).ParseBoolASAP(useRegex)
useRegex, _ := Annotations(annotations).ParseBoolASAP(useRegex)
fullPathRegex, _ := Annotations(annotations).ParseBoolForHigress(fullPathRegex)
return regex || target != ""
return useRegex || target != "" || fullPathRegex
}
func needRewriteConfig(annotations Annotations) bool {
return annotations.HasASAP(rewriteTarget) || annotations.HasASAP(useRegex) ||
annotations.HasASAP(upstreamVhost) || annotations.HasHigress(rewritePath)
annotations.HasASAP(upstreamVhost) || annotations.HasHigress(rewritePath) || annotations.HasHigress(fullPathRegex)
}

View File

@@ -59,7 +59,11 @@ const (
Prefix PathType = "prefix"
Regex PathType = "regex"
// PrefixRegex :if PathType is PrefixRegex, then the /foo/bar/[A-Z0-9]{3} is actually ^/foo/bar/[A-Z0-9]{3}.*
PrefixRegex PathType = "prefixRegex"
// FullPathRegex :if PathType is FullPathRegex, then the /foo/bar/[A-Z0-9]{3} is actually ^/foo/bar/[A-Z0-9]{3}$
FullPathRegex PathType = "fullPathRegex"
DefaultStatusUpdateInterval = 10 * time.Second

View File

@@ -43,11 +43,11 @@ func TestConstructRouteName(t *testing.T) {
{
input: &WrapperHTTPRoute{
Host: "*.test.com",
OriginPathType: Regex,
OriginPathType: PrefixRegex,
OriginPath: "/test/(.*)/?[0-9]",
HTTPRoute: &networking.HTTPRoute{},
},
expect: "*.test.com-regex-/test/(.*)/?[0-9]",
expect: "*.test.com-prefixRegex-/test/(.*)/?[0-9]",
},
{
input: &WrapperHTTPRoute{
@@ -390,7 +390,7 @@ func TestSortRoutes(t *testing.T) {
AnnotationsConfig: &annotations.Ingress{},
},
Host: "test.com",
OriginPathType: Regex,
OriginPathType: PrefixRegex,
OriginPath: "/d(.*)",
ClusterId: "cluster1",
HTTPRoute: &networking.HTTPRoute{

View File

@@ -0,0 +1,50 @@
// Copyright (c) 2022 Alibaba Group Holding Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package configmap
import (
"encoding/json"
)
type Result int32
const (
ResultNothing Result = iota
ResultReplace
ResultDelete
HigressConfigMapName = "higress-config"
HigressConfigMapKey = "higress"
ModelUpdatedReason = "higress configmap updated"
)
type ItemEventHandler = func(name string)
type HigressConfig struct {
Tracing *Tracing `json:"tracing,omitempty"`
}
func NewDefaultHigressConfig() *HigressConfig {
higressConfig := &HigressConfig{
Tracing: NewDefaultTracing(),
}
return higressConfig
}
func GetHigressConfigString(higressConfig *HigressConfig) string {
bytes, _ := json.Marshal(higressConfig)
return string(bytes)
}

View File

@@ -0,0 +1,202 @@
// 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 configmap
import (
"reflect"
"sync/atomic"
"github.com/alibaba/higress/pkg/ingress/kube/controller"
"github.com/alibaba/higress/pkg/ingress/kube/util"
. "github.com/alibaba/higress/pkg/ingress/log"
"istio.io/istio/pilot/pkg/model"
"istio.io/istio/pkg/config"
"istio.io/istio/pkg/config/schema/gvk"
kubeclient "istio.io/istio/pkg/kube"
"istio.io/istio/pkg/kube/controllers"
"k8s.io/apimachinery/pkg/types"
listersv1 "k8s.io/client-go/listers/core/v1"
"sigs.k8s.io/yaml"
)
type HigressConfigController controller.Controller[listersv1.ConfigMapNamespaceLister]
func NewController(client kubeclient.Client, clusterId string, namespace string) HigressConfigController {
informer := client.KubeInformer().Core().V1().ConfigMaps().Informer()
return controller.NewCommonController("higressConfig", client.KubeInformer().Core().V1().ConfigMaps().Lister().ConfigMaps(namespace),
informer, GetConfigmap, clusterId)
}
func GetConfigmap(lister listersv1.ConfigMapNamespaceLister, namespacedName types.NamespacedName) (controllers.Object, error) {
return lister.Get(namespacedName.Name)
}
type ItemController interface {
GetName() string
AddOrUpdateHigressConfig(name util.ClusterNamespacedName, old *HigressConfig, new *HigressConfig) error
ValidHigressConfig(higressConfig *HigressConfig) error
ConstructEnvoyFilters() ([]*config.Config, error)
RegisterItemEventHandler(eventHandler ItemEventHandler)
}
type ConfigmapMgr struct {
Namespace string
HigressConfigController HigressConfigController
HigressConfigLister listersv1.ConfigMapNamespaceLister
higressConfig atomic.Value
ItemControllers []ItemController
XDSUpdater model.XDSUpdater
}
func NewConfigmapMgr(XDSUpdater model.XDSUpdater, namespace string, higressConfigController HigressConfigController, higressConfigLister listersv1.ConfigMapNamespaceLister) *ConfigmapMgr {
configmapMgr := &ConfigmapMgr{
XDSUpdater: XDSUpdater,
Namespace: namespace,
HigressConfigController: higressConfigController,
HigressConfigLister: higressConfigLister,
higressConfig: atomic.Value{},
}
configmapMgr.HigressConfigController.AddEventHandler(configmapMgr.AddOrUpdateHigressConfig)
configmapMgr.SetHigressConfig(NewDefaultHigressConfig())
tracingController := NewTracingController(namespace)
configmapMgr.AddItemControllers(tracingController)
configmapMgr.initEventHandlers()
return configmapMgr
}
func (c *ConfigmapMgr) SetHigressConfig(higressConfig *HigressConfig) {
c.higressConfig.Store(higressConfig)
}
func (c *ConfigmapMgr) GetHigressConfig() *HigressConfig {
value := c.higressConfig.Load()
if value != nil {
if higressConfig, ok := value.(*HigressConfig); ok {
return higressConfig
}
}
return nil
}
func (c *ConfigmapMgr) AddItemControllers(controllers ...ItemController) {
c.ItemControllers = append(c.ItemControllers, controllers...)
}
func (c *ConfigmapMgr) AddOrUpdateHigressConfig(name util.ClusterNamespacedName) {
if name.Namespace != c.Namespace || name.Name != HigressConfigMapName {
return
}
IngressLog.Infof("configmapMgr AddOrUpdateHigressConfig")
higressConfigmap, err := c.HigressConfigLister.Get(HigressConfigMapName)
if err != nil {
IngressLog.Errorf("higress-config configmap is not found, namespace:%s, name:%s",
name.Namespace, name.Name)
return
}
if _, ok := higressConfigmap.Data[HigressConfigMapKey]; !ok {
return
}
newHigressConfig := NewDefaultHigressConfig()
if err = yaml.Unmarshal([]byte(higressConfigmap.Data[HigressConfigMapKey]), newHigressConfig); err != nil {
IngressLog.Errorf("data:%s, convert to higress config error, error: %+v", higressConfigmap.Data[HigressConfigMapKey], err)
return
}
for _, itemController := range c.ItemControllers {
if itemErr := itemController.ValidHigressConfig(newHigressConfig); itemErr != nil {
IngressLog.Errorf("configmap %s controller valid higress config error, error: %+v", itemController.GetName(), itemErr)
return
}
}
oldHigressConfig := c.GetHigressConfig()
IngressLog.Infof("configmapMgr oldHigressConfig: %s", GetHigressConfigString(oldHigressConfig))
IngressLog.Infof("configmapMgr newHigressConfig: %s", GetHigressConfigString(newHigressConfig))
result, _ := c.CompareHigressConfig(oldHigressConfig, newHigressConfig)
IngressLog.Infof("configmapMgr CompareHigressConfig reuslt is %d", result)
if result == ResultNothing {
return
}
if result == ResultDelete {
newHigressConfig = NewDefaultHigressConfig()
}
if result == ResultReplace || result == ResultDelete {
// Pass AddOrUpdateHigressConfig to itemControllers
for _, itemController := range c.ItemControllers {
IngressLog.Infof("configmap %s controller AddOrUpdateHigressConfig", itemController.GetName())
if itemErr := itemController.AddOrUpdateHigressConfig(name, oldHigressConfig, newHigressConfig); itemErr != nil {
IngressLog.Errorf("configmap %s controller AddOrUpdateHigressConfig error, error: %+v", itemController.GetName(), itemErr)
}
}
c.SetHigressConfig(newHigressConfig)
IngressLog.Infof("configmapMgr higress config AddOrUpdate success, reuslt is %d", result)
// Call updateConfig
}
}
func (c *ConfigmapMgr) ConstructEnvoyFilters() ([]*config.Config, error) {
configs := make([]*config.Config, 0)
for _, itemController := range c.ItemControllers {
IngressLog.Infof("controller %s ConstructEnvoyFilters", itemController.GetName())
if itemConfigs, err := itemController.ConstructEnvoyFilters(); err != nil {
IngressLog.Errorf("controller %s ConstructEnvoyFilters error, error: %+v", itemController.GetName(), err)
} else {
configs = append(configs, itemConfigs...)
}
}
return configs, nil
}
func (c *ConfigmapMgr) CompareHigressConfig(old *HigressConfig, new *HigressConfig) (Result, error) {
if old == nil || new == nil {
return ResultNothing, nil
}
if !reflect.DeepEqual(old, new) {
return ResultReplace, nil
}
return ResultNothing, nil
}
func (c *ConfigmapMgr) initEventHandlers() error {
itemEventHandler := func(name string) {
c.XDSUpdater.ConfigUpdate(&model.PushRequest{
Full: true,
ConfigsUpdated: map[model.ConfigKey]struct{}{{
Kind: gvk.EnvoyFilter,
Name: name,
Namespace: c.Namespace,
}: {}},
Reason: []model.TriggerReason{ModelUpdatedReason},
})
}
for _, itemController := range c.ItemControllers {
itemController.RegisterItemEventHandler(itemEventHandler)
}
return nil
}

View File

@@ -0,0 +1,408 @@
// 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 configmap
import (
"encoding/json"
"errors"
"fmt"
"reflect"
"sync/atomic"
"github.com/alibaba/higress/pkg/ingress/kube/util"
. "github.com/alibaba/higress/pkg/ingress/log"
networking "istio.io/api/networking/v1alpha3"
"istio.io/istio/pkg/config"
"istio.io/istio/pkg/config/schema/gvk"
)
const (
higressTracingEnvoyFilterName = "higress-config-tracing"
defaultTimeout = 500
defaultSampling = 100.0
)
type Tracing struct {
// Flag to control trace
Enable bool `json:"enable,omitempty"`
// The percentage of requests (0.0 - 100.0) that will be randomly selected for trace generation,
// if not requested by the client or not forced. Default is 100.0.
Sampling float64 `json:"sampling,omitempty"`
// The timeout for the gRPC request. Default is 500ms
Timeout int32 `json:"timeout,omitempty"`
// The tracer implementation to be used by Envoy.
//
// Types that are assignable to Tracer:
Zipkin *Zipkin `json:"zipkin,omitempty"`
Skywalking *Skywalking `json:"skywalking,omitempty"`
OpenTelemetry *OpenTelemetry `json:"opentelemetry,omitempty"`
}
// Zipkin defines configuration for a Zipkin tracer.
type Zipkin struct {
// Address of the Zipkin service (e.g. _zipkin:9411_).
Service string `json:"service,omitempty"`
Port string `json:"port,omitempty"`
}
// Skywalking Defines configuration for a Skywalking tracer.
type Skywalking struct {
// Address of the Skywalking tracer.
Service string `json:"service,omitempty"`
Port string `json:"port,omitempty"`
// The access token
AccessToken string `json:"access_token,omitempty"`
}
// OpenTelemetry Defines configuration for a OpenTelemetry tracer.
type OpenTelemetry struct {
// Address of OpenTelemetry tracer.
Service string `json:"service,omitempty"`
Port string `json:"port,omitempty"`
}
func validServiceAndPort(service string, port string) bool {
if len(service) == 0 || len(port) == 0 {
return false
}
return true
}
func validTracing(t *Tracing) error {
if t == nil {
return nil
}
if t.Timeout <= 0 {
return errors.New("timeout can not be less than zero")
}
if t.Sampling < 0 || t.Sampling > 100 {
return errors.New("sampling must be in (0.0 - 100.0)")
}
tracerNum := 0
if t.Zipkin != nil {
if validServiceAndPort(t.Zipkin.Service, t.Zipkin.Port) {
tracerNum++
} else {
return errors.New("zipkin service and port can not be empty")
}
}
if t.Skywalking != nil {
if validServiceAndPort(t.Skywalking.Service, t.Skywalking.Port) {
tracerNum++
} else {
return errors.New("skywalking service and port can not be empty")
}
}
if t.OpenTelemetry != nil {
if validServiceAndPort(t.OpenTelemetry.Service, t.OpenTelemetry.Port) {
tracerNum++
} else {
return errors.New("opentelemetry service and port can not be empty")
}
}
if tracerNum != 1 {
return errors.New("only one of skywalkingzipkin and opentelemetry configuration can be set")
}
return nil
}
func compareTracing(old *Tracing, new *Tracing) (Result, error) {
if old == nil && new == nil {
return ResultNothing, nil
}
if new == nil {
return ResultDelete, nil
}
if !reflect.DeepEqual(old, new) {
return ResultReplace, nil
}
return ResultNothing, nil
}
func deepCopyTracing(tracing *Tracing) (*Tracing, error) {
newTracing := NewDefaultTracing()
bytes, err := json.Marshal(tracing)
if err != nil {
return nil, err
}
err = json.Unmarshal(bytes, newTracing)
return newTracing, err
}
func NewDefaultTracing() *Tracing {
tracing := &Tracing{
Enable: false,
Timeout: defaultTimeout,
Sampling: defaultSampling,
}
return tracing
}
type TracingController struct {
Namespace string
tracing atomic.Value
Name string
eventHandler ItemEventHandler
}
func NewTracingController(namespace string) *TracingController {
tracingMgr := &TracingController{
Namespace: namespace,
tracing: atomic.Value{},
Name: "tracing",
}
tracingMgr.SetTracing(NewDefaultTracing())
return tracingMgr
}
func (t *TracingController) SetTracing(tracing *Tracing) {
t.tracing.Store(tracing)
}
func (t *TracingController) GetTracing() *Tracing {
value := t.tracing.Load()
if value != nil {
if tracing, ok := value.(*Tracing); ok {
return tracing
}
}
return nil
}
func (t *TracingController) GetName() string {
return t.Name
}
func (t *TracingController) AddOrUpdateHigressConfig(name util.ClusterNamespacedName, old *HigressConfig, new *HigressConfig) error {
if err := validTracing(new.Tracing); err != nil {
IngressLog.Errorf("data:%+v convert to tracing , error: %+v", new.Tracing, err)
return nil
}
result, _ := compareTracing(old.Tracing, new.Tracing)
switch result {
case ResultReplace:
if newTracing, err := deepCopyTracing(new.Tracing); err != nil {
IngressLog.Infof("tracing deepcopy error:%v", err)
} else {
t.SetTracing(newTracing)
IngressLog.Infof("AddOrUpdate Higress config tracing")
t.eventHandler(higressTracingEnvoyFilterName)
IngressLog.Infof("send event with filter name:%s", higressTracingEnvoyFilterName)
}
case ResultDelete:
t.SetTracing(NewDefaultTracing())
IngressLog.Infof("Delete Higress config tracing")
t.eventHandler(higressTracingEnvoyFilterName)
IngressLog.Infof("send event with filter name:%s", higressTracingEnvoyFilterName)
}
return nil
}
func (t *TracingController) ValidHigressConfig(higressConfig *HigressConfig) error {
if higressConfig == nil {
return nil
}
if higressConfig.Tracing == nil {
return nil
}
return validTracing(higressConfig.Tracing)
}
func (t *TracingController) RegisterItemEventHandler(eventHandler ItemEventHandler) {
t.eventHandler = eventHandler
}
func (t *TracingController) ConstructEnvoyFilters() ([]*config.Config, error) {
configs := make([]*config.Config, 0)
tracing := t.GetTracing()
namespace := t.Namespace
if tracing == nil {
return configs, nil
}
if tracing.Enable == false {
return configs, nil
}
tracingConfig := t.constructTracingTracer(tracing, namespace)
if len(tracingConfig) == 0 {
return configs, nil
}
config := &config.Config{
Meta: config.Meta{
GroupVersionKind: gvk.EnvoyFilter,
Name: higressTracingEnvoyFilterName,
Namespace: namespace,
},
Spec: &networking.EnvoyFilter{
ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{
{
ApplyTo: networking.EnvoyFilter_NETWORK_FILTER,
Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{
Context: networking.EnvoyFilter_GATEWAY,
ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{
Listener: &networking.EnvoyFilter_ListenerMatch{
FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{
Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{
Name: "envoy.filters.network.http_connection_manager",
},
},
},
},
},
Patch: &networking.EnvoyFilter_Patch{
Operation: networking.EnvoyFilter_Patch_MERGE,
Value: util.BuildPatchStruct(tracingConfig),
},
},
{
ApplyTo: networking.EnvoyFilter_HTTP_FILTER,
Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{
Context: networking.EnvoyFilter_GATEWAY,
ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{
Listener: &networking.EnvoyFilter_ListenerMatch{
FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{
Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{
Name: "envoy.filters.network.http_connection_manager",
SubFilter: &networking.EnvoyFilter_ListenerMatch_SubFilterMatch{
Name: "envoy.filters.http.router",
},
},
},
},
},
},
Patch: &networking.EnvoyFilter_Patch{
Operation: networking.EnvoyFilter_Patch_MERGE,
Value: util.BuildPatchStruct(`{
"name":"envoy.filters.http.router",
"typed_config":{
"@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router",
"start_child_span": true
}
}`),
},
},
},
},
}
configs = append(configs, config)
return configs, nil
}
func (t *TracingController) constructTracingTracer(tracing *Tracing, namespace string) string {
tracingConfig := ""
timeout := float32(tracing.Timeout) / 1000
if tracing.Skywalking != nil {
skywalking := tracing.Skywalking
tracingConfig = fmt.Sprintf(`{
"name": "envoy.filters.network.http_connection_manager",
"typed_config": {
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
"tracing": {
"provider": {
"name": "envoy.tracers.skywalking",
"typed_config": {
"@type": "type.googleapis.com/envoy.config.trace.v3.SkyWalkingConfig",
"client_config": {
"service_name": "higress-gateway.%s",
"backend_token": "%s"
},
"grpc_service": {
"envoy_grpc": {
"cluster_name": "outbound|%s||%s"
},
"timeout": "%.3fs"
}
}
},
"random_sampling": {
"value": %.1f
}
}
}
}`, namespace, skywalking.AccessToken, skywalking.Port, skywalking.Service, timeout, tracing.Sampling)
}
if tracing.Zipkin != nil {
zipkin := tracing.Zipkin
tracingConfig = fmt.Sprintf(`{
"name": "envoy.filters.network.http_connection_manager",
"typed_config": {
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
"tracing": {
"provider": {
"name": "envoy.tracers.zipkin",
"typed_config": {
"@type": "type.googleapis.com/envoy.config.trace.v3.ZipkinConfig",
"collector_cluster": "outbound|%s||%s",
"collector_endpoint": "/api/v2/spans",
"collector_hostname": "higress-gateway",
"collector_endpoint_version": "HTTP_JSON",
"split_spans_for_request": true
}
},
"random_sampling": {
"value": %.1f
}
}
}
}`, zipkin.Port, zipkin.Service, tracing.Sampling)
}
if tracing.OpenTelemetry != nil {
opentelemetry := tracing.OpenTelemetry
tracingConfig = fmt.Sprintf(`{
"name": "envoy.filters.network.http_connection_manager",
"typed_config": {
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
"tracing": {
"provider": {
"name": "envoy.tracers.opentelemetry",
"typed_config": {
"@type": "type.googleapis.com/envoy.config.trace.v3.OpenTelemetryConfig",
"service_name": "higress-gateway.%s"
"grpc_service": {
"envoy_grpc": {
"cluster_name": "outbound|%s||%s"
},
"timeout": "%.3fs"
}
}
},
"random_sampling": {
"value": %.1f
}
}
}
}`, namespace, opentelemetry.Port, opentelemetry.Service, timeout, tracing.Sampling)
}
return tracingConfig
}

View File

@@ -0,0 +1,36 @@
// 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 http2rpc
import (
"istio.io/istio/pkg/kube/controllers"
"k8s.io/apimachinery/pkg/types"
listersv1 "github.com/alibaba/higress/client/pkg/listers/networking/v1"
"github.com/alibaba/higress/pkg/ingress/kube/controller"
kubeclient "github.com/alibaba/higress/pkg/kube"
)
type Http2RpcController controller.Controller[listersv1.Http2RpcLister]
func NewController(client kubeclient.Client, clusterId string) Http2RpcController {
informer := client.HigressInformer().Networking().V1().Http2Rpcs().Informer()
return controller.NewCommonController("http2rpc", client.HigressInformer().Networking().V1().Http2Rpcs().Lister(),
informer, GetHttp2Rpc, clusterId)
}
func GetHttp2Rpc(lister listersv1.Http2RpcLister, namespacedName types.NamespacedName) (controllers.Object, error) {
return lister.Http2Rpcs(namespacedName.Namespace).Get(namespacedName.Name)
}

View File

@@ -293,8 +293,9 @@ func (c *controller) HasSynced() bool {
}
func (c *controller) List() []config.Config {
c.mutex.RLock()
out := make([]config.Config, 0, len(c.ingresses))
c.mutex.RUnlock()
for _, raw := range c.ingressInformer.GetStore().List() {
ing, ok := raw.(*ingress.Ingress)
if !ok {
@@ -534,8 +535,12 @@ func (c *controller) ConvertHTTPRoute(convertOptions *common.ConvertOptions, wra
var pathType common.PathType
originPath := httpPath.Path
if wrapper.AnnotationsConfig.NeedRegexMatch() {
pathType = common.Regex
if annotationsConfig := wrapper.AnnotationsConfig; annotationsConfig.NeedRegexMatch() {
if annotationsConfig.IsPrefixRegexMatch() {
pathType = common.PrefixRegex
} else if annotationsConfig.IsFullPathRegexMatch() {
pathType = common.FullPathRegex
}
} else {
switch *httpPath.PathType {
case ingress.PathTypeExact:
@@ -741,8 +746,12 @@ func (c *controller) ApplyCanaryIngress(convertOptions *common.ConvertOptions, w
var pathType common.PathType
originPath := httpPath.Path
if wrapper.AnnotationsConfig.NeedRegexMatch() {
pathType = common.Regex
if annotationsConfig := wrapper.AnnotationsConfig; annotationsConfig.NeedRegexMatch() {
if annotationsConfig.IsPrefixRegexMatch() {
pathType = common.PrefixRegex
} else if annotationsConfig.IsFullPathRegexMatch() {
pathType = common.FullPathRegex
}
} else {
switch *httpPath.PathType {
case ingress.PathTypeExact:
@@ -1166,10 +1175,14 @@ func (c *controller) generateHttpMatches(pathType common.PathType, path string,
httpMatch := &networking.HTTPMatchRequest{}
switch pathType {
case common.Regex:
case common.PrefixRegex:
httpMatch.Uri = &networking.StringMatch{
MatchType: &networking.StringMatch_Regex{Regex: path + ".*"},
}
case common.FullPathRegex:
httpMatch.Uri = &networking.StringMatch{
MatchType: &networking.StringMatch_Regex{Regex: path + "$"},
}
case common.Exact:
httpMatch.Uri = &networking.StringMatch{
MatchType: &networking.StringMatch_Exact{Exact: path},

View File

@@ -39,8 +39,7 @@ type statusSyncer struct {
watchedNamespace string
ingressLister ingresslister.IngressLister
ingressClassLister ingresslister.IngressClassLister
ingressLister ingresslister.IngressLister
// search service in the mse vpc
serviceLister listerv1.ServiceLister
}
@@ -48,11 +47,10 @@ type statusSyncer struct {
// newStatusSyncer creates a new instance
func newStatusSyncer(localKubeClient, client kubelib.Client, controller *controller, namespace string) *statusSyncer {
return &statusSyncer{
client: client,
controller: controller,
watchedNamespace: namespace,
ingressLister: client.KubeInformer().Networking().V1beta1().Ingresses().Lister(),
ingressClassLister: client.KubeInformer().Networking().V1beta1().IngressClasses().Lister(),
client: client,
controller: controller,
watchedNamespace: namespace,
ingressLister: client.KubeInformer().Networking().V1beta1().Ingresses().Lister(),
// search service in the mse vpc
serviceLister: localKubeClient.KubeInformer().Core().V1().Services().Lister(),
}

View File

@@ -517,8 +517,12 @@ func (c *controller) ConvertHTTPRoute(convertOptions *common.ConvertOptions, wra
var pathType common.PathType
originPath := httpPath.Path
if wrapper.AnnotationsConfig.NeedRegexMatch() {
pathType = common.Regex
if annotationsConfig := wrapper.AnnotationsConfig; annotationsConfig.NeedRegexMatch() {
if annotationsConfig.IsPrefixRegexMatch() {
pathType = common.PrefixRegex
} else if annotationsConfig.IsFullPathRegexMatch() {
pathType = common.FullPathRegex
}
} else {
switch *httpPath.PathType {
case ingress.PathTypeExact:
@@ -610,10 +614,14 @@ func (c *controller) generateHttpMatches(pathType common.PathType, path string,
httpMatch := &networking.HTTPMatchRequest{}
switch pathType {
case common.Regex:
case common.PrefixRegex:
httpMatch.Uri = &networking.StringMatch{
MatchType: &networking.StringMatch_Regex{Regex: path + ".*"},
}
case common.FullPathRegex:
httpMatch.Uri = &networking.StringMatch{
MatchType: &networking.StringMatch_Regex{Regex: path + "$"},
}
case common.Exact:
httpMatch.Uri = &networking.StringMatch{
MatchType: &networking.StringMatch_Exact{Exact: path},
@@ -747,8 +755,12 @@ func (c *controller) ApplyCanaryIngress(convertOptions *common.ConvertOptions, w
var pathType common.PathType
originPath := httpPath.Path
if wrapper.AnnotationsConfig.NeedRegexMatch() {
pathType = common.Regex
if annotationsConfig := wrapper.AnnotationsConfig; annotationsConfig.NeedRegexMatch() {
if annotationsConfig.IsPrefixRegexMatch() {
pathType = common.PrefixRegex
} else if annotationsConfig.IsFullPathRegexMatch() {
pathType = common.FullPathRegex
}
} else {
switch *httpPath.PathType {
case ingress.PathTypeExact:

View File

@@ -40,8 +40,7 @@ type statusSyncer struct {
watchedNamespace string
ingressLister ingresslister.IngressLister
ingressClassLister ingresslister.IngressClassLister
ingressLister ingresslister.IngressLister
// search service in the mse vpc
serviceLister listerv1.ServiceLister
}
@@ -49,11 +48,10 @@ type statusSyncer struct {
// newStatusSyncer creates a new instance
func newStatusSyncer(localKubeClient, client kubelib.Client, controller *controller, namespace string) *statusSyncer {
return &statusSyncer{
client: client,
controller: controller,
watchedNamespace: namespace,
ingressLister: client.KubeInformer().Networking().V1().Ingresses().Lister(),
ingressClassLister: client.KubeInformer().Networking().V1().IngressClasses().Lister(),
client: client,
controller: controller,
watchedNamespace: namespace,
ingressLister: client.KubeInformer().Networking().V1().Ingresses().Lister(),
// search service in the mse vpc
serviceLister: localKubeClient.KubeInformer().Core().V1().Services().Lister(),
}

View File

@@ -15,16 +15,9 @@
package mcpbridge
import (
"time"
"istio.io/istio/pkg/kube/controllers"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/tools/cache"
v1 "github.com/alibaba/higress/client/pkg/apis/networking/v1"
"github.com/alibaba/higress/client/pkg/clientset/versioned"
informersv1 "github.com/alibaba/higress/client/pkg/informers/externalversions/networking/v1"
listersv1 "github.com/alibaba/higress/client/pkg/listers/networking/v1"
"github.com/alibaba/higress/pkg/ingress/kube/controller"
kubeclient "github.com/alibaba/higress/pkg/kube"
@@ -33,11 +26,8 @@ import (
type McpBridgeController controller.Controller[listersv1.McpBridgeLister]
func NewController(client kubeclient.Client, clusterId string) McpBridgeController {
informer := client.HigressInformer().InformerFor(&v1.McpBridge{}, func(k versioned.Interface, resync time.Duration) cache.SharedIndexInformer {
return informersv1.NewMcpBridgeInformer(k, metav1.NamespaceAll, resync,
cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
})
return controller.NewCommonController("mcpbridge", listersv1.NewMcpBridgeLister(informer.GetIndexer()),
informer := client.HigressInformer().Networking().V1().McpBridges().Informer()
return controller.NewCommonController("mcpbridge", client.HigressInformer().Networking().V1().McpBridges().Lister(),
informer, GetMcpBridge, clusterId)
}

View File

@@ -82,3 +82,9 @@ func MessageToGoGoStruct(msg proto.Message) (*types.Struct, error) {
func CreateServiceFQDN(namespace, name string) string {
return fmt.Sprintf("%s.%s.svc.%s", name, namespace, DefaultDomainSuffix)
}
func BuildPatchStruct(config string) *types.Struct {
val := &types.Struct{}
_ = jsonpb.Unmarshal(strings.NewReader(config), val)
return val
}

View File

@@ -15,16 +15,9 @@
package wasmplugin
import (
"time"
"istio.io/istio/pkg/kube/controllers"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/tools/cache"
v1 "github.com/alibaba/higress/client/pkg/apis/extensions/v1alpha1"
"github.com/alibaba/higress/client/pkg/clientset/versioned"
informersv1 "github.com/alibaba/higress/client/pkg/informers/externalversions/extensions/v1alpha1"
listersv1 "github.com/alibaba/higress/client/pkg/listers/extensions/v1alpha1"
"github.com/alibaba/higress/pkg/ingress/kube/controller"
kubeclient "github.com/alibaba/higress/pkg/kube"
@@ -33,11 +26,8 @@ import (
type WasmPluginController controller.Controller[listersv1.WasmPluginLister]
func NewController(client kubeclient.Client, clusterId string) WasmPluginController {
informer := client.HigressInformer().InformerFor(&v1.WasmPlugin{}, func(k versioned.Interface, resync time.Duration) cache.SharedIndexInformer {
return informersv1.NewWasmPluginInformer(k, metav1.NamespaceAll, resync,
cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
})
return controller.NewCommonController("wasmplugin", listersv1.NewWasmPluginLister(informer.GetIndexer()),
informer := client.HigressInformer().Extensions().V1alpha1().WasmPlugins().Informer()
return controller.NewCommonController("wasmplugin", client.HigressInformer().Extensions().V1alpha1().WasmPlugins().Lister(),
informer, GetWasmPlugin, clusterId)
}

View File

@@ -0,0 +1,286 @@
# Function Description
The `hmac-auth` plugin implements the generation of tamper-proof signatures for HTTP requests based on HMAC algorithm, and uses the signature for identity authentication and authorization.
# Configuration Fields
| Name | Data Type | Required | Default | Description |
| ------------- | --------------- | -------------| ------ | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `consumers` | array of object | Required | - | Configures the caller of the service to authenticate the request. |
| `date_offset` | number | Optional | - | Configures the maximum allowed time deviation of the client, in seconds. It is used to parse the client's UTC time from `the Date` header of the request, and can be used to prevent replay attacks. If not configured, no validation is performed. |
| `_rules_` | array of object | Optional | - | Configures the access control list for specific routes or domains, used for authorization of requests. |
The configuration fields for each item in `consumers` are as follows :
| Name | Data Type| Required | Default| Description |
| -------- | -------- | ------------ | ------ | ----------------------------------------------------------------------- |
| `key` | string | Required | - | Configures the key extracted from the `x-ca-key` header of the request. |
| `secret` | string | Required | - | Configures the secret used to generate the signature. |
| `name` | string | Required | - | Configures the name of the consumer. |
The configuration fields for each item in `_rules_` are as follows:
| Name | Data Type | Required | Default | Description |
| ---------------- | --------------- | ------------------------------------------------- | ---------------------------- | -------------------------------------------------- |
| `_match_route_` | array of string | Optional, either `_match_route_` or `_match_domain_` must be provided | - | Configures the name of the route to match. |
| `_match_domain_` | array of string | Optional, either `_match_route_` or `_match_domain_` must be provided | - | Configures the name of the domain to match. |
| `allow` | array of string | Required | - | Configures the name of the consumer to allow for requests that match the specified route or domain. |
**Note**
- If `_rules_` is not configured, authentication is enabled for all routes on the current gateway instance by default
- For requests that pass authentication and authorization, a `X-Mse-Consumer` header will be added to the request headers to identify the name of the consumer.
# Configuration Example
The following configuration enables Hmac Auth authentication and authorization for specific routes or domains on the gateway. Note that the `key` field should not be duplicated.
## Enabling for specific routes or domains
```yaml
consumers:
- key: appKey-example-1
secret: appSecret-example-1
name: consumer-1
- key: appKey-example-2
secret: appSecret-example-2
name: consumer-2
# Configuring Fine-Grained Rules using _rules_ Field
_rules_:
# Rule 1: Matching by route name.
- _match_route_:
- route-a
- route-b
allow:
- consumer-1
# Rule 2: Applies based on domain name matching.
- _match_domain_:
- "*.example.com"
- test.com
allow:
- consumer-2
```
The `allow` field under each matching rule specifies the list of callers allowed to access under that matching condition;
In this example, `route-a` and `route-b` specified in `_match_route_` are the route names filled in when creating the gateway route. When either of these routes is matched, it will allow access to the caller named `consumer-1`, while denying access to other callers
In` _match_domain_`, `*.example.com` and `test.com` are used to match the requested domain name. When a match is found, it will allow access to the caller named `consumer-2`, while denying access to other callers
Upon successful authentication, the `X-Mse-Consumer` field will be added to the request header with the value set to the caller's name, such as `consumer-1`.。
## Enable at the Gateway Instance Level
The following configuration enables HMAC authentication at the gateway instance level.
```yaml
consumers:
- key: appKey-example-1
secret: appSecret-example-1
name: consumer-1
- key: appKey-example-2
secret: appSecret-example-2
name: consumer-2
```
# Description of Signing Mechanism
## Configuration Preparation
As mentioned in the guide above, configure the credential settings required for generating and validating signatures in the plugin configuration.
- key: Used for setting in the request header `x-ca-key`.
- secret: Used for generating the request signature.
## Client Signature Generation Method
### Overview of the Process
The process for generating a signature on the client side consists of three steps:
1. Extracting key data from the original request to obtain a string to be signed.
2. Using encryption algorithms and the configured `secret` to encrypt the key data signing string and obtain a signature.
3. Adding all headers related to the signature to the original HTTP request to obtain the final HTTP request.
As shown below :
![](https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/1745707061/p188113.png)
### Process for Extracting Signing String
To generate a signature, the client needs to extract key data from the HTTP request and combine it into a signing string. The format of the generated signing string is as follows:
```text
HTTPMethod
Accept
Content-MD5
Content-Type
Date
Headers
PathAndParameters
```
The signing string consists of the above 7 fields separated by \n. If Headers is empty, no \n is needed. If other fields are empty, the \n should still be retained. The signature is case-sensitive. Below are the rules for extracting each field:
- HTTPMethod: The HTTP method used in the request, in all capital letters, such as POST.
- Accept: The value of the Accept header in the request, which can be empty. It is recommended to explicitly set the Accept header. When Accept is empty, some HTTP clients will set the default value of `*/*`, which may cause signature verification to fail.
- Content-MD5: The value of the Content-MD5 header in the request, which can be empty. It is only calculated when there is a non-form body in the request. The following is a reference calculation method for Content-MD5 values in
```java
String content-MD5 = Base64.encodeBase64(MD5(bodyStream.getbytes("UTF-8")));
```
- Content-Type: The value of the Content-Type header in the request, which can be empty.
- Date: The value of the Date header in the request. When the` date_offset` configuration is not enabled, it can be empty. Otherwise, it will be used for time offset verification.
- Headers: Users can select specific headers to participate in the signature. There are the following rules for concatenating the signature string with headers:
- The keys of the headers participating in the signature calculation are sorted in alphabetical order and concatenated as follows:
```text
HeaderKey1 + ":" + HeaderValue1 + "\n"\+
HeaderKey2 + ":" + HeaderValue2 + "\n"\+
...
HeaderKeyN + ":" + HeaderValueN + "\n"
```
- If the value of a header is empty, it will participate in the signature with the `HeaderKey+":"+"\n"` only, and the key and english colon should be retained.
- The set of keys for all headers participating in the signature is separated by a comma and placed in the `X-Ca-Signature-Headers header`.
- The following headers are not included in the header signature calculation: X-Ca-Signature, X-Ca-Signature-Headers, Accept, Content-MD5, Content-Type, Date.
- PathAndParameters: This field contains all parameters in the path, query, and form. The specific format is as follows:
```text
Path + "?" + Key1 + "=" + Value1 + "&" + Key2 + "=" + Value2 + ... "&" + KeyN + "=" + ValueN
```
Notes:
1. The keys of the query and form parameter pairs are sorted alphabetically, and the same format as above is used for concatenation.
2. If there are no query and form parameters, use the path directly without adding `?` .
3. If the value of a parameter is empty, only the key will be included in the signature. The equal sign should not be included in the signature.
4. If there are array parameters in the query or form (parameters with the same key but different values), only the first value should be included in the signature calculation.
### Example of Extracting Signing String
The initial HTTP request :
```text
POST /http2test/test?param1=test HTTP/1.1
host:api.aliyun.com
accept:application/json; charset=utf-8
ca_version:1
content-type:application/x-www-form-urlencoded; charset=utf-8
x-ca-timestamp:1525872629832
date:Wed, 09 May 2018 13:30:29 GMT+00:00
user-agent:ALIYUN-ANDROID-DEMO
x-ca-nonce:c9f15cbf-f4ac-4a6c-b54d-f51abf4b5b44
content-length:33
username=xiaoming&password=123456789
```
The correct generated signature string is :
```text
POST
application/json; charset=utf-8
application/x-www-form-urlencoded; charset=utf-8
Wed, 09 May 2018 13:30:29 GMT+00:00
x-ca-key:203753385
x-ca-nonce:c9f15cbf-f4ac-4a6c-b54d-f51abf4b5b44
x-ca-signature-method:HmacSHA256
x-ca-timestamp:1525872629832
/http2test/test?param1=test&password=123456789&username=xiaoming
```
### Signature Calculation Process
After extracting the key data from the HTTP request and assembling it into a signature string, the client needs to encrypt and encode the signature string to form the final signature.
The specific encryption format is as follows, where `stringToSign` is the extracted signature string, `secret` is the one filled in the plugin configuration, and `sign` is the final generated signature:
```java
Mac hmacSha256 = Mac.getInstance("HmacSHA256");
byte[] secretBytes = secret.getBytes("UTF-8");
hmacSha256.init(new SecretKeySpec(secretBytes, 0, secretBytes.length, "HmacSHA256"));
byte[] result = hmacSha256.doFinal(stringToSign.getBytes("UTF-8"));
String sign = Base64.encodeBase64String(result);
```
In summary, the `stringToSign` is decoded using UTF-8 to obtain a Byte array. Then, an encryption algorithm is used to encrypt the Byte array, and finally, the Base64 algorithm is used to encode the encrypted data, resulting in the final signature.
### The Process of Adding a Signature
The client needs to include the following four headers in the HTTP request to be transmitted to the API gateway for signature verification:
- x-ca-key: The value is the APP Key and is required.
- x-ca-signature-method: The signature algorithm, the value can be HmacSHA256 or HmacSHA1, optional. The default value is HmacSHA256.
- x-ca-signature-headers: The collection of keys for all signature headers, separated by commas. Optional.
- x-ca-signature: The signature and it is required.
Here is an example of a complete HTTP request with a signature :
```text
POST /http2test/test?param1=test HTTP/1.1
host:api.aliyun.com
accept:application/json; charset=utf-8
ca_version:1
content-type:application/x-www-form-urlencoded; charset=utf-8
x-ca-timestamp:1525872629832
date:Wed, 09 May 2018 13:30:29 GMT+00:00
user-agent:ALIYUN-ANDROID-DEMO
x-ca-nonce:c9f15cbf-f4ac-4a6c-b54d-f51abf4b5b44
x-ca-key:203753385
x-ca-signature-method:HmacSHA256
x-ca-signature-headers:x-ca-timestamp,x-ca-key,x-ca-nonce,x-ca-signature-method
x-ca-signature:xfX+bZxY2yl7EB/qdoDy9v/uscw3Nnj1pgoU+Bm6xdM=
content-length:33
username=xiaoming&password=123456789
```
## Server-side Signature Verification Method
### Overview of the Process
The server-side signature verification of the client's request involves four steps :
1. Extract crucial data from the received request to obtain a string for signing.
2. Retrieve the `key` from the received request and use it to query its corresponding `secret`.
3. Encrypt the string for signing using the encryption algorithm and `secret`.
4. Retrieve the client's signature from the received request, and compare the consistency of the server-side signature with the client's signature.
As shown below :
![](https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/1745707061/p188116.png)
## Troubleshooting Signature Errors
When the gateway signature verification fails, the server-side signing string (StringToSign) will be returned to the client in the HTTP Response Header. The key is X-Ca-Error-Message. Users only need to compare the locally calculated signing string with the server-side signing string returned to locate the problem;
If the StringToSign on the server side is consistent with that on the client side, please check whether the APP Secret used for signature calculation is correct
Because line breaks cannot be represented in HTTP headers, all line breaks in the StringToSign are replaced with #, as shown below:
```text
X-Ca-Error-Message: Server StringToSign:`GET#application/json##application/json##X-Ca-Key:200000#X-Ca-Timestamp:1589458000000#/app/v1/config/keys?keys=TEST`
```
# Related Error Codes
| HTTP Status Code | Error Message | Reason |
| ----------- | ---------------------- | -------------------------------------------------------------------------------- |
| 401 | Invalid Key | The x-ca-key request header is not provided or is invalid. |
| 401 | Empty Signature | The x-ca-signature request header does not contain a signature. |
| 400 | Invalid Signature | The x-ca-signature request header contains a signature that does not match the server-calculated signature. |
| 400 | Invalid Content-MD5 | The content-md5 request header is incorrect. |
| 400 | Invalid Date | The time offset calculated based on the date request header exceeds the configured date_offset. |
| 413 | Request Body Too Large | The request body exceeds the size limit of 32 MB. |
| 413 | Payload Too Large | The request body exceeds the DownstreamConnectionBufferLimits global configuration. |
| 403 | Unauthorized Consumer | The requesting party does not have access permission. |

View File

@@ -244,12 +244,14 @@ public class GenerateJwtDemo {
- 只有当`from_headers`,`from_params`,`from_cookies`均未配置时,才会使用默认值
`from_headers` 中每一项的配置字段说明如下:
| 名称 | 数据类型 | 填写要求| 默认值 | 描述 |
| ---------------- | --------------- | ------- | ------ | --------------------------------------------------------- |
| `name` | string | 必填 | - | 抽取JWT的请求header |
| `value_prefix` | string | 必填 | - | 对请求header的value去除此前缀剩余部分作为JWT |
`claims_to_headers` 中每一项的配置字段说明如下:
| 名称 | 数据类型 | 填写要求| 默认值 | 描述 |
| ---------------- | --------------- | ------- | ------ | --------------------------------------------------------- |
| `claim` | string | 必填 | - | JWT payload中的指定字段要求必须是字符串或无符号整数类型 |

View File

@@ -390,10 +390,10 @@ consumers:
```
# Common Error Codes
|
HTTP Status Code | Error Message | Reason Description|
| ----------- | ---------------------- | -------------------------------------------------------------------------------- |
| 401 | JWT missing | The JWT is not provided in the request header. |
| 401 | JWT expired | The JWT has expired. |
| 401 | JWT verification fails | The JWT payload verification failed, such as the iss mismatch. |
| 403 | Access denied | Access to the current route is denied. |
| HTTP Status Code | Error Message | Reason Description|
|------------------| ---------------------- | -------------------------------------------------------------------------------- |
| 401 | JWT missing | The JWT is not provided in the request header. |
| 401 | JWT expired | The JWT has expired. |
| 401 | JWT verification fails | The JWT payload verification failed, such as the iss mismatch. |
| 403 | Access denied | Access to the current route is denied. |

View File

@@ -14,6 +14,7 @@
| limit_keys | array of object | 必填 | - | 配置匹配键值后的限流次数 |
`limit_keys`中每一项的配置字段说明
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
| -------- | -------- | -------- | -------- | -------- |
| key | string | 必填 | - | 匹配的键值 |

View File

@@ -14,6 +14,7 @@
| limit_keys | array of object | Required | - | Rate-limiting thresholds when matching specific key-values |
Field descriptions of `limit_keys` items:
| Name | Type | Requirement | Default Value | Description |
| -------- | -------- | -------- | -------- | -------- |
| key | string | Required | - | Value to match of the specific key |

View File

@@ -1,6 +1,10 @@
ARG BUILDER=higress-registry.cn-hangzhou.cr.aliyuncs.com/plugins/wasm-go-builder:go1.19-tinygo0.27.0
ARG BUILDER=higress-registry.cn-hangzhou.cr.aliyuncs.com/plugins/wasm-go-builder:go1.19-tinygo0.25.0-oras1.0.0
FROM $BUILDER as builder
ARG GOPROXY
ENV GOPROXY=${GOPROXY}
ARG PLUGIN_NAME=hello-world
WORKDIR /workspace

View File

@@ -1,84 +1,115 @@
# The Dockerfile for wasm-go builder only support amd64 and arm64 yet.
# If you want to build on another architecture, the following information may be helpful.
#
# - arch: amd64 \
# - arch: amd64
# base image: docker.io/ubuntu
# go_url: https://golang.google.cn/dl/go1.20.1.linux-amd64.tar.gz"
# tinygo_url: https://github.com/tinygo-org/tinygo/releases/download/v0.27.0/tinygo_0.27.0_amd64.deb
# tinygo_url: https://github.com/alibaba/higress/releases/download/v1.0.0-rc/higress-tinygo0.25.0.linux-amd64.tar.gz
# oras_url: https://github.com/oras-project/oras/releases/download/v1.0.0/oras_1.0.0_linux_amd64.tar.gz
#
# - arch: arm64
# base image: docker.io/ubuntu
# go_url: https://golang.google.cn/dl/go1.20.1.linux-arm64.tar.gz
# tinygo_url: https://github.com/tinygo-org/tinygo/releases/download/v0.27.0/tinygo_0.27.0_arm64.deb
# tinygo_url: https://github.com/alibaba/higress/releases/download/v1.0.0-rc/higress-tinygo0.25.0.linux-arm64.tar.gz
# oras_url: https://github.com/oras-project/oras/releases/download/v1.0.0/oras_1.0.0_linux_arm64.tar.gz
#
# - arch: armel
# base image: build yourself
# go_url: install from source code
# tinygo_url: build yourself
# oras_url: build your self
#
# - arch: i386
# base image: build yourself
# go_url: https://dl.google.com/go/go1.20.1.linux-386.tar.gz
# tinygo_url: build yourself
# oras_url: build your self
#
# - arch: mips64el
# base image: build your self
# go_url: https://dl.google.com/go/go1.20.1.linux-386.tar.gz
# tinygo_url: build your self
# oras_url: build your self
#
# - arch: ppc64el
# base image: build your self
# go_url: https://dl.google.com/go/go1.20.1.linux-ppc64le.tar.gz
# tinygo_url: build your self
# oras_url: build your self
#
# - arch: s390x
# base image: docker.io/ubuntu
# go_url: https://dl.google.com/go/go1.20.1.linux-s390x.tar.gz
# tinygo_url: build your self
# oras_url: build your self
#
# - arch: armhf
# base image: build your self
# go_url: https://golang.google.cn/dl/go1.20.1.linux-armv6l.tar.gz
# tinygo_url: https://github.com/tinygo-org/tinygo/releases/download/v0.27.0/tinygo_0.27.0_armhf.deb
# tinygo_url: https://github.com/tinygo-org/tinygo/releases/download/v0.25.0/tinygo_0.25.0_armhf.deb
# oras_url: build your self
ARG BASE_IMAGE=docker.io/ubuntu
FROM $BASE_IMAGE
ARG GO_VERSION
ARG TINYGO_VERSION
ARG ORAS_VERSION
ARG HIGRESS_VERSION
ARG USE_HIGRESS_TINYGO
LABEL go_version=$GO_VERSION tinygo_version=$TINYGO_VERSION
LABEL go_version=$GO_VERSION tinygo_version=$TINYGO_VERSION oras_version=$ORAS_VERSION
RUN apt-get update \
&& apt-get install -y wget build-essential \
&& apt-get install -y wget \
&& rm -rf /var/lib/apt/lists/*
RUN arch="$(dpkg --print-architecture)"; arch="${arch##*-}"; \
go_url=; \
go_url=; \
tinygo_url=; \
go_version=${GO_VERSION:-1.19}; \
tinygo_version=${TINYGO_VERSION:-0.27.0}; \
tinygo_version=${TINYGO_VERSION:-0.25.0}; \
oras_version=${ORAS_VERSION:-1.0.0}; \
higress_version=${HIGRESS_VERSION:-1.0.0-rc}; \
use_higress_tinygo=${USE_HIGRESS_TINYGO:-false}; \
echo "arch: '$arch'"; \
echo "go go_version: '$go_version'"; \
echo "tinygo_version: '$tinygo_version'"; \
case "$arch" in \
'amd64') \
echo "oras_version: '$oras_version'"; \
echo "higress_version: '$higress_version'"; \
echo "use_higress_tinygo: '$use_higress_tinygo'"; \
case "$arch" in \
'amd64') \
go_url="https://golang.google.cn/dl/go$go_version.linux-amd64.tar.gz"; \
tinygo_url="https://github.com/tinygo-org/tinygo/releases/download/v$tinygo_version/tinygo_${tinygo_version}_amd64.deb"; \
;; \
'arm64') \
if [ "$use_higress_tinygo" = "true" ]; \
then \
tinygo_url="https://github.com/alibaba/higress/releases/download/v$higress_version/higress-tinygo${tinygo_version}.linux-amd64.tar.gz"; \
else \
tinygo_url="https://github.com/tinygo-org/tinygo/releases/download/v$tinygo_version/tinygo${tinygo_version}.linux-amd64.tar.gz"; \
fi; \
oras_url="https://github.com/oras-project/oras/releases/download/v$oras_version/oras_${oras_version}_linux_amd64.tar.gz"; \
;; \
'arm64') \
go_url="https://golang.google.cn/dl/go$go_version.linux-arm64.tar.gz"; \
tinygo_url="https://github.com/tinygo-org/tinygo/releases/download/v$tinygo_version/tinygo_${tinygo_version}_arm64.deb"; \
;; \
*) echo >&2 "error: unsupported architecture '$arch' "; exit 1 ;; \
esac; \
if [ "$use_higress_tinygo" = "true" ]; \
then \
tinygo_url="https://github.com/alibaba/higress/releases/download/v$higress_version/higress-tinygo${tinygo_version}.linux-arm64.tar.gz"; \
else \
tinygo_url="https://github.com/tinygo-org/tinygo/releases/download/v$tinygo_version/tinygo${tinygo_version}.linux-arm64.tar.gz"; \
fi; \
oras_url="https://github.com/oras-project/oras/releases/download/v$oras_version/oras_${oras_version}_linux_arm64.tar.gz"; \
;; \
*) echo >&2 "error: unsupported architecture '$arch' "; exit 1 ;; \
esac; \
echo "go_url: '$go_url'"; \
echo "tinygo_url: '$tinygo_url'"; \
wget -O go.tgz "$go_url" --progress=dot:giga; \
wget -O tinygo.deb "$tinygo_url" --progress=dot:giga; \
echo "Download complete"; \
rm -rf /usr/local/go && tar -C /usr/local -xzf go.tgz && rm -rf go.tgz; \
dpkg -i tinygo.deb && rm -rf tinygo.deb
echo "tinygo_url: '$tinygo_url'"; \
wget -O tinygo.tgz "$tinygo_url" --progress=dot:giga; \
rm -rf /usr/local/tinygo && tar -C /usr/local -xzf tinygo.tgz && rm -rf tinygo.tgz; \
echo "oras_url: '$oras_url'"; \
wget -O oras.tgz "$oras_url" --progress=dot:giga; \
tar -C /usr/local/bin -xzf oras.tgz && rm -rf oras.tgz; \
echo "done";
ENV PATH=$PATH:/usr/local/go/bin:/usr/local/bin
ENV PATH=$PATH:/usr/local/go/bin:/usr/local/tinygo/bin:/usr/local/bin

View File

@@ -1,41 +1,59 @@
PLUGIN_NAME ?= hello-world
REGISTRY ?= higress-registry.cn-hangzhou.cr.aliyuncs.com/plugins/
BUILDER_REGISTRY ?= higress-registry.cn-hangzhou.cr.aliyuncs.com/plugins/
REGISTRY ?=
GO_VERSION ?= 1.19
TINYGO_VERSION ?= 0.27.0
BUILDER ?= ${REGISTRY}wasm-go-builder:go${GO_VERSION}-tinygo${TINYGO_VERSION}
TINYGO_VERSION ?= 0.25.0
ORAS_VERSION ?= 1.0.0
HIGRESS_VERSION ?= 1.0.0-rc
USE_HIGRESS_TINYGO ?= true
BUILDER ?= ${BUILDER_REGISTRY}wasm-go-builder:go${GO_VERSION}-tinygo${TINYGO_VERSION}-oras${ORAS_VERSION}
BUILD_TIME := $(shell date "+%Y%m%d-%H%M%S")
COMMIT_ID := $(shell git rev-parse --short HEAD 2>/dev/null)
IMG ?= ${REGISTRY}${PLUGIN_NAME}:${BUILD_TIME}-${COMMIT_ID}
IMAGE_TAG = $(if $(strip $(PLUGIN_VERSION)),${PLUGIN_VERSION},${BUILD_TIME}-${COMMIT_ID})
IMG ?= ${REGISTRY}${PLUGIN_NAME}:${IMAGE_TAG}
GOPROXY := $(shell go env GOPROXY)
.DEFAULT:
build:
DOCKER_BUILDKIT=1 docker build --build-arg PLUGIN_NAME=${PLUGIN_NAME} \
--build-arg BUILDER=${BUILDER} \
--build-arg GOPROXY=$(GOPROXY) \
-t ${IMG} \
--output extensions/${PLUGIN_NAME} \
.
@echo ""
@echo "image: ${IMG}"
@echo "output wasm file: extensions/${PLUGIN_NAME}/plugin.wasm"
build-push: build
build-image:
DOCKER_BUILDKIT=1 docker build --build-arg PLUGIN_NAME=${PLUGIN_NAME} \
--build-arg BUILDER=${BUILDER} \
--build-arg GOPROXY=$(GOPROXY) \
-t ${IMG} \
--load \
.
@echo ""
@echo "image: ${IMG}"
build-push: build-image
docker push ${IMG}
# builder:
# To build a wasm-go-builder image.
# e.g.
# REGISTRY=<your_docker_registry> make builder
# If you want to use Go/TinyGo with another version, please modify GO_VERSION/TINYGO_VERSION.
# If you want to use Go/TinyGo/Oras with another version, please modify GO_VERSION/TINYGO_VERSION/ORAS_VERSION.
# After your wasm-go-builder image is built, you can use it to build plugin image.
# e.g.
# PLUGIN_NAME=request-block BUILDER=<your-wasm-go-builder> make
builder:
BUILDER=$(REGISTRY)wasm-go-builder:go$(GO_VERSION)-tinygo$(TINYGO_VERSION)
docker buildx build --no-cache \
--platform linux/amd64,linux/arm64 \
--build-arg BASE_IMAGE=docker.io/ubuntu \
--build-arg GO_VERSION=$(GO_VERSION) \
--build-arg TINYGO_VERSION=$(TINYGO_VERSION) \
--build-arg ORAS_VERSION=$(ORAS_VERSION) \
--build-arg HIGRESS_VERSION=$(HIGRESS_VERSION) \
--build-arg USE_HIGRESS_TINYGO=$(USE_HIGRESS_TINYGO) \
-f DockerfileBuilder \
-t ${BUILDER} \
--push \

View File

@@ -143,3 +143,55 @@ spec:
```
所有规则会按上面配置的顺序一次执行匹配,当有一个规则匹配时,就停止匹配,并选择匹配的配置执行插件逻辑。
## E2E测试
当你完成一个GO语言的插件功能时, 可以同时创建关联的e2e test cases, 并在本地对插件功能完成测试验证。
### step1. 编写 test cases
在目录./test/ingress/conformance下面, 分别添加xxx.yaml文件和xxx.go文件, 比如测试插件request-block
./test/ingress/conformance/request-block.yaml
```
apiVersion: networking.k8s.io/v1
kind: Ingress
...
...
spec:
defaultConfig:
block_urls:
- "swagger.html"
url: file:///opt/plugins/wasm-go/extensions/request-block/plugin.wasm
```
`其中url中extensions后面的'request-block'为插件所在文件夹名称`
./test/ingress/conformance/request-block.go
### step2. 添加 test cases
将上述所写test cases添加到e2e测试列表中,
./test/ingress/conformance/request-block.yaml
```
...
cSuite.Setup(t)
var higressTests []suite.ConformanceTest
if *isWasmPluginTest {
higressTests = []suite.ConformanceTest{
tests.WasmPluginsRequestBlock,
//这里新增你新写的case方法名称
}
} else {
higressTests = []suite.ConformanceTest{
tests.HTTPRouteSimpleSameNamespace,
tests.HTTPRouteHostNameSameNamespace,
...
```
### step3. 编译插件并执行 test cases
考虑到本地构建wasm比较耗时, 我们支持只构建需要测试的插件(同时你也可以临时修改上面第二小步的测试cases列表, 只执行你新写的case)。
```bash
PLUGIN_NAME=request-block make ingress-wasmplugin-test
```

View File

@@ -29,11 +29,11 @@ You can also use `make build-push` to build and push the image at the same time.
### Environmental parameters
| Name | Optional/Required | Default | 含义 |
|---------------|-------------------|--------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------|
| `PLUGIN_NAME` | Optional | hello-world | The name of the plugin to build. |
| `REGISTRY` | Optional | empty | The regitstry address of the generated image, e.g. `example.registry.io/my-name/`. Note that the REGISTRY value should end with /. |
| `IMG` | Optional | If it is empty, it is generated based on the repository address, plugin name, build time, and git commit id. | The generated image tag will override the `REGISTRY` parameter if it is not empty. |
| Name | Optional/Required | Default | meaning |
|---------------|---------------|--------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------|
| `PLUGIN_NAME` | Optional | hello-world | The name of the plugin to build. |
| `REGISTRY` | Optional | empty | The registry address of the generated image, e.g. `example.registry.io/my-name/`. Note that the REGISTRY value should end with /. |
| `IMG` | Optional | If it is empty, it is generated based on the repository address, plugin name, build time, and git commit id. | The generated image tag will override the `REGISTRY` parameter if it is not empty. |
## Build on local yourself
@@ -138,3 +138,55 @@ spec:
The rules will be matched in the order of configuration. If one match is found, it will stop, and the matching configuration will take effect.
## E2E test
When you complete a GO plug-in function, you can create associated e2e test cases at the same time, and complete the test verification of the plug-in function locally.
### step1. write test cases
In the directory of `./ test/ingress/conformance`, add the xxx.yaml file and xxx.go file. Such as test for `request-block` wasm-plugin,
./test/ingress/conformance/request-block.yaml
```
apiVersion: networking.k8s.io/v1
kind: Ingress
...
...
spec:
defaultConfig:
block_urls:
- "swagger.html"
url: file:///opt/plugins/wasm-go/extensions/request-block/plugin.wasm
```
`Above of the url, the name of after extensions indicates the name of the folder where the plug-in resides.`
./test/ingress/conformance/request-block.go
### step2. add test cases
Add the test cases written above to the e2e test list,
./test/ingress/conformance/request-block.yaml
```
...
cSuite.Setup(t)
var higressTests []suite.ConformanceTest
if *isWasmPluginTest {
higressTests = []suite.ConformanceTest{
tests.WasmPluginsRequestBlock,
//Add your newly written case method name here
}
} else {
higressTests = []suite.ConformanceTest{
tests.HTTPRouteSimpleSameNamespace,
tests.HTTPRouteHostNameSameNamespace,
...
```
### step3. compile and run test cases
Considering that building wasm locally is time-consuming, we support building only the plug-ins that need to be tested (at the same time, you can also temporarily modify the list of test cases in the second small step above, and only execute your newly written cases).
```bash
PLUGIN_NAME=request-block make ingress-wasmplugin-test
```

View File

@@ -0,0 +1,21 @@
# 功能说明
`chatgpt-proxy`插件实现了代理请求AI大语言模型服务的功能。
# 配置字段
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
| -------- | -------- | -------- | -------- | -------- |
| model | string | 选填 | text-davinci-003 | 配置使用的模型模型名称 |
| apiKey | string | 必填 | - | 配置使用的OpenAI API密钥 |
| promptParam | string | 选填 | prompt | 配置prompt的来源字段名称URL参数 |
| chatgptUri | string | 选填 | api.openai.com/v1/completions | 配置调用AI模型服务的URL路径默认值为OPENAI的API调用路径 |
# 配置示例
## 进行OpenAI curie模型的调用
```yaml
apiKey: "xxxxxxxxxxxxxx",
promptParam: "text",
model: "curie"
```

View File

@@ -0,0 +1,15 @@
module chatgpt-proxy
go 1.19
require (
github.com/alibaba/higress/plugins/wasm-go v0.0.0-20230629030002-81e467b6242d
github.com/tetratelabs/proxy-wasm-go-sdk v0.22.0
github.com/tidwall/gjson v1.14.4
)
require (
github.com/google/uuid v1.3.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
)

View File

@@ -0,0 +1,22 @@
github.com/alibaba/higress/plugins/wasm-go v0.0.0-20230619024848-7d2a05ef1c35 h1:9xBMl8mJ5CGE1ebqtx9s11zrKJHs0559Yf/QWFh0WIQ=
github.com/alibaba/higress/plugins/wasm-go v0.0.0-20230619024848-7d2a05ef1c35/go.mod h1:AzSnkuon5c26nIePTiJQIAFsKdhkNdncLcTuahpGtQs=
github.com/alibaba/higress/plugins/wasm-go v0.0.0-20230629030002-81e467b6242d h1:PyBnIfssGRMKvBf6wKH11Z1t+X1Z7qegD1pAO60sFrk=
github.com/alibaba/higress/plugins/wasm-go v0.0.0-20230629030002-81e467b6242d/go.mod h1:AzSnkuon5c26nIePTiJQIAFsKdhkNdncLcTuahpGtQs=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/tetratelabs/proxy-wasm-go-sdk v0.19.1-0.20220822060051-f9d179a57f8c h1:OCUFXVGixHLfNjg6/QYEhv+jHJ5mRGhpEUVFv9eWPJE=
github.com/tetratelabs/proxy-wasm-go-sdk v0.19.1-0.20220822060051-f9d179a57f8c/go.mod h1:5t/pWFNJ9eMyu/K/Z+OeGhDJ9sN9eCo8fc2pyM/Qjg4=
github.com/tetratelabs/proxy-wasm-go-sdk v0.22.0 h1:kS7BvMKN+FiptV4pfwiNX8e3q14evxAWkhYbxt8EI1M=
github.com/tetratelabs/proxy-wasm-go-sdk v0.22.0/go.mod h1:qkW5MBz2jch2u8bS59wws65WC+Gtx3x0aPUX5JL7CXI=
github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw=
github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View File

@@ -0,0 +1,128 @@
package main
import (
"errors"
"fmt"
"net/http"
"net/url"
"strings"
"github.com/alibaba/higress/plugins/wasm-go/pkg/wrapper"
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm"
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types"
"github.com/tidwall/gjson"
)
func main() {
wrapper.SetCtx(
"chatgpt-proxy",
wrapper.ParseConfigBy(parseConfig),
wrapper.ProcessRequestHeadersBy(onHttpRequestHeaders),
)
}
type MyConfig struct {
Model string
ApiKey string
PromptParam string
ChatgptPath string
HumainId string
AIId string
client wrapper.HttpClient
}
func parseConfig(json gjson.Result, config *MyConfig, log wrapper.Log) error {
chatgptUri := json.Get("chatgptUri").String()
var chatgptHost string
if chatgptUri == "" {
config.ChatgptPath = "/v1/completions"
chatgptHost = "api.openai.com"
} else {
cp, err := url.Parse(chatgptUri)
if err != nil {
return err
}
config.ChatgptPath = cp.Path
chatgptHost = cp.Host
}
if config.ChatgptPath == "" {
return errors.New("not found path in chatgptUri")
}
if chatgptHost == "" {
return errors.New("not found host in chatgptUri")
}
config.client = wrapper.NewClusterClient(wrapper.RouteCluster{
Host: chatgptHost,
})
config.Model = json.Get("model").String()
if config.Model == "" {
config.Model = "text-davinci-003"
}
config.ApiKey = json.Get("apiKey").String()
if config.ApiKey == "" {
return errors.New("no apiKey found in config")
}
config.PromptParam = json.Get("promptParam").String()
if config.PromptParam == "" {
config.PromptParam = "prompt"
}
config.HumainId = json.Get("HumainId").String()
if config.HumainId == "" {
config.HumainId = "Humain:"
}
config.AIId = json.Get("AIId").String()
if config.AIId == "" {
config.AIId = "AI:"
}
return nil
}
const bodyTemplate string = `
{
"model":"%s",
"prompt":"%s",
"temperature":0.9,
"max_tokens": 150,
"top_p": 1,
"frequency_penalty": 0.0,
"presence_penalty": 0.6,
"stop": [" %s", " %s"]
}
`
func onHttpRequestHeaders(ctx wrapper.HttpContext, config MyConfig, log wrapper.Log) types.Action {
pairs := strings.SplitN(ctx.Path(), "?", 2)
if len(pairs) < 2 {
proxywasm.SendHttpResponse(400, nil, []byte("1-need prompt param"), -1)
return types.ActionContinue
}
querys, err := url.ParseQuery(pairs[1])
if err != nil {
proxywasm.SendHttpResponse(400, nil, []byte("2-need prompt param"), -1)
return types.ActionContinue
}
var prompt []string
var ok bool
if prompt, ok = querys[config.PromptParam]; !ok || len(prompt) == 0 {
proxywasm.SendHttpResponse(400, nil, []byte("3-need prompt param"), -1)
return types.ActionContinue
}
body := fmt.Sprintf(bodyTemplate, config.Model, prompt[0], config.HumainId, config.AIId)
err = config.client.Post(config.ChatgptPath, [][2]string{
{"Content-Type", "application/json"},
{"Authorization", "Bearer " + config.ApiKey},
}, []byte(body),
func(statusCode int, responseHeaders http.Header, responseBody []byte) {
var headers [][2]string
for key, value := range responseHeaders {
headers = append(headers, [2]string{key, value[0]})
}
proxywasm.SendHttpResponse(uint32(statusCode), headers, responseBody, -1)
}, 10000)
if err != nil {
proxywasm.SendHttpResponse(500, nil, []byte("Internel Error: "+err.Error()), -1)
return types.ActionContinue
}
return types.ActionPause
}

View File

Binary file not shown.

View File

@@ -0,0 +1,230 @@
# 功能说明
`cors` 插件可以为服务端启用 CORSCross-Origin Resource Sharing跨域资源共享的返回 http 响应头。
# 配置字段
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
|-----------------------|-----------------|-------|---------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| allow_origins | array of string | 选填 | * | 允许跨域访问的 Origin格式为 scheme://host:port示例如 http://example.com:8081。当 allow_credentials 为 false 时,可以使用 * 来表示允许所有 Origin 通过 |
| allow_origin_patterns | array of string | 选填 | - | 允许跨域访问的 Origin 模式匹配, 用 * 匹配域名或者端口, <br/>比如 http://*.example.com -- 匹配域名, http://*.example.com:[8080,9090] -- 匹配域名和指定端口, http://*.example.com:[*] -- 匹配域名和所有端口。单独 * 表示匹配所有域名和端口 |
| allow_methods | array of string | 选填 | GET, PUT, POST, DELETE, PATCH, OPTIONS | 允许跨域访问的 Method比如GETPOST 等。可以使用 * 来表示允许所有 Method。 |
| allow_headers | array of string | 选填 | DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With<br/>If-Modified-Since,Cache-Control,Content-Type,Authorization | 允许跨域访问时请求方携带哪些非 CORS 规范以外的 Header。可以使用 * 来表示允许任意 Header。 |
| expose_headers | array of string | 选填 | - | 允许跨域访问时响应方携带哪些非 CORS 规范以外的 Header。可以使用 * 来表示允许任意 Header。 |
| allow_credentials | bool | 选填 | false | 是否允许跨域访问的请求方携带凭据(如 Cookie 等)。根据 CORS 规范,如果设置该选项为 true在 allow_origins 不能使用 * 替换成使用 allow_origin_patterns * |
| max_age | number | 选填 | 86400秒 | 浏览器缓存 CORS 结果的最大时间,单位为秒。<br/>在这个时间范围内,浏览器会复用上一次的检查结果 |
> 注意
> * allow_credentials 是一个很敏感的选项请谨慎开启。开启之后allow_credentials 和 allow_origins 为 * 不能同时使用,同时设置时, allow_origins 值为 "*" 生效。
> * allow_origins 和 allow_origin_patterns 可以同时设置, 先检查 allow_origins 是否匹配,然后再检查 allow_origin_patterns 是否匹配
> * 非法 CORS 请求, HTTP 状态码返回是 403 返回体内容为 "Invalid CORS request"
# 配置示例
## 允许所有跨域访问, 不允许请求方携带凭据
```yaml
allow_origins:
- '*'
allow_methods:
- '*'
allow_headers:
- '*'
expose_headers:
- '*'
allow_credentials: false
max_age: 7200
```
## 允许所有跨域访问,同时允许请求方携带凭据
```yaml
allow_origin_patterns:
- '*'
allow_methods:
- '*'
allow_headers:
- '*'
expose_headers:
- '*'
allow_credentials: true
max_age: 7200
```
## 允许特定子域,特定方法,特定请求头跨域访问,同时允许请求方携带凭据
```yaml
allow_origin_patterns:
- http://*.example.com
- http://*.example.org:[8080,9090]
allow_methods:
- GET
- PUT
- POST
- DELETE
allow_headers:
- Token
- Content-Type
- Authorization
expose_headers:
- '*'
allow_credentials: true
max_age: 7200
```
# 测试
## 测试配置
```yaml
apiVersion: networking.higress.io/v1
kind: McpBridge
metadata:
name: mcp-cors-httpbin
namespace: higress-system
spec:
registries:
- domain: httpbin.org
name: httpbin
port: 80
type: dns
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
higress.io/destination: httpbin.dns
higress.io/upstream-vhost: "httpbin.org"
higress.io/backend-protocol: HTTP
name: ingress-cors-httpbin
namespace: higress-system
spec:
ingressClassName: higress
rules:
- host: httpbin.example.com
http:
paths:
- backend:
resource:
apiGroup: networking.higress.io
kind: McpBridge
name: mcp-cors-httpbin
path: /
pathType: Prefix
---
apiVersion: extensions.higress.io/v1alpha1
kind: WasmPlugin
metadata:
name: wasm-cors-httpbin
namespace: higress-system
spec:
defaultConfigDisable: true
matchRules:
- config:
allow_origins:
- http://httpbin.example.net
allow_origin_patterns:
- http://*.example.com:[*]
- http://*.example.org:[9090,8080]
allow_methods:
- GET
- POST
- PATCH
allow_headers:
- Content-Type
- Token
- Authorization
expose_headers:
- X-Custom-Header
- X-Env-UTM
allow_credentials: true
max_age: 3600
configDisable: false
ingress:
- ingress-cors-httpbin
url: oci://higress-registry.cn-hangzhou.cr.aliyuncs.com/plugins/cors:1.0.0
imagePullPolicy: Always
```
## 请求测试
### 简单请求
```shell
curl -v -H "Origin: http://httpbin2.example.org:9090" -H "Host: httpbin.example.com" http://127.0.0.1/anything/get\?foo\=1
< HTTP/1.1 200 OK
> x-cors-version: 1.0.0
> access-control-allow-origin: http://httpbin2.example.org:9090
> access-control-expose-headers: X-Custom-Header,X-Env-UTM
> access-control-allow-credentials: true
```
### 预检请求
```shell
curl -v -X OPTIONS -H "Origin: http://httpbin2.example.org:9090" -H "Host: httpbin.example.com" -H "Access-Control-Request-Method: POST" -H "Access-Control-Request-Headers: Content-Type, Token" http://127.0.0.1/anything/get\?foo\=1
< HTTP/1.1 200 OK
< x-cors-version: 1.0.0
< access-control-allow-origin: http://httpbin2.example.org:9090
< access-control-allow-methods: GET,POST,PATCH
< access-control-allow-headers: Content-Type,Token,Authorization
< access-control-expose-headers: X-Custom-Header,X-Env-UTM
< access-control-allow-credentials: true
< access-control-max-age: 3600
< date: Tue, 23 May 2023 11:41:28 GMT
< server: istio-envoy
< content-length: 0
<
* Connection #0 to host 127.0.0.1 left intact
* Closing connection 0
```
### 非法 CORS Origin 预检请求
```shell
curl -v -X OPTIONS -H "Origin: http://httpbin2.example.org" -H "Host: httpbin.example.com" -H "Access-Control-Request-Method: GET" http://127.0.0.1/anything/get\?foo\=1
HTTP/1.1 403 Forbidden
< content-length: 70
< content-type: text/plain
< x-cors-version: 1.0.0
< date: Tue, 23 May 2023 11:27:01 GMT
< server: istio-envoy
<
* Connection #0 to host 127.0.0.1 left intact
Invalid CORS request
```
### 非法 CORS Method 预检请求
```shell
curl -v -X OPTIONS -H "Origin: http://httpbin2.example.org:9090" -H "Host: httpbin.example.com" -H "Access-Control-Request-Method: DELETE" http://127.0.0.1/anything/get\?foo\=1
< HTTP/1.1 403 Forbidden
< content-length: 49
< content-type: text/plain
< x-cors-version: 1.0.0
< date: Tue, 23 May 2023 11:28:51 GMT
< server: istio-envoy
<
* Connection #0 to host 127.0.0.1 left intact
Invalid CORS request
```
### 非法 CORS Header 预检请求
```shell
curl -v -X OPTIONS -H "Origin: http://httpbin2.example.org:9090" -H "Host: httpbin.example.com" -H "Access-Control-Request-Method: GET" -H "Access-Control-Request-Headers: TokenView" http://127.0.0.1/anything/get\?foo\=1
< HTTP/1.1 403 Forbidden
< content-length: 52
< content-type: text/plain
< x-cors-version: 1.0.0
< date: Tue, 23 May 2023 11:31:03 GMT
< server: istio-envoy
<
* Connection #0 to host 127.0.0.1 left intact
Invalid CORS request
```
# 参考文档
- https://www.ruanyifeng.com/blog/2016/04/cors.html
- https://fetch.spec.whatwg.org/#http-cors-protocol

View File

@@ -0,0 +1 @@
1.0.0

View File

@@ -0,0 +1,457 @@
// 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 config
import (
"errors"
"fmt"
"net/url"
"regexp"
"strings"
)
const (
defaultMatchAll = "*"
defaultAllowMethods = "GET, PUT, POST, DELETE, PATCH, OPTIONS"
defaultAllAllowMethods = "GET, PUT, POST, DELETE, PATCH, OPTIONS, HEAD, TRACE, CONNECT"
defaultAllowHeaders = "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With," +
"If-Modified-Since,Cache-Control,Content-Type,Authorization"
defaultMaxAge = 86400
protocolHttpName = "http"
protocolHttpPort = "80"
protocolHttpsName = "https"
protocolHttpsPort = "443"
HeaderPluginDebug = "X-Cors-Version"
HeaderPluginTrace = "X-Cors-Trace"
HeaderOrigin = "Origin"
HttpMethodOptions = "OPTIONS"
HeaderAccessControlAllowOrigin = "Access-Control-Allow-Origin"
HeaderAccessControlAllowMethods = "Access-Control-Allow-Methods"
HeaderAccessControlAllowHeaders = "Access-Control-Allow-Headers"
HeaderAccessControlAllowCredentials = "Access-Control-Allow-Credentials"
HeaderAccessControlExposeHeaders = "Access-Control-Expose-Headers"
HeaderAccessControlMaxAge = "Access-Control-Max-Age"
HeaderControlRequestMethod = "Access-Control-Request-Method"
HeaderControlRequestHeaders = "Access-Control-Request-Headers"
HttpContextKey = "CORS"
)
var portsRegex = regexp.MustCompile(`(.*):\[(\*|\d+(,\d+)*)]`)
type OriginPattern struct {
declaredPattern string
pattern *regexp.Regexp
patternValue string
}
func newOriginPatternFromString(declaredPattern string) OriginPattern {
declaredPattern = strings.ToLower(strings.TrimSuffix(declaredPattern, "/"))
matches := portsRegex.FindAllStringSubmatch(declaredPattern, -1)
portList := ""
patternValue := declaredPattern
if len(matches) > 0 {
patternValue = matches[0][1]
portList = matches[0][2]
}
patternValue = "\\Q" + patternValue + "\\E"
patternValue = strings.ReplaceAll(patternValue, "*", "\\E.*\\Q")
if len(portList) > 0 {
if portList == defaultMatchAll {
patternValue += "(:\\d+)?"
} else {
patternValue += ":(" + strings.ReplaceAll(portList, ",", "|") + ")"
}
}
return OriginPattern{
declaredPattern: declaredPattern,
patternValue: patternValue,
pattern: regexp.MustCompile(patternValue),
}
}
type CorsConfig struct {
// allowOrigins A list of origins for which cross-origin requests are allowed.
// Be a specific domain, e.g. "https://example.com", or the CORS defined special value "*" for all origins.
// Keep in mind however that the CORS spec does not allow "*" when allowCredentials is set to true, using allowOriginPatterns instead
// By default, it is set to "*" when allowOriginPatterns is not set too.
allowOrigins []string
// allowOriginPatterns A list of origin patterns for which cross-origin requests are allowed
// origins patterns with "*" anywhere in the host name in addition to port
// lists Examples:
// https://*.example.com -- domains ending with example.com
// https://*.example.com:[8080,9090] -- domains ending with example.com on port 8080 or port 9090
// https://*.example.com:[*] -- domains ending with example.com on any port, including the default port
// The special value "*" allows all origins
// By default, it is not set.
allowOriginPatterns []OriginPattern
// allowMethods A list of method for which cross-origin requests are allowed
// The special value "*" allows all methods.
// By default, it is set to "GET, PUT, POST, DELETE, PATCH, OPTIONS".
allowMethods []string
// allowHeaders A list of headers that a pre-flight request can list as allowed
// The special value "*" allows actual requests to send any header
// By default, it is set to "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization"
allowHeaders []string
// exposeHeaders A list of response headers an actual response might have and can be exposed.
// The special value "*" allows all headers to be exposed for non-credentialed requests.
// By default, it is not set
exposeHeaders []string
// allowCredentials Whether user credentials are supported.
// By default, it is not set (i.e. user credentials are not supported).
allowCredentials bool
// maxAge Configure how long, in seconds, the response from a pre-flight request can be cached by clients.
// By default, it is set to 86400 seconds.
maxAge int
}
type HttpCorsContext struct {
IsValid bool
ValidReason string
IsPreFlight bool
IsCorsRequest bool
AllowOrigin string
AllowMethods string
AllowHeaders string
ExposeHeaders string
AllowCredentials bool
MaxAge int
}
func (c *CorsConfig) GetVersion() string {
return "1.0.0"
}
func (c *CorsConfig) FillDefaultValues() {
if len(c.allowOrigins) == 0 && len(c.allowOriginPatterns) == 0 && c.allowCredentials == false {
c.allowOrigins = []string{defaultMatchAll}
}
if len(c.allowHeaders) == 0 {
c.allowHeaders = []string{defaultAllowHeaders}
}
if len(c.allowMethods) == 0 {
c.allowMethods = strings.Split(defaultAllowMethods, "")
}
if c.maxAge == 0 {
c.maxAge = defaultMaxAge
}
}
func (c *CorsConfig) AddAllowOrigin(origin string) error {
origin = strings.TrimSpace(origin)
if len(origin) == 0 {
return nil
}
if origin == defaultMatchAll {
if c.allowCredentials == true {
return errors.New("can't set origin to * when allowCredentials is true, use AllowOriginPatterns instead")
}
c.allowOrigins = []string{defaultMatchAll}
return nil
}
c.allowOrigins = append(c.allowOrigins, strings.TrimSuffix(origin, "/"))
return nil
}
func (c *CorsConfig) AddAllowHeader(header string) {
header = strings.TrimSpace(header)
if len(header) == 0 {
return
}
if header == defaultMatchAll {
c.allowHeaders = []string{defaultMatchAll}
return
}
c.allowHeaders = append(c.allowHeaders, header)
}
func (c *CorsConfig) AddAllowMethod(method string) {
method = strings.TrimSpace(method)
if len(method) == 0 {
return
}
if method == defaultMatchAll {
c.allowMethods = []string{defaultMatchAll}
return
}
c.allowMethods = append(c.allowMethods, strings.ToUpper(method))
}
func (c *CorsConfig) AddExposeHeader(header string) {
header = strings.TrimSpace(header)
if len(header) == 0 {
return
}
if header == defaultMatchAll {
c.exposeHeaders = []string{defaultMatchAll}
return
}
c.exposeHeaders = append(c.exposeHeaders, header)
}
func (c *CorsConfig) AddAllowOriginPattern(pattern string) {
pattern = strings.TrimSpace(pattern)
if len(pattern) == 0 {
return
}
originPattern := newOriginPatternFromString(pattern)
c.allowOriginPatterns = append(c.allowOriginPatterns, originPattern)
}
func (c *CorsConfig) SetAllowCredentials(allowCredentials bool) error {
if allowCredentials && len(c.allowOrigins) > 0 && c.allowOrigins[0] == defaultMatchAll {
return errors.New("can't set allowCredentials to true when allowOrigin is *")
}
c.allowCredentials = allowCredentials
return nil
}
func (c *CorsConfig) SetMaxAge(maxAge int) {
if maxAge <= 0 {
c.maxAge = defaultMaxAge
} else {
c.maxAge = maxAge
}
}
func (c *CorsConfig) Process(scheme string, host string, method string, headers [][2]string) (HttpCorsContext, error) {
scheme = strings.ToLower(strings.TrimSpace(scheme))
host = strings.ToLower(strings.TrimSpace(host))
method = strings.ToLower(strings.TrimSpace(method))
// Init httpCorsContext with default values
httpCorsContext := HttpCorsContext{IsValid: true, IsPreFlight: false, IsCorsRequest: false, AllowCredentials: false, MaxAge: 0}
// Get request origin, controlRequestMethod, controlRequestHeaders from http headers
origin := ""
controlRequestMethod := ""
controlRequestHeaders := ""
for _, header := range headers {
key := header[0]
// Get origin
if strings.ToLower(key) == strings.ToLower(HeaderOrigin) {
origin = strings.TrimSuffix(strings.TrimSpace(header[1]), "/")
}
// Get control request method & headers
if strings.ToLower(key) == strings.ToLower(HeaderControlRequestMethod) {
controlRequestMethod = strings.TrimSpace(header[1])
}
if strings.ToLower(key) == strings.ToLower(HeaderControlRequestHeaders) {
controlRequestHeaders = strings.TrimSpace(header[1])
}
}
// Parse if request is CORS and pre-flight request.
isCorsRequest := c.isCorsRequest(scheme, host, origin)
isPreFlight := c.isPreFlight(origin, method, controlRequestMethod)
httpCorsContext.IsCorsRequest = isCorsRequest
httpCorsContext.IsPreFlight = isPreFlight
// Skip when it is not CORS request
if !isCorsRequest {
httpCorsContext.IsValid = true
return httpCorsContext, nil
}
// Check origin
allowOrigin, originOk := c.checkOrigin(origin)
if !originOk {
// Reject: origin is not allowed
httpCorsContext.IsValid = false
httpCorsContext.ValidReason = fmt.Sprintf("origin:%s is not allowed", origin)
return httpCorsContext, nil
}
// Check method
requestMethod := method
if isPreFlight {
requestMethod = controlRequestMethod
}
allowMethods, methodOk := c.checkMethods(requestMethod)
if !methodOk {
// Reject: method is not allowed
httpCorsContext.IsValid = false
httpCorsContext.ValidReason = fmt.Sprintf("method:%s is not allowed", requestMethod)
return httpCorsContext, nil
}
// Check headers
allowHeaders, headerOK := c.checkHeaders(controlRequestHeaders)
if isPreFlight && !headerOK {
// Reject: headers are not allowed
httpCorsContext.IsValid = false
httpCorsContext.ValidReason = "Reject: headers are not allowed"
return httpCorsContext, nil
}
// Store result in httpCorsContext and return it.
httpCorsContext.AllowOrigin = allowOrigin
if isPreFlight {
httpCorsContext.AllowMethods = allowMethods
}
if isPreFlight && len(allowHeaders) > 0 {
httpCorsContext.AllowHeaders = allowHeaders
}
if isPreFlight && c.maxAge > 0 {
httpCorsContext.MaxAge = c.maxAge
}
if len(c.exposeHeaders) > 0 {
httpCorsContext.ExposeHeaders = strings.Join(c.exposeHeaders, ",")
}
httpCorsContext.AllowCredentials = c.allowCredentials
return httpCorsContext, nil
}
func (c *CorsConfig) checkOrigin(origin string) (string, bool) {
origin = strings.TrimSpace(origin)
if len(origin) == 0 {
return "", false
}
matchOrigin := strings.ToLower(origin)
// Check exact match
for _, allowOrigin := range c.allowOrigins {
if allowOrigin == defaultMatchAll {
return origin, true
}
if strings.ToLower(allowOrigin) == matchOrigin {
return origin, true
}
}
// Check pattern match
for _, allowOriginPattern := range c.allowOriginPatterns {
if allowOriginPattern.declaredPattern == defaultMatchAll || allowOriginPattern.pattern.MatchString(matchOrigin) {
return origin, true
}
}
return "", false
}
func (c *CorsConfig) checkHeaders(requestHeaders string) (string, bool) {
if len(c.allowHeaders) == 0 {
return "", false
}
if len(requestHeaders) == 0 {
return strings.Join(c.allowHeaders, ","), true
}
// Return all request headers when allowHeaders contains *
if c.allowHeaders[0] == defaultMatchAll {
return requestHeaders, true
}
checkHeaders := strings.Split(requestHeaders, ",")
// Each request header should be existed in allowHeaders configuration
for _, h := range checkHeaders {
isExist := false
for _, allowHeader := range c.allowHeaders {
if strings.ToLower(h) == strings.ToLower(allowHeader) {
isExist = true
break
}
}
if !isExist {
return "", false
}
}
return strings.Join(c.allowHeaders, ","), true
}
func (c *CorsConfig) checkMethods(requestMethod string) (string, bool) {
if len(requestMethod) == 0 {
return "", false
}
// Find method existed in allowMethods configuration
for _, method := range c.allowMethods {
if method == defaultMatchAll {
return defaultAllAllowMethods, true
}
if strings.ToLower(method) == strings.ToLower(requestMethod) {
return strings.Join(c.allowMethods, ","), true
}
}
return "", false
}
func (c *CorsConfig) isPreFlight(origin, method, controllerRequestMethod string) bool {
return len(origin) > 0 && strings.ToLower(method) == strings.ToLower(HttpMethodOptions) && len(controllerRequestMethod) > 0
}
func (c *CorsConfig) isCorsRequest(scheme, host, origin string) bool {
if len(origin) == 0 {
return false
}
url, err := url.Parse(strings.TrimSpace(origin))
if err != nil {
return false
}
// Check scheme
if strings.ToLower(scheme) != strings.ToLower(url.Scheme) {
return true
}
// Check host and port
port := ""
originPort := ""
originHost := ""
host, port = c.getHostAndPort(scheme, host)
originHost, originPort = c.getHostAndPort(url.Scheme, url.Host)
if host != originHost || port != originPort {
return true
}
return false
}
func (c *CorsConfig) getHostAndPort(scheme string, host string) (string, string) {
// Get host and port
scheme = strings.ToLower(scheme)
host = strings.ToLower(host)
port := ""
hosts := strings.Split(host, ":")
if len(hosts) > 1 {
host = hosts[0]
port = hosts[1]
}
// Get default port according scheme
if len(port) == 0 && scheme == protocolHttpName {
port = protocolHttpPort
}
if len(port) == 0 && scheme == protocolHttpsName {
port = protocolHttpsPort
}
return host, port
}

View File

@@ -0,0 +1,408 @@
// 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 config
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestCorsConfig_getHostAndPort(t *testing.T) {
tests := []struct {
name string
scheme string
host string
wantHost string
wantPort string
}{
{
name: "http without port",
scheme: "http",
host: "http.example.com",
wantHost: "http.example.com",
wantPort: "80",
},
{
name: "https without port",
scheme: "https",
host: "http.example.com",
wantHost: "http.example.com",
wantPort: "443",
},
{
name: "http with port and case insensitive",
scheme: "hTTp",
host: "hTTp.Example.com:8080",
wantHost: "http.example.com",
wantPort: "8080",
},
{
name: "https with port and case insensitive",
scheme: "hTTps",
host: "hTTp.Example.com:8080",
wantHost: "http.example.com",
wantPort: "8080",
},
{
name: "protocal is not http",
scheme: "wss",
host: "hTTp.Example.com",
wantHost: "http.example.com",
wantPort: "",
},
{
name: "protocal is not http",
scheme: "wss",
host: "hTTp.Example.com:8080",
wantHost: "http.example.com",
wantPort: "8080",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &CorsConfig{}
host, port := c.getHostAndPort(tt.scheme, tt.host)
assert.Equal(t, tt.wantHost, host)
assert.Equal(t, tt.wantPort, port)
})
}
}
func TestCorsConfig_isCorsRequest(t *testing.T) {
tests := []struct {
name string
scheme string
host string
origin string
want bool
}{
{
name: "blank origin",
scheme: "http",
host: "httpbin.example.com",
origin: "",
want: false,
},
{
name: "normal equal case with space and case ",
scheme: "http",
host: "httpbin.example.com",
origin: "http://hTTPbin.Example.com",
want: false,
},
{
name: "cors request with port diff",
scheme: "http",
host: "httpbin.example.com",
origin: " http://httpbin.example.com:8080 ",
want: true,
},
{
name: "cors request with scheme diff",
scheme: "http",
host: "httpbin.example.com",
origin: " https://HTTPpbin.Example.com ",
want: true,
},
{
name: "cors request with host diff",
scheme: "http",
host: "httpbin.example.com",
origin: " http://HTTPpbin.Example.org ",
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &CorsConfig{}
assert.Equalf(t, tt.want, c.isCorsRequest(tt.scheme, tt.host, tt.origin), "isCorsRequest(%v, %v, %v)", tt.scheme, tt.host, tt.origin)
})
}
}
func TestCorsConfig_isPreFlight(t *testing.T) {
tests := []struct {
name string
origin string
method string
controllerRequestMethod string
want bool
}{
{
name: "blank case",
origin: "",
method: "",
controllerRequestMethod: "",
want: false,
},
{
name: "normal case",
origin: "http://httpbin.example.com",
method: "Options",
controllerRequestMethod: "PUT",
want: true,
},
{
name: "bad case with diff method",
origin: "http://httpbin.example.com",
method: "GET",
controllerRequestMethod: "PUT",
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &CorsConfig{}
assert.Equalf(t, tt.want, c.isPreFlight(tt.origin, tt.method, tt.controllerRequestMethod), "isPreFlight(%v, %v, %v)", tt.origin, tt.method, tt.controllerRequestMethod)
})
}
}
func TestCorsConfig_checkMethods(t *testing.T) {
tests := []struct {
name string
allowMethods []string
requestMethod string
wantMethods string
wantOk bool
}{
{
name: "default *",
allowMethods: []string{"*"},
requestMethod: "GET",
wantMethods: defaultAllAllowMethods,
wantOk: true,
},
{
name: "normal allow case",
allowMethods: []string{"GET", "PUT", "HEAD"},
requestMethod: "get",
wantMethods: "GET,PUT,HEAD",
wantOk: true,
},
{
name: "forbidden case",
allowMethods: []string{"GET", "PUT", "HEAD"},
requestMethod: "POST",
wantMethods: "",
wantOk: false,
},
{
name: "blank method",
allowMethods: []string{"GET", "PUT", "HEAD"},
requestMethod: "",
wantMethods: "",
wantOk: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &CorsConfig{
allowMethods: tt.allowMethods,
}
allowMethods, allowOk := c.checkMethods(tt.requestMethod)
assert.Equalf(t, tt.wantMethods, allowMethods, "checkMethods(%v)", tt.requestMethod)
assert.Equalf(t, tt.wantOk, allowOk, "checkMethods(%v)", tt.requestMethod)
})
}
}
func TestCorsConfig_checkHeaders(t *testing.T) {
tests := []struct {
name string
allowHeaders []string
requestHeaders string
wantHeaders string
wantOk bool
}{
{
name: "not pre-flight",
allowHeaders: []string{"Content-Type", "Authorization"},
requestHeaders: "",
wantHeaders: "Content-Type,Authorization",
wantOk: true,
},
{
name: "blank allowheaders case 1",
allowHeaders: []string{},
requestHeaders: "",
wantHeaders: "",
wantOk: false,
},
{
name: "blank allowheaders case 2",
requestHeaders: "Authorization",
wantHeaders: "",
wantOk: false,
},
{
name: "allowheaders *",
allowHeaders: []string{"*"},
requestHeaders: "Content-Type,Authorization",
wantHeaders: "Content-Type,Authorization",
wantOk: true,
},
{
name: "allowheader values 1",
allowHeaders: []string{"Content-Type", "Authorization"},
requestHeaders: "Content-Type,Authorization",
wantHeaders: "Content-Type,Authorization",
wantOk: true,
},
{
name: "allowheader values 2",
allowHeaders: []string{"Content-Type", "Authorization"},
requestHeaders: "",
wantHeaders: "Content-Type,Authorization",
wantOk: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &CorsConfig{
allowHeaders: tt.allowHeaders,
}
allowHeaders, allowOk := c.checkHeaders(tt.requestHeaders)
assert.Equalf(t, tt.wantHeaders, allowHeaders, "checkHeaders(%v)", tt.requestHeaders)
assert.Equalf(t, tt.wantOk, allowOk, "checkHeaders(%v)", tt.requestHeaders)
})
}
}
func TestCorsConfig_checkOrigin(t *testing.T) {
tests := []struct {
name string
allowOrigins []string
allowOriginPatterns []OriginPattern
origin string
wantOrigin string
wantOk bool
}{
{
name: "allowOrigins *",
allowOrigins: []string{defaultMatchAll},
allowOriginPatterns: []OriginPattern{},
origin: "http://Httpbin.Example.COM",
wantOrigin: "http://Httpbin.Example.COM",
wantOk: true,
},
{
name: "allowOrigins exact match case 1",
allowOrigins: []string{"http://httpbin.example.com"},
allowOriginPatterns: []OriginPattern{},
origin: "http://HTTPBin.EXample.COM",
wantOrigin: "http://HTTPBin.EXample.COM",
wantOk: true,
},
{
name: "allowOrigins exact match case 2",
allowOrigins: []string{"https://httpbin.example.com"},
allowOriginPatterns: []OriginPattern{},
origin: "http://HTTPBin.EXample.COM",
wantOrigin: "",
wantOk: false,
},
{
name: "OriginPattern pattern match with *",
allowOrigins: []string{},
allowOriginPatterns: []OriginPattern{
newOriginPatternFromString("*"),
},
origin: "http://HTTPBin.EXample.COM",
wantOrigin: "http://HTTPBin.EXample.COM",
wantOk: true,
},
{
name: "OriginPattern pattern match case with any port",
allowOrigins: []string{},
allowOriginPatterns: []OriginPattern{
newOriginPatternFromString("http://*.example.com:[*]"),
},
origin: "http://HTTPBin.EXample.COM",
wantOrigin: "http://HTTPBin.EXample.COM",
wantOk: true,
},
{
name: "OriginPattern pattern match case with any port",
allowOrigins: []string{},
allowOriginPatterns: []OriginPattern{
newOriginPatternFromString("http://*.example.com:[*]"),
},
origin: "http://HTTPBin.EXample.COM:10000",
wantOrigin: "http://HTTPBin.EXample.COM:10000",
wantOk: true,
},
{
name: "OriginPattern pattern match case with specail port 1",
allowOrigins: []string{},
allowOriginPatterns: []OriginPattern{
newOriginPatternFromString("http://*.example.com:[8080,9090]"),
},
origin: "http://HTTPBin.EXample.COM:10000",
wantOrigin: "",
wantOk: false,
},
{
name: "OriginPattern pattern match case with specail port 2",
allowOrigins: []string{},
allowOriginPatterns: []OriginPattern{
newOriginPatternFromString("http://*.example.com:[8080,9090]"),
},
origin: "http://HTTPBin.EXample.COM:9090",
wantOrigin: "http://HTTPBin.EXample.COM:9090",
wantOk: true,
},
{
name: "OriginPattern pattern match case with specail port 3",
allowOrigins: []string{},
allowOriginPatterns: []OriginPattern{
newOriginPatternFromString("http://*.example.com:[8080,9090]"),
},
origin: "http://HTTPBin.EXample.org:9090",
wantOrigin: "",
wantOk: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &CorsConfig{
allowOrigins: tt.allowOrigins,
allowOriginPatterns: tt.allowOriginPatterns,
}
allowOrigin, allowOk := c.checkOrigin(tt.origin)
assert.Equalf(t, tt.wantOrigin, allowOrigin, "checkOrigin(%v)", tt.origin)
assert.Equalf(t, tt.wantOk, allowOk, "checkOrigin(%v)", tt.origin)
})
}
}

View File

@@ -0,0 +1,67 @@
apiVersion: networking.higress.io/v1
kind: McpBridge
metadata:
name: mcp-cors-httpbin
namespace: higress-system
spec:
registries:
- domain: httpbin.org
name: httpbin
port: 80
type: dns
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
higress.io/destination: httpbin.dns
higress.io/upstream-vhost: "httpbin.org"
higress.io/backend-protocol: HTTP
name: ingress-cors-httpbin
namespace: higress-system
spec:
ingressClassName: higress
rules:
- host: httpbin.example.com
http:
paths:
- backend:
resource:
apiGroup: networking.higress.io
kind: McpBridge
name: mcp-cors-httpbin
path: /
pathType: Prefix
---
apiVersion: extensions.higress.io/v1alpha1
kind: WasmPlugin
metadata:
name: wasm-cors-httpbin
namespace: higress-system
spec:
defaultConfigDisable: true
matchRules:
- config:
allow_origins:
- http://httpbin.example.net
allow_origin_patterns:
- http://*.example.com:[*]
- http://*.example.org:[9090,8080]
allow_methods:
- GET
- POST
- PATCH
allow_headers:
- Content-Type
- Token
- Authorization
expose_headers:
- X-Custom-Header
- X-Env-UTM
allow_credentials: true
max_age: 3600
configDisable: false
ingress:
- ingress-cors-httpbin
url: oci://docker.io/2456868764/cors:1.0.0
imagePullPolicy: Always

View File

@@ -0,0 +1,78 @@
static_resources:
listeners:
- name: main
address:
socket_address:
address: 0.0.0.0
port_value: 18000
filter_chains:
- filters:
- name: envoy.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: ingress_http
codec_type: auto
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains:
- "httpbin.example.com"
routes:
- match:
prefix: "/"
route:
cluster: httpbin
http_filters:
- name: envoy.filters.http.wasm
typed_config:
"@type": type.googleapis.com/udpa.type.v1.TypedStruct
type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm
value:
config:
configuration:
"@type": type.googleapis.com/google.protobuf.StringValue
value: |-
{
"allow_origins": ["http://httpbin.example.net"],
"allow_origin_patterns": ["http://*.example.com:[*]", "http://*.example.org:[9090,8080]"],
"allow_methods": ["GET","PUT","POST", "PATCH", "HEAD", "OPTIONS"],
"allow_credentials": true,
"allow_headers":["Content-Type", "Token","Authorization"],
"expose_headers":["X-Custom-Header"],
"max_age": 3600
}
vm_config:
runtime: "envoy.wasm.runtime.v8"
code:
local:
filename: "./main.wasm"
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
clusters:
- name: httpbin
connect_timeout: 0.5s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
dns_refresh_rate: 5s
dns_lookup_family: V4_ONLY
load_assignment:
cluster_name: httpbin
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: httpbin.org
port_value: 80
admin:
access_log_path: "/dev/null"
address:
socket_address:
address: 0.0.0.0
port_value: 8001

View File

@@ -0,0 +1,21 @@
module cors
go 1.19
replace github.com/alibaba/higress/plugins/wasm-go => ../..
require (
github.com/alibaba/higress/plugins/wasm-go v0.0.0-20230519024024-625c06e58f91
github.com/stretchr/testify v1.8.3
github.com/tetratelabs/proxy-wasm-go-sdk v0.22.0
github.com/tidwall/gjson v1.14.4
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View File

@@ -0,0 +1,20 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tetratelabs/proxy-wasm-go-sdk v0.22.0 h1:kS7BvMKN+FiptV4pfwiNX8e3q14evxAWkhYbxt8EI1M=
github.com/tetratelabs/proxy-wasm-go-sdk v0.22.0/go.mod h1:qkW5MBz2jch2u8bS59wws65WC+Gtx3x0aPUX5JL7CXI=
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -0,0 +1,169 @@
// 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 main
import (
"cors/config"
"fmt"
"github.com/alibaba/higress/plugins/wasm-go/pkg/wrapper"
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm"
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types"
"github.com/tidwall/gjson"
)
func main() {
wrapper.SetCtx(
"cors",
wrapper.ParseConfigBy(parseConfig),
wrapper.ProcessRequestHeadersBy(onHttpRequestHeaders),
wrapper.ProcessRequestBodyBy(onHttpRequestBody),
wrapper.ProcessResponseBodyBy(onHttpResponseBody),
wrapper.ProcessResponseHeadersBy(onHttpResponseHeaders),
)
}
func parseConfig(json gjson.Result, corsConfig *config.CorsConfig, log wrapper.Log) error {
log.Debug("parseConfig()")
allowOrigins := json.Get("allow_origins").Array()
for _, origin := range allowOrigins {
if err := corsConfig.AddAllowOrigin(origin.String()); err != nil {
log.Warnf("failed to AddAllowOrigin:%s, error:%v", origin, err)
}
}
allowOriginPatterns := json.Get("allow_origin_patterns").Array()
for _, pattern := range allowOriginPatterns {
corsConfig.AddAllowOriginPattern(pattern.String())
}
allowMethods := json.Get("allow_methods").Array()
for _, method := range allowMethods {
corsConfig.AddAllowMethod(method.String())
}
allowHeaders := json.Get("allow_headers").Array()
for _, header := range allowHeaders {
corsConfig.AddAllowHeader(header.String())
}
exposeHeaders := json.Get("expose_headers").Array()
for _, header := range exposeHeaders {
corsConfig.AddExposeHeader(header.String())
}
allowCredentials := json.Get("allow_credentials").Bool()
if err := corsConfig.SetAllowCredentials(allowCredentials); err != nil {
log.Warnf("failed to set AllowCredentials error: %v", err)
}
maxAge := json.Get("max_age").Int()
corsConfig.SetMaxAge(int(maxAge))
// Fill default values
corsConfig.FillDefaultValues()
log.Debugf("corsConfig:%+v", corsConfig)
return nil
}
func onHttpRequestHeaders(ctx wrapper.HttpContext, corsConfig config.CorsConfig, log wrapper.Log) types.Action {
log.Debug("onHttpRequestHeaders()")
requestUrl, _ := proxywasm.GetHttpRequestHeader(":path")
method, _ := proxywasm.GetHttpRequestHeader(":method")
host := ctx.Host()
scheme := ctx.Scheme()
log.Debugf("scheme:%s, host:%s, method:%s, request:%s", scheme, host, method, requestUrl)
// Get headers
headers, _ := proxywasm.GetHttpRequestHeaders()
// Process request
httpCorsContext, err := corsConfig.Process(scheme, host, method, headers)
if err != nil {
log.Warnf("failed to process %s : %v", requestUrl, err)
return types.ActionContinue
}
log.Debugf("Process httpCorsContext:%+v", httpCorsContext)
// Set HttpContext
ctx.SetContext(config.HttpContextKey, httpCorsContext)
// Response forbidden when it is not valid cors request
if !httpCorsContext.IsValid {
headers := make([][2]string, 0)
headers = append(headers, [2]string{config.HeaderPluginTrace, "trace"})
proxywasm.SendHttpResponse(403, headers, []byte("Invalid CORS request"), -1)
return types.ActionPause
}
// Response directly when it is cors preflight request
if httpCorsContext.IsPreFlight {
headers := make([][2]string, 0)
headers = append(headers, [2]string{config.HeaderPluginTrace, "trace"})
proxywasm.SendHttpResponse(200, headers, nil, -1)
return types.ActionPause
}
return types.ActionContinue
}
func onHttpRequestBody(ctx wrapper.HttpContext, corsConfig config.CorsConfig, body []byte, log wrapper.Log) types.Action {
log.Debug("onHttpRequestBody()")
return types.ActionContinue
}
func onHttpResponseHeaders(ctx wrapper.HttpContext, corsConfig config.CorsConfig, log wrapper.Log) types.Action {
log.Debug("onHttpResponseHeaders()")
// Remove trace header if existed
proxywasm.RemoveHttpResponseHeader(config.HeaderPluginTrace)
// Remove upstream cors response headers if existed
proxywasm.RemoveHttpResponseHeader(config.HeaderAccessControlAllowOrigin)
proxywasm.RemoveHttpResponseHeader(config.HeaderAccessControlAllowMethods)
proxywasm.RemoveHttpResponseHeader(config.HeaderAccessControlExposeHeaders)
proxywasm.RemoveHttpResponseHeader(config.HeaderAccessControlAllowCredentials)
proxywasm.RemoveHttpResponseHeader(config.HeaderAccessControlMaxAge)
// Add debug header
proxywasm.AddHttpResponseHeader(config.HeaderPluginDebug, corsConfig.GetVersion())
// Restore httpCorsContext from HttpContext
httpCorsContext, ok := ctx.GetContext(config.HttpContextKey).(config.HttpCorsContext)
if !ok {
log.Debug("restore httpCorsContext from HttpContext error")
return types.ActionContinue
}
log.Debugf("Restore httpCorsContext:%+v", httpCorsContext)
// Skip which it is not valid or not cors request
if !httpCorsContext.IsValid || !httpCorsContext.IsCorsRequest {
return types.ActionContinue
}
// Add Cors headers when it is cors and valid request
if len(httpCorsContext.AllowOrigin) > 0 {
proxywasm.AddHttpResponseHeader(config.HeaderAccessControlAllowOrigin, httpCorsContext.AllowOrigin)
}
if len(httpCorsContext.AllowMethods) > 0 {
proxywasm.AddHttpResponseHeader(config.HeaderAccessControlAllowMethods, httpCorsContext.AllowMethods)
}
if len(httpCorsContext.AllowHeaders) > 0 {
proxywasm.AddHttpResponseHeader(config.HeaderAccessControlAllowHeaders, httpCorsContext.AllowHeaders)
}
if len(httpCorsContext.ExposeHeaders) > 0 {
proxywasm.AddHttpResponseHeader(config.HeaderAccessControlExposeHeaders, httpCorsContext.ExposeHeaders)
}
if httpCorsContext.AllowCredentials {
proxywasm.AddHttpResponseHeader(config.HeaderAccessControlAllowCredentials, "true")
}
if httpCorsContext.MaxAge > 0 {
proxywasm.AddHttpResponseHeader(config.HeaderAccessControlMaxAge, fmt.Sprintf("%d", httpCorsContext.MaxAge))
}
return types.ActionContinue
}
func onHttpResponseBody(ctx wrapper.HttpContext, corsConfig config.CorsConfig, body []byte, log wrapper.Log) types.Action {
log.Debug("onHttpResponseBody()")
return types.ActionContinue
}

View File

@@ -0,0 +1,195 @@
# DeGraphQL
## GraphQL
### GraphQL 端点
REST API 有多个端点GraphQL API 只有一个端点。
```shell
https://api.github.com/graphql
```
### 与 GraphQL 通信
由于 GraphQL 操作由多行 JSON 组成,可以使用 curl 或任何其他采用 HTTP 的库。
在 REST 中HTTP 谓词确定执行的操作。 在 GraphQL 中,执行查询要提供 JSON 请求体,因此 HTTP 谓词为 POST。 唯一的例外是内省查询,它是一种简单的 GET 到终结点查询。
### GraphQL POST 请求参数
标准的 GraphQL POST 请求情况如下:
- 添加 HTTP 请求头: Content-Type: application/json
- 使用 JSON 格式的请求体
- JSON 请求体包含三个字段
- query查询文档必填
- variables变量选填
- operationName操作名称选填查询文档有多个操作时必填
```json
{
"query": "{viewer{name}}",
"operationName": "",
"variables": {
"name": "value"
}
}
```
### GraphQL 基本参数类型
- 基本参数类型包含: String, Int, Float, Boolean
- [类型]代表数组,例如:[Int]代表整型数组
- GraphQL 基本参数传递
- 小括号内定义形参,注意:参数需要定义类型
- !(叹号)代表参数不能为空
```shell
query ($owner : String!, $name : String!) {
repository(owner: $owner, name: $name) {
name
forkCount
description
}
}
```
### GitHub GraphQL 测试
使用 curl 命令查询 GraphQL 用有效 JSON 请求体发出 POST 请求。 有效请求体必须包含一个名为 query 的字符串。
```shell
curl https://api.github.com/graphql -X POST \
-H "Authorization: bearer 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" \
-d "{\"query\": \"query { viewer { login }}\"}"
{
"data": {
"viewer": {
"login": "2456868764"
}
}
}
```
```shell
curl 'https://api.github.com/graphql' -X POST \
-H 'Authorization: bearer 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' \
-d '{"query":"query ($owner: String!, $name: String!) {\n repository(owner: $owner, name: $name) {\n name\n forkCount\n description\n }\n}\n","variables":{"owner":"2456868764","name":"higress"}}'
{
"data": {
"repository": {
"name": "higress",
"forkCount": 149,
"description": "Next-generation Cloud Native Gateway | 下一代云原生网关"
}
}
}
```
## DeGraphQL 插件
### 参数配置
| 参数 | 描述 | 默认 |
|:----------------|:------------------------|:-----------|
| `gql` | graphql 查询 | 不能为空 |
| `endpoint` | graphql 查询端点 | `/graphql` |
| `timeout` | 查询连接超时,单位毫秒 | `5000` |
| `domain` | 服务域名当服务来源是dns配置 | |
### 插件使用
https://github.com/alibaba/higress/issues/268
- 测试配置
```yaml
apiVersion: networking.higress.io/v1
kind: McpBridge
metadata:
name: default
namespace: higress-system
spec:
registries:
- domain: api.github.com
name: github
port: 443
type: dns
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
higress.io/destination: github.dns
higress.io/upstream-vhost: "api.github.com"
higress.io/backend-protocol: HTTPS
name: github-api
namespace: higress-system
spec:
ingressClassName: higress
rules:
- http:
paths:
- backend:
resource:
apiGroup: networking.higress.io
kind: McpBridge
name: default
path: /api
pathType: Prefix
---
apiVersion: extensions.higress.io/v1alpha1
kind: WasmPlugin
metadata:
name: de-graphql-github-api
namespace: higress-system
spec:
matchRules:
- ingress:
- github-api
config:
timeout: 5000
endpoint: /graphql
domain: api.github.com
gql: |
query ($owner:String! $name:String!){
repository(owner:$owner, name:$name) {
name
forkCount
description
}
}
url: oci://higress-registry.cn-hangzhou.cr.aliyuncs.com/plugins/de-graphql:1.0.0
```
- 测试结果
```shell
curl "http://localhost/api?owner=alibaba&name=higress" -H "Authorization: Bearer some-token"
{
"data": {
"repository": {
"description": "Next-generation Cloud Native Gateway",
"forkCount": 149,
"name": "higress"
}
}
}
```
## 参考文档
- https://github.com/graphql/graphql-spec
- https://docs.github.com/zh/graphql/guides/forming-calls-with-graphql
- https://github.com/altair-graphql/altair

View File

@@ -0,0 +1 @@
1.0.0

View File

@@ -0,0 +1,198 @@
// 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 config
import (
"errors"
"net/url"
"regexp"
"strings"
)
const (
DefaultEndpoint string = "/graphql"
DefaultConnectionTimeout uint32 = 5000
)
var gqlVariableRegex = regexp.MustCompile(`\$(\w+)\s*:\s*(String|Float|Int|Boolean)(!?)`)
type VariableType string
const (
StringType VariableType = "String"
IntType VariableType = "Int"
FloatType VariableType = "Float"
BooleanType VariableType = "Boolean"
)
type Variable struct {
name string
typ VariableType
blank bool
value string
}
type DeGraphQLConfig struct {
gql string
endpoint string
timeout uint32
domain string
variables []Variable
}
func (d *DeGraphQLConfig) SetEndpoint(endpoint string) error {
endpoint = strings.TrimSpace(endpoint)
if endpoint == "" {
d.endpoint = DefaultEndpoint
} else {
d.endpoint = endpoint
}
return nil
}
func (d *DeGraphQLConfig) GetDomain() string {
return d.domain
}
func (d *DeGraphQLConfig) SetDomain(domain string) {
d.domain = domain
}
func (d *DeGraphQLConfig) GetEndpoint() string {
return d.endpoint
}
func (d *DeGraphQLConfig) GetTimeout() uint32 {
return d.timeout
}
func (d *DeGraphQLConfig) SetTimeout(timeout uint32) {
if timeout <= 0 {
d.timeout = DefaultConnectionTimeout
} else {
d.timeout = timeout
}
}
func (d *DeGraphQLConfig) SetGql(gql string) error {
if strings.TrimSpace(gql) == "" {
return errors.New("gql can't be empty")
}
d.gql = gql
d.variables = make([]Variable, 0)
matches := gqlVariableRegex.FindAllStringSubmatch(d.gql, -1)
if len(matches) > 0 {
for _, subMatch := range matches {
variable := Variable{}
variable.name = subMatch[1]
switch subMatch[2] {
case "String":
variable.typ = StringType
case "Float":
variable.typ = FloatType
case "Int":
variable.typ = IntType
case "Boolean":
variable.typ = BooleanType
}
variable.blank = subMatch[3] != "!"
d.variables = append(d.variables, variable)
}
}
return nil
}
func (d *DeGraphQLConfig) GetGql() string {
return d.gql
}
func (d *DeGraphQLConfig) GetVersion() string {
return "1.0.0"
}
func (d *DeGraphQLConfig) ParseGqlFromUrl(requestUrl string) (string, error) {
if strings.TrimSpace(requestUrl) == "" {
return "", errors.New("request url can't be empty")
}
url, _ := url.Parse(requestUrl)
queryValues := url.Query()
values := make(map[string]string, len(queryValues))
for k, v := range queryValues {
var v1 string
if len(v) > 1 {
v1 = strings.Join(v, ",")
} else {
v1 = v[0]
}
values[k] = v1
}
variables := make([]Variable, 0, len(d.variables))
for _, variable := range d.variables {
val, ok := values[variable.name]
// TODO validate variable type and blank
if ok {
variables = append(variables, Variable{
name: variable.name,
typ: variable.typ,
blank: variable.blank,
value: val,
})
}
}
var build strings.Builder
// write query
build.WriteString("{\"query\":")
build.WriteString("\"")
build.WriteString(getJsonStr(d.gql))
build.WriteString("\"")
// write varialbes
if len(variables) > 0 {
index := 0
build.WriteString(",")
build.WriteString("\"variables\":{")
for _, variable := range variables {
build.WriteString("\"")
build.WriteString(variable.name)
build.WriteString("\":")
if variable.typ == StringType {
build.WriteString("\"")
build.WriteString(getJsonStr(variable.value))
build.WriteString("\"")
} else {
build.WriteString(variable.value)
}
if index < len(variables)-1 {
build.WriteString(",")
}
index++
}
build.WriteString("}")
}
build.WriteString("}")
return build.String(), nil
}
func getJsonStr(str string) string {
d := strings.ReplaceAll(str, "\"", "\\\"")
return strings.ReplaceAll(d, "\n", "\\n")
}

View File

@@ -0,0 +1,177 @@
// 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 config
import (
"errors"
"github.com/stretchr/testify/assert"
"testing"
)
func TestDeGraphQLConfig_SetGql(t *testing.T) {
tests := []struct {
name string
gql string
wantVariables []Variable
wantErr error
}{
{
name: "empty gql",
gql: "",
wantErr: errors.New("gql can't be empty"),
},
{
name: "no params",
gql: "query",
wantVariables: []Variable{},
wantErr: nil,
},
{
name: "four params",
gql: "query ($owner:String $num:Float! $int : Int! $boolean : Boolean )",
wantErr: nil,
wantVariables: []Variable{
{
name: "owner",
typ: StringType,
blank: true,
},
{
name: "num",
typ: FloatType,
blank: false,
},
{
name: "int",
typ: IntType,
blank: false,
},
{
name: "boolean",
typ: BooleanType,
blank: true,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
d := &DeGraphQLConfig{}
err := d.SetGql(tt.gql)
assert.Equal(t, tt.wantErr, err)
if err != nil {
return
}
assert.Equal(t, tt.wantVariables, d.variables)
})
}
}
func TestDeGraphQLConfig_ParseGqlFromUrl(t *testing.T) {
tests := []struct {
name string
gql string
url string
want string
wantErr error
}{
{
name: "empty url",
gql: "query ($owner:String! $name:String!)",
url: "",
want: "",
wantErr: errors.New("request url can't be empty"),
},
{
name: "no params",
gql: "query HeroNameQuery {\n hero {\n name\n }\n}",
url: "/api?owner=a",
want: "{\"query\":\"query HeroNameQuery {\\n hero {\\n name\\n }\\n}\"}",
wantErr: nil,
},
{
name: "one string variable",
gql: "query FetchSomeIDQuery($someId: String!) {\n human(id: $someId) {\n name\n }\n}",
url: "/api?someId=a",
want: "{\"query\":\"query FetchSomeIDQuery($someId: String!) {\\n human(id: $someId) {\\n name\\n }\\n}\",\"variables\":{\"someId\":\"a\"}}",
wantErr: nil,
},
{
name: "multi variables",
gql: "query FetchSomeIDQuery($someId: String! $num: Int $price: Float! $need:Boolean!) {\n human(id: $someId) {\n name\n }\n}",
url: "/api?someId=a&num=10&price=12.0&need=false&hee=1",
want: "{\"query\":\"query FetchSomeIDQuery($someId: String! $num: Int $price: Float! $need:Boolean!) {\\n human(id: $someId) {\\n name\\n }\\n}\",\"variables\":{\"someId\":\"a\",\"num\":10,\"price\":12.0,\"need\":false}}",
wantErr: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
d := &DeGraphQLConfig{}
d.SetGql(tt.gql)
body, err := d.ParseGqlFromUrl(tt.url)
assert.Equal(t, tt.wantErr, err)
if err != nil {
return
}
assert.Equal(t, tt.want, body)
})
}
}
func TestDeGraphQLConfig_SetEndpoint(t *testing.T) {
tests := []struct {
name string
endPoint string
wantErr error
want string
}{
{
name: "empty endpoint",
endPoint: "",
wantErr: nil,
want: "/graphql",
},
{
name: "empty endpoint with blank",
endPoint: " ",
wantErr: nil,
want: "/graphql",
},
{
name: "with value",
endPoint: " /graphql2 ",
wantErr: nil,
want: "/graphql2",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
d := &DeGraphQLConfig{}
err := d.SetEndpoint(tt.endPoint)
assert.Equal(t, tt.wantErr, err)
if err != nil {
return
}
assert.Equal(t, tt.want, d.endpoint)
})
}
}

View File

@@ -0,0 +1,122 @@
static_resources:
listeners:
- name: main
address:
socket_address:
address: 0.0.0.0
port_value: 18000
filter_chains:
- filters:
- name: envoy.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: ingress_http
codec_type: auto
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains:
- "*"
routes:
- match:
prefix: "/api"
route:
cluster: github
http_filters:
- name: envoy.filters.http.wasm
typed_config:
"@type": type.googleapis.com/udpa.type.v1.TypedStruct
type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm
value:
config:
configuration:
"@type": type.googleapis.com/google.protobuf.StringValue
value: |-
{
"gql": "query ($owner:String! $name:String!){\n repository(owner:$owner, name:$name) {\n name\n forkCount\n description\n}\n}",
"domain": "api.github.com",
"endpoint": "/graphql",
"timeout": 2000
}
vm_config:
runtime: "envoy.wasm.runtime.v8"
code:
local:
filename: "./main.wasm"
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
- name: staticreply
address:
socket_address:
address: 127.0.0.1
port_value: 8099
filter_chains:
- filters:
- name: envoy.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: ingress_http
codec_type: auto
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains:
- "*"
routes:
- match:
prefix: "/"
direct_response:
status: 200
body:
inline_string: "example body\n"
http_filters:
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
clusters:
- name: mock_service
connect_timeout: 0.25s
type: STATIC
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: mock_service
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 127.0.0.1
port_value: 8099
- name: github
connect_timeout: 0.5s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
dns_refresh_rate: 5s
dns_lookup_family: V4_ONLY
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
load_assignment:
cluster_name: github
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: api.github.com
port_value: 443
admin:
access_log_path: "/dev/null"
address:
socket_address:
address: 0.0.0.0
port_value: 8001

View File

@@ -0,0 +1,22 @@
module de-graphql
go 1.19
replace github.com/alibaba/higress/plugins/wasm-go => ../..
require (
github.com/alibaba/higress/plugins/wasm-go v0.0.0-20230410091208-df60dd43079c
github.com/stretchr/testify v1.8.0
github.com/tetratelabs/proxy-wasm-go-sdk v0.22.0
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/tetratelabs/wazero v1.0.0-rc.1 // indirect
github.com/tidwall/gjson v1.14.4 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View File

@@ -0,0 +1,29 @@
github.com/alibaba/higress/plugins/wasm-go v0.0.0-20230410091208-df60dd43079c h1:W1QzLx6pefqDWi4peW2HKcZY0rgEy11+JCuWtssp1Ew=
github.com/alibaba/higress/plugins/wasm-go v0.0.0-20230410091208-df60dd43079c/go.mod h1:AzSnkuon5c26nIePTiJQIAFsKdhkNdncLcTuahpGtQs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/tetratelabs/proxy-wasm-go-sdk v0.22.0 h1:kS7BvMKN+FiptV4pfwiNX8e3q14evxAWkhYbxt8EI1M=
github.com/tetratelabs/proxy-wasm-go-sdk v0.22.0/go.mod h1:qkW5MBz2jch2u8bS59wws65WC+Gtx3x0aPUX5JL7CXI=
github.com/tetratelabs/wazero v1.0.0-rc.1 h1:ytecMV5Ue0BwezjKh/cM5yv1Mo49ep2R2snSsQUyToc=
github.com/tetratelabs/wazero v1.0.0-rc.1/go.mod h1:wYx2gNRg8/WihJfSDxA1TIL8H+GkfLYm+bIfbblu9VQ=
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -0,0 +1,59 @@
apiVersion: networking.higress.io/v1
kind: McpBridge
metadata:
name: default
namespace: higress-system
spec:
registries:
- domain: api.github.com
name: github
port: 443
type: dns
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
higress.io/destination: github.dns
higress.io/upstream-vhost: "api.github.com"
higress.io/backend-protocol: HTTPS
name: github-api
namespace: higress-system
spec:
ingressClassName: higress
rules:
- http:
paths:
- backend:
resource:
apiGroup: networking.higress.io
kind: McpBridge
name: default
path: /api
pathType: Prefix
---
apiVersion: extensions.higress.io/v1alpha1
kind: WasmPlugin
metadata:
name: de-graphql-github-api
namespace: higress-system
spec:
defaultConfigDisable: true
matchRules:
- config:
domain: api.github.com
endpoint: /graphql
gql: |-
query ($owner:String! $name:String!){
repository(owner:$owner, name:$name) {
name
forkCount
description
}
}
timeout: 5000
configDisable: false
ingress:
- github-api
url: oci://docker.io/2456868764/de-graphql:1.0.0

View File

@@ -0,0 +1,117 @@
// 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 main
import (
"fmt"
"net/http"
"de-graphql/config"
"github.com/alibaba/higress/plugins/wasm-go/pkg/wrapper"
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm"
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types"
"github.com/tidwall/gjson"
)
func main() {
wrapper.SetCtx(
"de-graphql",
wrapper.ParseConfigBy(parseConfig),
wrapper.ProcessRequestHeadersBy(onHttpRequestHeaders),
wrapper.ProcessRequestBodyBy(onHttpRequestBody),
wrapper.ProcessResponseBodyBy(onHttpResponseBody),
wrapper.ProcessResponseHeadersBy(onHttpResponseHeaders),
)
}
func parseConfig(json gjson.Result, config *config.DeGraphQLConfig, log wrapper.Log) error {
log.Debug("parseConfig()")
gql := json.Get("gql").String()
endpoint := json.Get("endpoint").String()
timeout := json.Get("timeout").Int()
domain := json.Get("domain").String()
log.Debugf("gql:%s endpoint:%s timeout:%d domain:%s", gql, endpoint, timeout, domain)
err := config.SetGql(gql)
if err != nil {
return err
}
err = config.SetEndpoint(endpoint)
if err != nil {
return err
}
config.SetTimeout(uint32(timeout))
config.SetDomain(domain)
return nil
}
func onHttpRequestHeaders(ctx wrapper.HttpContext, config config.DeGraphQLConfig, log wrapper.Log) types.Action {
log.Debug("onHttpRequestHeaders()")
log.Debugf("schema:%s host:%s path:%s", ctx.Scheme(), ctx.Host(), ctx.Path())
requestUrl, _ := proxywasm.GetHttpRequestHeader(":path")
method, _ := proxywasm.GetHttpRequestHeader(":method")
log.Debugf("method:%s, request:%s", method, requestUrl)
if err := proxywasm.RemoveHttpRequestHeader("content-length"); err != nil {
log.Debug("can not reset content-length")
}
replaceBody, err := config.ParseGqlFromUrl(requestUrl)
if err != nil {
log.Warnf("failed to parse request url %s : %v", requestUrl, err)
}
log.Debugf("replace body:%s", replaceBody)
// Pass headers to upstream cluster
headers, _ := proxywasm.GetHttpRequestHeaders()
for i := len(headers) - 1; i >= 0; i-- {
key := headers[i][0]
if key == ":method" || key == ":path" || key == ":authority" {
headers = append(headers[:i], headers[i+1:]...)
}
}
// Add header Content-Type: application/json
headers = append(headers, [2]string{"Content-Type", "application/json"})
client := wrapper.NewClusterClient(wrapper.RouteCluster{Host: config.GetDomain()})
// Call upstream graphql endpoint
client.Post(config.GetEndpoint(), headers, []byte(replaceBody),
func(statusCode int, responseHeaders http.Header, responseBody []byte) {
// Pass response headers and body to client
headers := make([][2]string, 0, len(responseHeaders)+3)
for headK, headV := range responseHeaders {
headers = append(headers, [2]string{headK, headV[0]})
}
// Add debug headers
headers = append(headers, [2]string{"x-degraphql-endpoint", config.GetEndpoint()})
headers = append(headers, [2]string{"x-degraphql-timeout", fmt.Sprintf("%d", config.GetTimeout())})
headers = append(headers, [2]string{"x-degraphql-version", config.GetVersion()})
proxywasm.SendHttpResponse(uint32(statusCode), headers, responseBody, -1)
return
}, config.GetTimeout())
return types.ActionPause
}
func onHttpRequestBody(ctx wrapper.HttpContext, config config.DeGraphQLConfig, body []byte, log wrapper.Log) types.Action {
log.Debug("onHttpRequestBody()")
return types.ActionContinue
}
func onHttpResponseHeaders(ctx wrapper.HttpContext, config config.DeGraphQLConfig, log wrapper.Log) types.Action {
log.Debug("onHttpResponseHeaders()")
return types.ActionContinue
}
func onHttpResponseBody(ctx wrapper.HttpContext, config config.DeGraphQLConfig, body []byte, log wrapper.Log) types.Action {
log.Debug("onHttpResponseBody()")
return types.ActionContinue
}

Some files were not shown because too many files have changed in this diff Show More