mirror of
https://github.com/alibaba/higress.git
synced 2026-02-26 13:40:49 +08:00
Compare commits
53 Commits
v1.0.0-rc.
...
v1.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c76ae26bb | ||
|
|
c1250aec2e | ||
|
|
02bc319eef | ||
|
|
28892cf3ae | ||
|
|
bee03a37a4 | ||
|
|
2a97921d2b | ||
|
|
d4dbaba760 | ||
|
|
d718870b65 | ||
|
|
b65446fa25 | ||
|
|
3fd37abab7 | ||
|
|
81e467b624 | ||
|
|
736eea6cf9 | ||
|
|
c32e1ab69b | ||
|
|
fc05a3b256 | ||
|
|
9fc2760b7d | ||
|
|
be88647752 | ||
|
|
a56172095a | ||
|
|
f3270123ba | ||
|
|
5e2d62406b | ||
|
|
39cab9d724 | ||
|
|
89865733f6 | ||
|
|
1ccf9195b2 | ||
|
|
7d2a05ef1c | ||
|
|
32c2acefda | ||
|
|
ea7b581e26 | ||
|
|
ac2f0a5545 | ||
|
|
51d7124454 | ||
|
|
ec6a185adc | ||
|
|
f9ffda288b | ||
|
|
2dbe41324a | ||
|
|
80d6ecfddb | ||
|
|
b23fae7a12 | ||
|
|
2c19d97252 | ||
|
|
efd7ccd5fe | ||
|
|
81fd0d6386 | ||
|
|
176ddc6963 | ||
|
|
44637c2449 | ||
|
|
3e68ae75d1 | ||
|
|
18ad817edb | ||
|
|
d48e0ce773 | ||
|
|
9734ffeb3e | ||
|
|
1421ce8667 | ||
|
|
625c06e58f | ||
|
|
e4a47dfb46 | ||
|
|
6b483189ac | ||
|
|
74ad9a555a | ||
|
|
f6e181ecb6 | ||
|
|
30a5b2ab2b | ||
|
|
91a23cc27e | ||
|
|
51e515d53e | ||
|
|
67274bfa0d | ||
|
|
4f24979579 | ||
|
|
1f4bf8e0b2 |
120
.github/workflows/build-and-test.yaml
vendored
120
.github/workflows/build-and-test.yaml
vendored
@@ -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
|
||||
|
||||
144
.github/workflows/build-image-and-push.yaml
vendored
Normal file
144
.github/workflows/build-image-and-push.yaml
vendored
Normal 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
|
||||
37
.github/workflows/deploy-standalone-to-oss.yaml
vendored
Normal file
37
.github/workflows/deploy-standalone-to-oss.yaml
vendored
Normal 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
|
||||
5
.github/workflows/deploy-to-oss.yaml
vendored
5
.github/workflows/deploy-to-oss.yaml
vendored
@@ -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
4
.gitignore
vendored
@@ -12,4 +12,6 @@ bazel-out
|
||||
bazel-testlogs
|
||||
bazel-wasm-cpp
|
||||
tools/bin/
|
||||
helm/**/charts/**.tgz
|
||||
helm/**/charts/**.tgz
|
||||
target/
|
||||
tools/hack/cluster.conf
|
||||
@@ -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
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
Higress 是基于阿里内部两年多的 Envoy Gateway 实践沉淀,以开源 [Istio](https://github.com/istio/istio) 与 [Envoy](https://github.com/envoyproxy/envoy) 为核心构建的下一代云原生网关。Higress 实现了安全防护网关、流量网关、微服务网关三层网关合一,可以显著降低网关的部署和运维成本。
|
||||
|
||||

|
||||

|
||||
|
||||
## Summary
|
||||
|
||||
@@ -125,7 +125,7 @@ Higress 是基于阿里内部两年多的 Envoy Gateway 实践沉淀,以开源
|
||||
|
||||
社区交流群:
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
开发者群:
|
||||
|
||||
@@ -23,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>
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
1666
api/networking/v1/http_2_rpc.pb.go
Normal file
1666
api/networking/v1/http_2_rpc.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
74
api/networking/v1/http_2_rpc.proto
Normal file
74
api/networking/v1/http_2_rpc.proto
Normal 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 {
|
||||
}
|
||||
121
api/networking/v1/http_2_rpc_deepcopy.gen.go
Normal file
121
api/networking/v1/http_2_rpc_deepcopy.gen.go
Normal 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()
|
||||
}
|
||||
78
api/networking/v1/http_2_rpc_json.gen.go
Normal file
78
api/networking/v1/http_2_rpc_json.gen.go
Normal 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}
|
||||
)
|
||||
@@ -41,6 +41,8 @@ func Resource(resource string) schema.GroupResource {
|
||||
|
||||
func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
scheme.AddKnownTypes(SchemeGroupVersion,
|
||||
&Http2Rpc{},
|
||||
&Http2RpcList{},
|
||||
&McpBridge{},
|
||||
&McpBridgeList{},
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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}
|
||||
}
|
||||
|
||||
@@ -16,4 +16,6 @@
|
||||
|
||||
package v1
|
||||
|
||||
type Http2RpcExpansion interface{}
|
||||
|
||||
type McpBridgeExpansion interface{}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
@@ -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}
|
||||
|
||||
@@ -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{}
|
||||
|
||||
92
client/pkg/listers/networking/v1/http2rpc.gen.go
Normal file
92
client/pkg/listers/networking/v1/http2rpc.gen.go
Normal 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
1
helm/core/.helmignore
Normal file
@@ -0,0 +1 @@
|
||||
crds/customresourcedefinitions.gen_lt1.16.yaml
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
176
helm/core/crds/customresourcedefinitions.gen_lt1.16.yaml
Normal file
176
helm/core/crds/customresourcedefinitions.gen_lt1.16.yaml
Normal 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: {}
|
||||
---
|
||||
@@ -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 }}
|
||||
|
||||
@@ -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 }}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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: 1.0.0-rc
|
||||
digest: sha256:e22d3f977aec759e23f035bad2cdff643e5f670788872316234701f6d0d91418
|
||||
generated: "2023-05-08T09:40:35.6095006+08:00"
|
||||
version: 1.1.0
|
||||
digest: sha256:ab013ed8a315faf016e4114f2315af3449df0a6aa62bb0b8b83704d346109722
|
||||
generated: "2023-07-10T20:56:37.100196+08:00"
|
||||
|
||||
@@ -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: 1.0.0-rc
|
||||
version: 1.1.0
|
||||
type: application
|
||||
version: 1.0.0-rc
|
||||
version: 1.1.0
|
||||
|
||||
@@ -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" \
|
||||
|
||||
30
istio/1.12/patches/istio/20230618-debug-api-anonymous.patch
Normal file
30
istio/1.12/patches/istio/20230618-debug-api-anonymous.patch
Normal 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 {
|
||||
15
istio/1.12/patches/istio/20230627-debug-fix-configz.patch
Normal file
15
istio/1.12/patches/istio/20230627-debug-fix-configz.patch
Normal 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)
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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{},
|
||||
|
||||
53
pkg/ingress/kube/annotations/http2rpc.go
Normal file
53
pkg/ingress/kube/annotations/http2rpc.go
Normal 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)
|
||||
}
|
||||
59
pkg/ingress/kube/annotations/http2rpc_test.go
Normal file
59
pkg/ingress/kube/annotations/http2rpc_test.go
Normal 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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{
|
||||
|
||||
50
pkg/ingress/kube/configmap/config.go
Normal file
50
pkg/ingress/kube/configmap/config.go
Normal 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)
|
||||
}
|
||||
202
pkg/ingress/kube/configmap/controller.go
Normal file
202
pkg/ingress/kube/configmap/controller.go
Normal 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
|
||||
}
|
||||
408
pkg/ingress/kube/configmap/tracing.go
Normal file
408
pkg/ingress/kube/configmap/tracing.go
Normal 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 skywalking,zipkin 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
|
||||
}
|
||||
36
pkg/ingress/kube/http2rpc/controller.go
Normal file
36
pkg/ingress/kube/http2rpc/controller.go
Normal 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)
|
||||
}
|
||||
@@ -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},
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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中的指定字段,要求必须是字符串或无符号整数类型 |
|
||||
|
||||
@@ -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. |
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
| limit_keys | array of object | 必填 | - | 配置匹配键值后的限流次数 |
|
||||
|
||||
`limit_keys`中每一项的配置字段说明
|
||||
|
||||
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
|
||||
| -------- | -------- | -------- | -------- | -------- |
|
||||
| key | string | 必填 | - | 匹配的键值 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
ARG BUILDER=higress-registry.cn-hangzhou.cr.aliyuncs.com/plugins/wasm-go-builder:go1.19-tinygo0.27.0-oras1.0.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
|
||||
|
||||
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
# - 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
|
||||
@@ -46,7 +46,7 @@
|
||||
# - 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
|
||||
@@ -55,46 +55,61 @@ 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 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'"; \
|
||||
echo "oras_version: '$oras_version'"; \
|
||||
case "$arch" in \
|
||||
'amd64') \
|
||||
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"; \
|
||||
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') \
|
||||
;; \
|
||||
'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"; \
|
||||
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 >&2 "error: unsupported architecture '$arch' "; exit 1 ;; \
|
||||
esac; \
|
||||
echo "go_url: '$go_url'"; \
|
||||
echo "tinygo_url: '$tinygo_url'"; \
|
||||
echo "oras_url: '$oras_url'"; \
|
||||
wget -O go.tgz "$go_url" --progress=dot:giga; \
|
||||
wget -O tinygo.deb "$tinygo_url" --progress=dot:giga; \
|
||||
wget -O oras.tgz "$oras_url" --progress=dot:giga; \
|
||||
echo "Download complete"; \
|
||||
rm -rf /usr/local/go && tar -C /usr/local -xzf go.tgz && rm -rf go.tgz; \
|
||||
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; \
|
||||
dpkg -i tinygo.deb && rm -rf tinygo.deb
|
||||
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
|
||||
|
||||
@@ -1,27 +1,40 @@
|
||||
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
|
||||
TINYGO_VERSION ?= 0.25.0
|
||||
ORAS_VERSION ?= 1.0.0
|
||||
BUILDER ?= ${REGISTRY}wasm-go-builder:go${GO_VERSION}-tinygo${TINYGO_VERSION}-oras${ORAS_VERSION}
|
||||
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) \
|
||||
--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:
|
||||
@@ -39,6 +52,8 @@ builder:
|
||||
--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 \
|
||||
|
||||
@@ -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
|
||||
```
|
||||
@@ -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
|
||||
```
|
||||
21
plugins/wasm-go/extensions/chatgpt-proxy/README.md
Normal file
21
plugins/wasm-go/extensions/chatgpt-proxy/README.md
Normal 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"
|
||||
```
|
||||
|
||||
|
||||
15
plugins/wasm-go/extensions/chatgpt-proxy/go.mod
Normal file
15
plugins/wasm-go/extensions/chatgpt-proxy/go.mod
Normal 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
|
||||
)
|
||||
22
plugins/wasm-go/extensions/chatgpt-proxy/go.sum
Normal file
22
plugins/wasm-go/extensions/chatgpt-proxy/go.sum
Normal 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=
|
||||
128
plugins/wasm-go/extensions/chatgpt-proxy/main.go
Normal file
128
plugins/wasm-go/extensions/chatgpt-proxy/main.go
Normal 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
|
||||
}
|
||||
BIN
plugins/wasm-go/extensions/chatgpt-proxy/main.wasm
Normal file
BIN
plugins/wasm-go/extensions/chatgpt-proxy/main.wasm
Normal file
Binary file not shown.
230
plugins/wasm-go/extensions/cors/README.md
Normal file
230
plugins/wasm-go/extensions/cors/README.md
Normal file
@@ -0,0 +1,230 @@
|
||||
# 功能说明
|
||||
|
||||
`cors` 插件可以为服务端启用 CORS(Cross-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,比如:GET,POST 等。可以使用 * 来表示允许所有 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
|
||||
1
plugins/wasm-go/extensions/cors/VERSION
Normal file
1
plugins/wasm-go/extensions/cors/VERSION
Normal file
@@ -0,0 +1 @@
|
||||
1.0.0
|
||||
457
plugins/wasm-go/extensions/cors/config/cors_config.go
Normal file
457
plugins/wasm-go/extensions/cors/config/cors_config.go
Normal 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
|
||||
}
|
||||
408
plugins/wasm-go/extensions/cors/config/cors_config_test.go
Normal file
408
plugins/wasm-go/extensions/cors/config/cors_config_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
67
plugins/wasm-go/extensions/cors/cors.yaml
Normal file
67
plugins/wasm-go/extensions/cors/cors.yaml
Normal 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
|
||||
78
plugins/wasm-go/extensions/cors/envoy.yaml
Normal file
78
plugins/wasm-go/extensions/cors/envoy.yaml
Normal 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
|
||||
21
plugins/wasm-go/extensions/cors/go.mod
Normal file
21
plugins/wasm-go/extensions/cors/go.mod
Normal 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
|
||||
)
|
||||
20
plugins/wasm-go/extensions/cors/go.sum
Normal file
20
plugins/wasm-go/extensions/cors/go.sum
Normal 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=
|
||||
169
plugins/wasm-go/extensions/cors/main.go
Normal file
169
plugins/wasm-go/extensions/cors/main.go
Normal 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
|
||||
}
|
||||
@@ -100,10 +100,6 @@ curl 'https://api.github.com/graphql' -X POST \
|
||||
| `gql` | graphql 查询 | 不能为空 |
|
||||
| `endpoint` | graphql 查询端点 | `/graphql` |
|
||||
| `timeout` | 查询连接超时,单位毫秒 | `5000` |
|
||||
| `serviceSource` | 服务来源:k8s, nacos,dns, ip | 不能为空 |
|
||||
| `serviceName` | 服务名称 | 不能为空 |
|
||||
| `servicePort` | 服务端口 | 不能为空 |
|
||||
| `namespace` | 服务命名空间, 当服务来源是nacos需要配置 | |
|
||||
| `domain` | 服务域名,当服务来源是dns配置 | |
|
||||
|
||||
### 插件使用
|
||||
@@ -158,9 +154,6 @@ spec:
|
||||
config:
|
||||
timeout: 5000
|
||||
endpoint: /graphql
|
||||
serviceSource: dns
|
||||
serviceName: github
|
||||
servicePort: 443
|
||||
domain: api.github.com
|
||||
gql: |
|
||||
query ($owner:String! $name:String!){
|
||||
|
||||
@@ -16,7 +16,6 @@ package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/alibaba/higress/plugins/wasm-go/pkg/wrapper"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
@@ -46,10 +45,10 @@ type Variable struct {
|
||||
}
|
||||
|
||||
type DeGraphQLConfig struct {
|
||||
client wrapper.HttpClient
|
||||
gql string
|
||||
endpoint string
|
||||
timeout uint32
|
||||
domain string
|
||||
variables []Variable
|
||||
}
|
||||
|
||||
@@ -63,6 +62,14 @@ func (d *DeGraphQLConfig) SetEndpoint(endpoint string) error {
|
||||
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
|
||||
}
|
||||
@@ -79,14 +86,6 @@ func (d *DeGraphQLConfig) SetTimeout(timeout uint32) {
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DeGraphQLConfig) SetClient(client wrapper.HttpClient) {
|
||||
d.client = client
|
||||
}
|
||||
|
||||
func (d *DeGraphQLConfig) GetClient() wrapper.HttpClient {
|
||||
return d.client
|
||||
}
|
||||
|
||||
func (d *DeGraphQLConfig) SetGql(gql string) error {
|
||||
if strings.TrimSpace(gql) == "" {
|
||||
return errors.New("gql can't be empty")
|
||||
|
||||
@@ -22,7 +22,7 @@ static_resources:
|
||||
- match:
|
||||
prefix: "/api"
|
||||
route:
|
||||
cluster: mock_service
|
||||
cluster: github
|
||||
http_filters:
|
||||
- name: envoy.filters.http.wasm
|
||||
typed_config:
|
||||
@@ -35,9 +35,6 @@ static_resources:
|
||||
value: |-
|
||||
{
|
||||
"gql": "query ($owner:String! $name:String!){\n repository(owner:$owner, name:$name) {\n name\n forkCount\n description\n}\n}",
|
||||
"serviceSource": "dns",
|
||||
"serviceName": "github",
|
||||
"servicePort": 443,
|
||||
"domain": "api.github.com",
|
||||
"endpoint": "/graphql",
|
||||
"timeout": 2000
|
||||
@@ -96,7 +93,7 @@ static_resources:
|
||||
socket_address:
|
||||
address: 127.0.0.1
|
||||
port_value: 8099
|
||||
- name: outbound|443||github.dns
|
||||
- name: github
|
||||
connect_timeout: 0.5s
|
||||
type: STRICT_DNS
|
||||
lb_policy: ROUND_ROBIN
|
||||
@@ -107,7 +104,7 @@ static_resources:
|
||||
typed_config:
|
||||
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
|
||||
load_assignment:
|
||||
cluster_name: outbound|443||github.dns
|
||||
cluster_name: github
|
||||
endpoints:
|
||||
- lb_endpoints:
|
||||
- endpoint:
|
||||
|
||||
@@ -2,6 +2,8 @@ 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
|
||||
|
||||
@@ -51,11 +51,9 @@ spec:
|
||||
description
|
||||
}
|
||||
}
|
||||
serviceName: github
|
||||
servicePort: 443
|
||||
serviceSource: dns
|
||||
timeout: 5000
|
||||
configDisable: false
|
||||
ingress:
|
||||
- github-api
|
||||
url: oci://docker.io/2456868764/de-graphql:1.0.0
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
@@ -42,7 +41,8 @@ func parseConfig(json gjson.Result, config *config.DeGraphQLConfig, log wrapper.
|
||||
gql := json.Get("gql").String()
|
||||
endpoint := json.Get("endpoint").String()
|
||||
timeout := json.Get("timeout").Int()
|
||||
log.Debugf("gql:%s endpoint:%s timeout:%d", gql, endpoint, timeout)
|
||||
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
|
||||
@@ -52,48 +52,7 @@ func parseConfig(json gjson.Result, config *config.DeGraphQLConfig, log wrapper.
|
||||
return err
|
||||
}
|
||||
config.SetTimeout(uint32(timeout))
|
||||
serviceSource := json.Get("serviceSource").String()
|
||||
serviceName := json.Get("serviceName").String()
|
||||
servicePort := json.Get("servicePort").Int()
|
||||
log.Debugf("serviceSource:%s serviceName:%s servicePort:%d", serviceSource, serviceName, servicePort)
|
||||
if serviceName == "" || servicePort == 0 {
|
||||
return errors.New("invalid service config")
|
||||
}
|
||||
switch serviceSource {
|
||||
case "k8s":
|
||||
namespace := json.Get("namespace").String()
|
||||
config.SetClient(wrapper.NewClusterClient(wrapper.K8sCluster{
|
||||
ServiceName: serviceName,
|
||||
Namespace: namespace,
|
||||
Port: servicePort,
|
||||
}))
|
||||
return nil
|
||||
case "nacos":
|
||||
namespace := json.Get("namespace").String()
|
||||
config.SetClient(wrapper.NewClusterClient(wrapper.NacosCluster{
|
||||
ServiceName: serviceName,
|
||||
NamespaceID: namespace,
|
||||
Port: servicePort,
|
||||
}))
|
||||
return nil
|
||||
case "ip":
|
||||
config.SetClient(wrapper.NewClusterClient(wrapper.StaticIpCluster{
|
||||
ServiceName: serviceName,
|
||||
Port: servicePort,
|
||||
}))
|
||||
return nil
|
||||
case "dns":
|
||||
domain := json.Get("domain").String()
|
||||
config.SetClient(wrapper.NewClusterClient(wrapper.DnsCluster{
|
||||
ServiceName: serviceName,
|
||||
Port: servicePort,
|
||||
Domain: domain,
|
||||
}))
|
||||
return nil
|
||||
default:
|
||||
return errors.New("unknown service source: " + serviceSource)
|
||||
}
|
||||
|
||||
config.SetDomain(domain)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -122,8 +81,9 @@ func onHttpRequestHeaders(ctx wrapper.HttpContext, config config.DeGraphQLConfig
|
||||
}
|
||||
// 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
|
||||
config.GetClient().Post(config.GetEndpoint(), headers, []byte(replaceBody),
|
||||
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)
|
||||
|
||||
3
plugins/wasm-go/extensions/waf/Dockerfile
Normal file
3
plugins/wasm-go/extensions/waf/Dockerfile
Normal file
@@ -0,0 +1,3 @@
|
||||
FROM scratch
|
||||
|
||||
COPY local/main.wasm /plugin.wasm
|
||||
52
plugins/wasm-go/extensions/waf/README.md
Normal file
52
plugins/wasm-go/extensions/waf/README.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# 功能说明
|
||||
waf插件实现了基于ModSecurity的规则防护引擎,可以根据用户配置的规则屏蔽可疑请求,并支持OWASP CRS,为站点提供基础的防护功能。
|
||||
|
||||
# 配置字段
|
||||
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
|
||||
| -------- | -------- | -------- | -------- | -------- |
|
||||
| useCRS | bool | 选填 | false | 是否开启OWASP CRS,详情可参考[coreruleset](https://github.com/coreruleset/coreruleset/tree/v3.3.2) |
|
||||
| secRules | array of string | 选填 | - | 用户自定义的waf防护规则,语法规则可参考[ModSecurity中文手册](http://www.modsecurity.cn/chm/) |
|
||||
|
||||
# 配置示例
|
||||
```yaml
|
||||
useCRS: true
|
||||
secRules:
|
||||
- "SecDebugLogLevel 3"
|
||||
- "SecRuleEngine On"
|
||||
- "SecAction \"id:100,phase:1,pass\""
|
||||
- "SecRule REQUEST_URI \"@streq /admin\" \"id:101,phase:1,t:lowercase,deny\""
|
||||
- "SecRule REQUEST_BODY \"@rx maliciouspayload\" \"id:102,phase:2,t:lowercase,deny\""
|
||||
```
|
||||
|
||||
根据该配置,以下请求将被禁止访问:
|
||||
```bash
|
||||
curl http://example.com/admin
|
||||
curl http://example.com -d "maliciouspayload"
|
||||
```
|
||||
|
||||
# 对特定路由或域名开启
|
||||
```yaml
|
||||
useCRS: true
|
||||
secRules:
|
||||
- "SecDebugLogLevel 3"
|
||||
- "SecRuleEngine On"
|
||||
- "SecAction \"id:100,phase:1,pass\""
|
||||
- "SecRule REQUEST_URI \"@streq /admin\" \"id:101,phase:1,t:lowercase,deny\""
|
||||
- "SecRule REQUEST_BODY \"@rx maliciouspayload\" \"id:102,phase:2,t:lowercase,deny\""
|
||||
_rules_:
|
||||
- _match_route_:
|
||||
- "route-1"
|
||||
secRules:
|
||||
- "SecDebugLogLevel 3"
|
||||
- "SecRuleEngine On"
|
||||
- "SecAction \"id:102,phase:1,deny\""
|
||||
- _match_domain_:
|
||||
- "*.example.com"
|
||||
- test.com
|
||||
secRules:
|
||||
- "SecDebugLogLevel 3"
|
||||
- "SecRuleEngine On"
|
||||
- "SecAction \"id:102,phase:1,pass\""
|
||||
```
|
||||
|
||||
此例 `_match_route_` 中指定的 `route-1` 即在创建网关路由时填写的路由名称,当匹配到这两个路由时,将使用此段配置; 此例 `_match_domain_` 中指定的 `*.example.com` 和 `test.com` 用于匹配请求的域名,当发现域名匹配时,将使用此段配置; 配置的匹配生效顺序,将按照 `_rules_` 下规则的排列顺序,匹配第一个规则后生效对应配置,后续规则将被忽略。
|
||||
37
plugins/wasm-go/extensions/waf/go.mod
Normal file
37
plugins/wasm-go/extensions/waf/go.mod
Normal file
@@ -0,0 +1,37 @@
|
||||
module github.com/corazawaf/coraza-proxy-wasm
|
||||
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/alibaba/higress/plugins/wasm-go v0.0.0-20230504075705-7e358eb1db7c
|
||||
github.com/corazawaf/coraza-wasilibs v0.0.0-20230408002644-e2e3af21f503
|
||||
github.com/corazawaf/coraza/v3 v3.0.0-rc.1.0.20230407165813-a18681b1ec28
|
||||
github.com/stretchr/testify v1.8.0
|
||||
github.com/tetratelabs/proxy-wasm-go-sdk v0.22.0
|
||||
github.com/tidwall/gjson v1.14.4
|
||||
github.com/wasilibs/nottinygc v0.2.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/corazawaf/libinjection-go v0.1.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
github.com/magefile/mage v1.14.0 // indirect
|
||||
github.com/petar-dambovaliev/aho-corasick v0.0.0-20211021192214-5ab2d9280aa9 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/tetratelabs/wazero v1.0.1 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/wasilibs/go-aho-corasick v0.3.0 // indirect
|
||||
github.com/wasilibs/go-libinjection v0.2.1 // indirect
|
||||
github.com/wasilibs/go-re2 v1.0.0 // indirect
|
||||
golang.org/x/net v0.9.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
rsc.io/binaryregexp v0.2.0 // indirect
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/alibaba/higress/plugins/wasm-go v0.0.0-20230504075705-7e358eb1db7c => github.com/rinfx/higress/plugins/wasm-go v0.0.0-20230508112120-f2d89b0606ee
|
||||
)
|
||||
65
plugins/wasm-go/extensions/waf/go.sum
Normal file
65
plugins/wasm-go/extensions/waf/go.sum
Normal file
@@ -0,0 +1,65 @@
|
||||
github.com/alibaba/higress/plugins/wasm-go v0.0.0-20230504075705-7e358eb1db7c h1:RKZJGYAkczZVqvJ/CuNJSY6YI5oyJjzm12MVzf3Z7/U=
|
||||
github.com/alibaba/higress/plugins/wasm-go v0.0.0-20230504075705-7e358eb1db7c/go.mod h1:AzSnkuon5c26nIePTiJQIAFsKdhkNdncLcTuahpGtQs=
|
||||
github.com/corazawaf/coraza-wasilibs v0.0.0-20230408002644-e2e3af21f503 h1:hGXspDwUBHQUne1NT2D6PmkR9wFCXsibjaJpz7xhf+g=
|
||||
github.com/corazawaf/coraza-wasilibs v0.0.0-20230408002644-e2e3af21f503/go.mod h1:bTc+NV7T2wQevFQHDDWhD/+IAA5bvKbbK4CxzfvJx/o=
|
||||
github.com/corazawaf/coraza/v3 v3.0.0-rc.1.0.20230407165813-a18681b1ec28 h1:Jrlvhe4YCR/PMCazDEBeun/XTYhlzczBN0WN4/ejORo=
|
||||
github.com/corazawaf/coraza/v3 v3.0.0-rc.1.0.20230407165813-a18681b1ec28/go.mod h1:TKREBLh55w3SiBbLsQpH9EFzjBAmEUH4KRaZ/kFYz20=
|
||||
github.com/corazawaf/libinjection-go v0.1.2 h1:oeiV9pc5rvJ+2oqOqXEAMJousPpGiup6f7Y3nZj5GoM=
|
||||
github.com/corazawaf/libinjection-go v0.1.2/go.mod h1:OP4TM7xdJ2skyXqNX1AN1wN5nNZEmJNuWbNPOItn7aw=
|
||||
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/foxcpp/go-mockdns v1.0.0 h1:7jBqxd3WDWwi/6WhDvacvH1XsN3rOLXyHM1uhvIx6FI=
|
||||
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/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo=
|
||||
github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
||||
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
|
||||
github.com/petar-dambovaliev/aho-corasick v0.0.0-20211021192214-5ab2d9280aa9 h1:lL+y4Xv20pVlCGyLzNHRC0I0rIHhIL1lTvHizoS/dU8=
|
||||
github.com/petar-dambovaliev/aho-corasick v0.0.0-20211021192214-5ab2d9280aa9/go.mod h1:EHPiTAKtiFmrMldLUNswFwfZ2eJIYBHktdaUTZxYWRw=
|
||||
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/rinfx/higress/plugins/wasm-go v0.0.0-20230508112120-f2d89b0606ee h1:5qHbEcei04hDcUTzvoFNtYtZiewnTgVv6wR9co8Ih6c=
|
||||
github.com/rinfx/higress/plugins/wasm-go v0.0.0-20230508112120-f2d89b0606ee/go.mod h1:AzSnkuon5c26nIePTiJQIAFsKdhkNdncLcTuahpGtQs=
|
||||
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.1 h1:xyWBoGyMjYekG3mEQ/W7xm9E05S89kJ/at696d/9yuc=
|
||||
github.com/tetratelabs/wazero v1.0.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/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/wasilibs/go-aho-corasick v0.3.0 h1:ScfPQhAwop/ELIkwY0dfMTFb/bwOdYI/MB3mkX2WOZI=
|
||||
github.com/wasilibs/go-aho-corasick v0.3.0/go.mod h1:LKW6EW9NWuWYE8PII+sFpRbbY3UcrMUgfUTkGaoWyMY=
|
||||
github.com/wasilibs/go-libinjection v0.2.1 h1:1aSwyE4oNpPGpFw3i3hoM15sF3qn1s4P0jC2jgFM2Qk=
|
||||
github.com/wasilibs/go-libinjection v0.2.1/go.mod h1:ZUoVe+HLQYq+QPBNTSgg3fxGvZsvXiDbi0UomBlsGzo=
|
||||
github.com/wasilibs/go-re2 v1.0.0 h1:pvrqtMzZgTMHVPfXJrk4YZwiqIXOKdfo5aed6CzUAW4=
|
||||
github.com/wasilibs/go-re2 v1.0.0/go.mod h1:8g69JapfgjSCx49dKOQij1dqA3sOvoH5NteaUy1X0SA=
|
||||
github.com/wasilibs/nottinygc v0.2.0 h1:cXz2Ac9bVMLkpuOlUlPQMWowjw0K2cOErXZOFdAj7yE=
|
||||
github.com/wasilibs/nottinygc v0.2.0/go.mod h1:oDcIotskuYNMpqMF23l7Z8uzD4TC0WXHK8jetlB3HIo=
|
||||
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
|
||||
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
||||
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/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=
|
||||
rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
14
plugins/wasm-go/extensions/waf/init_tinygo.go
Normal file
14
plugins/wasm-go/extensions/waf/init_tinygo.go
Normal file
@@ -0,0 +1,14 @@
|
||||
//go:build tinygo
|
||||
|
||||
package main
|
||||
|
||||
import _ "github.com/wasilibs/nottinygc"
|
||||
|
||||
// Compiled by nottinygc for delayed free but Envoy doesn't stub it yet,
|
||||
// luckily nottinygc doesn't actually call the function, so it's fine to
|
||||
// stub it out.
|
||||
|
||||
//export sched_yield
|
||||
func sched_yield() int32 {
|
||||
return 0
|
||||
}
|
||||
3
plugins/wasm-go/extensions/waf/local/Dockerfile
Normal file
3
plugins/wasm-go/extensions/waf/local/Dockerfile
Normal file
@@ -0,0 +1,3 @@
|
||||
FROM liuxr25/flask-helloworld:latest
|
||||
|
||||
COPY app.py /work/app.py
|
||||
14
plugins/wasm-go/extensions/waf/local/app.py
Normal file
14
plugins/wasm-go/extensions/waf/local/app.py
Normal file
@@ -0,0 +1,14 @@
|
||||
from flask import Flask, request
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route("/flask/test1", methods=["GET", "POST"])
|
||||
def test1():
|
||||
return "body normal", 200, [("test-header", "hahaha")]
|
||||
|
||||
@app.route("/flask/test2", methods=["GET", "POST"])
|
||||
def test2():
|
||||
return "body attack", 200, []
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run("0.0.0.0", 5000)
|
||||
96
plugins/wasm-go/extensions/waf/local/docker-compose.yaml
Normal file
96
plugins/wasm-go/extensions/waf/local/docker-compose.yaml
Normal file
@@ -0,0 +1,96 @@
|
||||
services:
|
||||
httpbin:
|
||||
image: kennethreitz/httpbin
|
||||
environment:
|
||||
- MAX_BODY_SIZE=15728640 # 15 MiB
|
||||
ports:
|
||||
- 8083:8080
|
||||
command:
|
||||
- "gunicorn"
|
||||
- "-b"
|
||||
- "0.0.0.0:8080"
|
||||
- "httpbin:app"
|
||||
- "-k"
|
||||
- "gevent"
|
||||
- --log-file
|
||||
- /home/envoy/logs/httpbin.log
|
||||
volumes:
|
||||
- logs:/home/envoy/logs:rw
|
||||
|
||||
flask:
|
||||
# image: liuxr25/flask-helloworld:latest
|
||||
build: .
|
||||
environment:
|
||||
- MAX_BODY_SIZE=15728640 # 15 MiB
|
||||
ports:
|
||||
- 8084:5000
|
||||
|
||||
chown:
|
||||
image: alpine:3.16
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- chown -R 101:101 /home/envoy/logs
|
||||
volumes:
|
||||
- logs:/home/envoy/logs:rw
|
||||
|
||||
envoy:
|
||||
depends_on:
|
||||
- chown
|
||||
- httpbin
|
||||
- flask
|
||||
image: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/envoy:1.20
|
||||
command:
|
||||
- -c
|
||||
- /conf/envoy-config.yaml
|
||||
- --log-level
|
||||
- info
|
||||
- --component-log-level
|
||||
- wasm:debug
|
||||
- --log-format [%Y-%m-%d %T.%f][%t][%l][%n] [%g:%#] %v
|
||||
- --log-path
|
||||
- /home/envoy/logs/envoy.log
|
||||
volumes:
|
||||
- .:/build
|
||||
- .:/conf
|
||||
- logs:/home/envoy/logs:rw
|
||||
ports:
|
||||
- 8080:8080
|
||||
- 8082:8082
|
||||
|
||||
# envoy-logs:
|
||||
# depends_on:
|
||||
# - envoy
|
||||
# - wasm-logs
|
||||
# image: debian:11-slim
|
||||
# entrypoint: bash
|
||||
# command:
|
||||
# - -c
|
||||
# - tail -c +0 -f /home/envoy/logs/envoy.log
|
||||
# volumes:
|
||||
# - logs:/home/envoy/logs:ro
|
||||
|
||||
wasm-logs:
|
||||
depends_on:
|
||||
- envoy
|
||||
image: debian:11-slim
|
||||
entrypoint: bash
|
||||
command:
|
||||
- -c
|
||||
- tail -c +0 -f /home/envoy/logs/envoy.log | grep --line-buffered "[critical][wasm]"
|
||||
volumes:
|
||||
- logs:/home/envoy/logs:ro
|
||||
|
||||
# debug-logs:
|
||||
# depends_on:
|
||||
# - envoy
|
||||
# image: debian:11-slim
|
||||
# entrypoint: bash
|
||||
# command:
|
||||
# - -c
|
||||
# - tail -c +0 -f /home/envoy/logs/envoy.log | grep --line-buffered "unreachable"
|
||||
# volumes:
|
||||
# - logs:/home/envoy/logs:ro
|
||||
|
||||
volumes:
|
||||
logs:
|
||||
143
plugins/wasm-go/extensions/waf/local/envoy-config.yaml
Normal file
143
plugins/wasm-go/extensions/waf/local/envoy-config.yaml
Normal file
@@ -0,0 +1,143 @@
|
||||
stats_config:
|
||||
stats_tags:
|
||||
# Envoy extracts the first matching group as a value.
|
||||
# See https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/metrics/v3/stats.proto#config-metrics-v3-statsconfig.
|
||||
- tag_name: phase
|
||||
regex: "(_phase=([a-z_]+))"
|
||||
- tag_name: rule_id
|
||||
regex: "(_ruleid=([0-9]+))"
|
||||
|
||||
static_resources:
|
||||
listeners:
|
||||
- address:
|
||||
socket_address:
|
||||
address: 0.0.0.0
|
||||
port_value: 8080
|
||||
filter_chains:
|
||||
- filters:
|
||||
- name: envoy.filters.network.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:
|
||||
virtual_hosts:
|
||||
- name: local_route
|
||||
domains:
|
||||
- "*"
|
||||
routes:
|
||||
- name: "route_1"
|
||||
match:
|
||||
path: "/headers"
|
||||
route:
|
||||
cluster: httpbin_server
|
||||
- name: "route_2"
|
||||
match:
|
||||
path: "/user-agent"
|
||||
route:
|
||||
cluster: httpbin_server
|
||||
- name: "route_flask"
|
||||
match:
|
||||
prefix: "/flask"
|
||||
route:
|
||||
cluster: flask_server
|
||||
- name: "route_httpbin"
|
||||
match:
|
||||
prefix: "/"
|
||||
route:
|
||||
cluster: httpbin_server
|
||||
# - name: "route_mock"
|
||||
# match:
|
||||
# prefix: "/"
|
||||
# direct_response:
|
||||
# status: 200
|
||||
# body:
|
||||
# inline_string: "mock response\n"
|
||||
http_filters:
|
||||
- name: envoy.filters.http.wasm
|
||||
typed_config:
|
||||
"@type": type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm
|
||||
config:
|
||||
name: "coraza-filter"
|
||||
root_id: ""
|
||||
configuration:
|
||||
"@type": "type.googleapis.com/google.protobuf.StringValue"
|
||||
value: |
|
||||
{
|
||||
"useCRS": true,
|
||||
"secRules": [
|
||||
"SecDebugLogLevel 3",
|
||||
"SecRuleEngine On",
|
||||
"SecRule REQUEST_URI \"@streq /admin\" \"id:101,phase:1,t:lowercase,deny\"",
|
||||
"SecRule REQUEST_BODY \"@rx maliciouspayload\" \"id:102,phase:2,t:lowercase,deny\"",
|
||||
"SecRule RESPONSE_HEADERS::status \"@rx 406\" \"id:103,phase:3,t:lowercase,deny\"",
|
||||
"SecRule RESPONSE_HEADERS:test-header \"@streq hahaha\" \"id:104,phase:3,t:lowercase,deny\"",
|
||||
"SecRule RESPONSE_BODY \"@rx attack\" \"id:105,phase:4,t:lowercase,deny\""
|
||||
],
|
||||
"_rules_": [
|
||||
{
|
||||
"_match_route_": [
|
||||
"route_1"
|
||||
],
|
||||
"secRules": [
|
||||
"SecDebugLogLevel 3",
|
||||
"SecRuleEngine On",
|
||||
"SecAction \"id:102,phase:1,deny\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"_match_route_": [
|
||||
"route_2"
|
||||
],
|
||||
"secRules": [
|
||||
"SecDebugLogLevel 3",
|
||||
"SecRuleEngine On",
|
||||
"SecAction \"id:102,phase:1,pass\""
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
vm_config:
|
||||
runtime: "envoy.wasm.runtime.v8"
|
||||
vm_id: "10086"
|
||||
code:
|
||||
local:
|
||||
filename: "build/main.wasm"
|
||||
- name: envoy.filters.http.router
|
||||
typed_config:
|
||||
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
|
||||
|
||||
clusters:
|
||||
- name: httpbin_server
|
||||
connect_timeout: 6000s
|
||||
type: STRICT_DNS
|
||||
lb_policy: ROUND_ROBIN
|
||||
load_assignment:
|
||||
cluster_name: httpbin_server
|
||||
endpoints:
|
||||
- lb_endpoints:
|
||||
- endpoint:
|
||||
address:
|
||||
socket_address:
|
||||
address: httpbin
|
||||
port_value: 8080
|
||||
- name: flask_server
|
||||
connect_timeout: 6000s
|
||||
type: STRICT_DNS
|
||||
lb_policy: ROUND_ROBIN
|
||||
load_assignment:
|
||||
cluster_name: flask_server
|
||||
endpoints:
|
||||
- lb_endpoints:
|
||||
- endpoint:
|
||||
address:
|
||||
socket_address:
|
||||
address: flask
|
||||
port_value: 5000
|
||||
|
||||
admin:
|
||||
access_log_path: "/dev/null"
|
||||
address:
|
||||
socket_address:
|
||||
address: 0.0.0.0
|
||||
port_value: 8082
|
||||
16
plugins/wasm-go/extensions/waf/mage.go
Normal file
16
plugins/wasm-go/extensions/waf/mage.go
Normal file
@@ -0,0 +1,16 @@
|
||||
//go:build ignore
|
||||
// +build ignore
|
||||
|
||||
// Entrypoint to mage for running without needing to install the command.
|
||||
// https://magefile.org/zeroinstall/
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/magefile/mage/mage"
|
||||
)
|
||||
|
||||
func main() {
|
||||
os.Exit(mage.Main())
|
||||
}
|
||||
16
plugins/wasm-go/extensions/waf/magefiles/go.mod
Normal file
16
plugins/wasm-go/extensions/waf/magefiles/go.mod
Normal file
@@ -0,0 +1,16 @@
|
||||
module github.com/corazawaf/coraza-proxy-wasm/magefiles
|
||||
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
fortio.org/fortio v1.38.4
|
||||
github.com/magefile/mage v1.14.0
|
||||
github.com/tetratelabs/wabin v0.0.0-20220927005300-3b0fbf39a46a
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20221111204811-129d8d6c17ab // indirect
|
||||
golang.org/x/net v0.2.0 // indirect
|
||||
golang.org/x/text v0.4.0 // indirect
|
||||
)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user