mirror of
https://github.com/alibaba/higress.git
synced 2026-03-18 01:07:29 +08:00
Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c415c60c3 | ||
|
|
59acb61926 | ||
|
|
29079f4e2a | ||
|
|
95edce024d | ||
|
|
b6d07a157c | ||
|
|
10569f49ae | ||
|
|
2a588c99c7 | ||
|
|
0cfef34bff | ||
|
|
5c2b5d5750 | ||
|
|
8f483518a9 | ||
|
|
f6ee4ed166 | ||
|
|
9a9e924037 | ||
|
|
e7d66f691f | ||
|
|
8c48fcb423 | ||
|
|
ef31e09310 | ||
|
|
c0f2cafdc8 | ||
|
|
d5a9ff3a98 | ||
|
|
f069ad5b0d | ||
|
|
85219b6c53 | ||
|
|
5041277be3 | ||
|
|
c00c8827f9 | ||
|
|
46218058d1 | ||
|
|
5306385e6b | ||
|
|
4e881fdd3f | ||
|
|
59aa3b5488 | ||
|
|
c40cf85aad | ||
|
|
7c749b864c | ||
|
|
74ddbf02f6 | ||
|
|
60c56a16ab | ||
|
|
5a2c6835f7 | ||
|
|
12a5612450 | ||
|
|
b9f5c4d1f2 | ||
|
|
d7bdcbd026 | ||
|
|
dd284d1f24 | ||
|
|
a7ee523c98 | ||
|
|
4bbfb131ee | ||
|
|
6fd71f9749 | ||
|
|
e0159f501a | ||
|
|
56226d5052 | ||
|
|
086a9cc973 | ||
|
|
e389313aa3 | ||
|
|
f64c601264 | ||
|
|
9c6ea109f8 | ||
|
|
4ca2d23404 | ||
|
|
0ce52de59b | ||
|
|
81e459da01 |
114
.github/workflows/build-and-push-wasm-plugin-image.yaml
vendored
Normal file
114
.github/workflows/build-and-push-wasm-plugin-image.yaml
vendored
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
name: Build and Push Wasm Plugin Image
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "wasm-go-*-v*.*.*" # 匹配 wasm-go-{pluginName}-vX.Y.Z 格式的标签
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
plugin_name:
|
||||||
|
description: 'Name of the plugin'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
version:
|
||||||
|
description: 'Version of the plugin (optional, without leading v)'
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-push-wasm-plugin-image:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
environment:
|
||||||
|
name: image-registry-msg
|
||||||
|
env:
|
||||||
|
IMAGE_REGISTRY_SERVICE: ${{ vars.IMAGE_REGISTRY || 'higress-registry.cn-hangzhou.cr.aliyuncs.com' }}
|
||||||
|
IMAGE_REPOSITORY: ${{ vars.PLUGIN_IMAGE_REPOSITORY || 'plugins' }}
|
||||||
|
GO_VERSION: 1.19
|
||||||
|
TINYGO_VERSION: 0.28.1
|
||||||
|
ORAS_VERSION: 1.0.0
|
||||||
|
steps:
|
||||||
|
- name: Set plugin_name and version from inputs or ref_name
|
||||||
|
id: set_vars
|
||||||
|
run: |
|
||||||
|
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
||||||
|
plugin_name="${{ github.event.inputs.plugin_name }}"
|
||||||
|
version="${{ github.event.inputs.version }}"
|
||||||
|
else
|
||||||
|
ref_name=${{ github.ref_name }}
|
||||||
|
plugin_name=${ref_name#*-*-} # 删除插件名前面的字段(wasm-go-)
|
||||||
|
plugin_name=${plugin_name%-*} # 删除插件名后面的字段(-vX.Y.Z)
|
||||||
|
version=$(echo "$ref_name" | awk -F'v' '{print $2}')
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "PLUGIN_NAME=$plugin_name" >> $GITHUB_ENV
|
||||||
|
echo "VERSION=$version" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: File Check
|
||||||
|
run: |
|
||||||
|
workspace=${{ github.workspace }}/plugins/wasm-go/extensions/${PLUGIN_NAME}
|
||||||
|
push_command="./plugin.tar.gz:application/vnd.oci.image.layer.v1.tar+gzip"
|
||||||
|
|
||||||
|
# 查找spec.yaml
|
||||||
|
if [ -f "${workspace}/spec.yaml" ]; then
|
||||||
|
echo "spec.yaml exists"
|
||||||
|
push_command="./spec.yaml:application/vnd.module.wasm.spec.v1+yaml $push_command "
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 查找README.md
|
||||||
|
if [ -f "${workspace}/README.md" ];then
|
||||||
|
echo "README.md exists"
|
||||||
|
push_command="./README.md:application/vnd.module.wasm.doc.v1+markdown $push_command "
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 查找README_{lang}.md
|
||||||
|
for file in ${workspace}/README_*.md; do
|
||||||
|
if [ -f "$file" ]; then
|
||||||
|
file_name=$(basename $file)
|
||||||
|
echo "$file_name exists"
|
||||||
|
lang=$(basename $file | sed 's/README_//; s/.md//')
|
||||||
|
push_command="./$file_name:application/vnd.module.wasm.doc.v1.$lang+markdown $push_command "
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "PUSH_COMMAND=\"$push_command\"" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Run a wasm-go-builder
|
||||||
|
env:
|
||||||
|
PLUGIN_NAME: ${{ env.PLUGIN_NAME }}
|
||||||
|
BUILDER_IMAGE: higress-registry.cn-hangzhou.cr.aliyuncs.com/plugins/wasm-go-builder:go${{ env.GO_VERSION }}-tinygo${{ env.TINYGO_VERSION }}-oras${{ env.ORAS_VERSION }}
|
||||||
|
run: |
|
||||||
|
docker run -itd --name builder -v ${{ github.workspace }}:/workspace -e PLUGIN_NAME=${{ env.PLUGIN_NAME }} --rm ${{ env.BUILDER_IMAGE }} /bin/bash
|
||||||
|
|
||||||
|
- name: Build Image and Push
|
||||||
|
run: |
|
||||||
|
push_command=${{ env.PUSH_COMMAND }}
|
||||||
|
push_command=${push_command#\"}
|
||||||
|
push_command=${push_command%\"} # 删除PUSH_COMMAND中的双引号,确保oras push正常解析
|
||||||
|
|
||||||
|
target_image="${{ env.IMAGE_REGISTRY_SERVICE }}/${{ env.IMAGE_REPOSITORY}}/${{ env.PLUGIN_NAME }}:${{ env.VERSION }}"
|
||||||
|
echo "TargetImage=${target_image}"
|
||||||
|
|
||||||
|
cd ${{ github.workspace }}/plugins/wasm-go/extensions/${PLUGIN_NAME}
|
||||||
|
if [ -f ./.buildrc ]; then
|
||||||
|
echo 'Found .buildrc file, sourcing it...'
|
||||||
|
. ./.buildrc
|
||||||
|
else
|
||||||
|
echo '.buildrc file not found'
|
||||||
|
fi
|
||||||
|
echo "EXTRA_TAGS=${EXTRA_TAGS}"
|
||||||
|
|
||||||
|
command="
|
||||||
|
set -e
|
||||||
|
cd /workspace/plugins/wasm-go/extensions/${PLUGIN_NAME}
|
||||||
|
go mod tidy
|
||||||
|
tinygo build -o ./plugin.wasm -scheduler=none -target=wasi -gc=custom -tags=\"custommalloc nottinygc_finalizer ${EXTRA_TAGS}\" .
|
||||||
|
tar czvf plugin.tar.gz plugin.wasm
|
||||||
|
echo ${{ secrets.REGISTRY_PASSWORD }} | oras login -u ${{ secrets.REGISTRY_USERNAME }} --password-stdin ${{ env.IMAGE_REGISTRY_SERVICE }}
|
||||||
|
oras push ${target_image} ${push_command}
|
||||||
|
"
|
||||||
|
docker exec builder bash -c "$command"
|
||||||
|
|
||||||
|
|
||||||
16
.github/workflows/build-and-test-plugin.yaml
vendored
16
.github/workflows/build-and-test-plugin.yaml
vendored
@@ -17,8 +17,8 @@ jobs:
|
|||||||
lint:
|
lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-go@v3
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: 1.19
|
go-version: 1.19
|
||||||
# There are too many lint errors in current code bases
|
# There are too many lint errors in current code bases
|
||||||
@@ -30,9 +30,9 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
# TODO(Xunzhuo): Enable C WASM Filters in CI
|
# TODO(Xunzhuo): Enable C WASM Filters in CI
|
||||||
wasmPluginType: [ GO ]
|
wasmPluginType: [ GO, RUST ]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Free Up GitHub Actions Ubuntu Runner Disk Space 🔧
|
- name: Free Up GitHub Actions Ubuntu Runner Disk Space 🔧
|
||||||
uses: jlumbroso/free-disk-space@main
|
uses: jlumbroso/free-disk-space@main
|
||||||
@@ -45,12 +45,12 @@ jobs:
|
|||||||
swap-storage: true
|
swap-storage: true
|
||||||
|
|
||||||
- name: "Setup Go"
|
- name: "Setup Go"
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: 1.19
|
go-version: 1.19
|
||||||
|
|
||||||
- name: Setup Golang Caches
|
- name: Setup Golang Caches
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |-
|
path: |-
|
||||||
~/.cache/go-build
|
~/.cache/go-build
|
||||||
@@ -60,7 +60,7 @@ jobs:
|
|||||||
${{ runner.os }}-go
|
${{ runner.os }}-go
|
||||||
|
|
||||||
- name: Setup Submodule Caches
|
- name: Setup Submodule Caches
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |-
|
path: |-
|
||||||
.git/modules
|
.git/modules
|
||||||
@@ -81,4 +81,4 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [ higress-wasmplugin-test ]
|
needs: [ higress-wasmplugin-test ]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|||||||
30
.github/workflows/build-and-test.yaml
vendored
30
.github/workflows/build-and-test.yaml
vendored
@@ -10,8 +10,8 @@ jobs:
|
|||||||
lint:
|
lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-go@v3
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: 1.19
|
go-version: 1.19
|
||||||
# There are too many lint errors in current code bases
|
# There are too many lint errors in current code bases
|
||||||
@@ -21,10 +21,10 @@ jobs:
|
|||||||
coverage-test:
|
coverage-test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup Golang Caches
|
- name: Setup Golang Caches
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |-
|
path: |-
|
||||||
~/.cache/go-build
|
~/.cache/go-build
|
||||||
@@ -33,7 +33,7 @@ jobs:
|
|||||||
restore-keys: ${{ runner.os }}-go
|
restore-keys: ${{ runner.os }}-go
|
||||||
|
|
||||||
- name: Setup Submodule Caches
|
- name: Setup Submodule Caches
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |-
|
path: |-
|
||||||
.git/modules
|
.git/modules
|
||||||
@@ -46,7 +46,7 @@ jobs:
|
|||||||
- name: Run Coverage Tests
|
- name: Run Coverage Tests
|
||||||
run: GOPROXY="https://proxy.golang.org,direct" make go.test.coverage
|
run: GOPROXY="https://proxy.golang.org,direct" make go.test.coverage
|
||||||
- name: Upload coverage to Codecov
|
- name: Upload coverage to Codecov
|
||||||
uses: codecov/codecov-action@v3
|
uses: codecov/codecov-action@v4
|
||||||
with:
|
with:
|
||||||
fail_ci_if_error: false
|
fail_ci_if_error: false
|
||||||
files: ./coverage.xml
|
files: ./coverage.xml
|
||||||
@@ -58,17 +58,17 @@ jobs:
|
|||||||
needs: [lint,coverage-test]
|
needs: [lint,coverage-test]
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout ${{ github.ref }}"
|
- name: "Checkout ${{ github.ref }}"
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
|
|
||||||
- name: "Setup Go"
|
- name: "Setup Go"
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: 1.19
|
go-version: 1.19
|
||||||
|
|
||||||
- name: Setup Golang Caches
|
- name: Setup Golang Caches
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |-
|
path: |-
|
||||||
~/.cache/go-build
|
~/.cache/go-build
|
||||||
@@ -77,7 +77,7 @@ jobs:
|
|||||||
restore-keys: ${{ runner.os }}-go
|
restore-keys: ${{ runner.os }}-go
|
||||||
|
|
||||||
- name: Setup Submodule Caches
|
- name: Setup Submodule Caches
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |-
|
path: |-
|
||||||
.git/modules
|
.git/modules
|
||||||
@@ -90,7 +90,7 @@ jobs:
|
|||||||
run: GOPROXY="https://proxy.golang.org,direct" make build
|
run: GOPROXY="https://proxy.golang.org,direct" make build
|
||||||
|
|
||||||
- name: Upload Higress Binary
|
- name: Upload Higress Binary
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: higress
|
name: higress
|
||||||
path: out/
|
path: out/
|
||||||
@@ -108,12 +108,12 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: "Setup Go"
|
- name: "Setup Go"
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: 1.19
|
go-version: 1.19
|
||||||
|
|
||||||
- name: Setup Golang Caches
|
- name: Setup Golang Caches
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |-
|
path: |-
|
||||||
~/.cache/go-build
|
~/.cache/go-build
|
||||||
@@ -123,7 +123,7 @@ jobs:
|
|||||||
${{ runner.os }}-go
|
${{ runner.os }}-go
|
||||||
|
|
||||||
- name: Setup Submodule Caches
|
- name: Setup Submodule Caches
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |-
|
path: |-
|
||||||
.git/modules
|
.git/modules
|
||||||
@@ -139,4 +139,4 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [higress-conformance-test,gateway-conformance-test]
|
needs: [higress-conformance-test,gateway-conformance-test]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|||||||
36
.github/workflows/build-image-and-push.yaml
vendored
36
.github/workflows/build-image-and-push.yaml
vendored
@@ -16,7 +16,7 @@ jobs:
|
|||||||
CONTROLLER_IMAGE_NAME: ${{ vars.CONTROLLER_IMAGE_NAME || 'higress/higress' }}
|
CONTROLLER_IMAGE_NAME: ${{ vars.CONTROLLER_IMAGE_NAME || 'higress/higress' }}
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout ${{ github.ref }}"
|
- name: "Checkout ${{ github.ref }}"
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
|
|
||||||
@@ -31,12 +31,12 @@ jobs:
|
|||||||
swap-storage: true
|
swap-storage: true
|
||||||
|
|
||||||
- name: "Setup Go"
|
- name: "Setup Go"
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: 1.19
|
go-version: 1.19
|
||||||
|
|
||||||
- name: Setup Golang Caches
|
- name: Setup Golang Caches
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |-
|
path: |-
|
||||||
~/.cache/go-build
|
~/.cache/go-build
|
||||||
@@ -45,7 +45,7 @@ jobs:
|
|||||||
restore-keys: ${{ runner.os }}-go
|
restore-keys: ${{ runner.os }}-go
|
||||||
|
|
||||||
- name: Setup Submodule Caches
|
- name: Setup Submodule Caches
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |-
|
path: |-
|
||||||
envoy
|
envoy
|
||||||
@@ -56,7 +56,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Calculate Docker metadata
|
- name: Calculate Docker metadata
|
||||||
id: docker-meta
|
id: docker-meta
|
||||||
uses: docker/metadata-action@v4
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
${{ env.CONTROLLER_IMAGE_REGISTRY }}/${{ env.CONTROLLER_IMAGE_NAME }}
|
${{ env.CONTROLLER_IMAGE_REGISTRY }}/${{ env.CONTROLLER_IMAGE_NAME }}
|
||||||
@@ -67,7 +67,7 @@ jobs:
|
|||||||
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
|
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
|
||||||
|
|
||||||
- name: Login to Docker Registry
|
- name: Login to Docker Registry
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.CONTROLLER_IMAGE_REGISTRY }}
|
registry: ${{ env.CONTROLLER_IMAGE_REGISTRY }}
|
||||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||||
@@ -92,7 +92,7 @@ jobs:
|
|||||||
PILOT_IMAGE_NAME: ${{ vars.PILOT_IMAGE_NAME || 'higress/pilot' }}
|
PILOT_IMAGE_NAME: ${{ vars.PILOT_IMAGE_NAME || 'higress/pilot' }}
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout ${{ github.ref }}"
|
- name: "Checkout ${{ github.ref }}"
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
|
|
||||||
@@ -107,12 +107,12 @@ jobs:
|
|||||||
swap-storage: true
|
swap-storage: true
|
||||||
|
|
||||||
- name: "Setup Go"
|
- name: "Setup Go"
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: 1.19
|
go-version: 1.19
|
||||||
|
|
||||||
- name: Setup Golang Caches
|
- name: Setup Golang Caches
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |-
|
path: |-
|
||||||
~/.cache/go-build
|
~/.cache/go-build
|
||||||
@@ -121,7 +121,7 @@ jobs:
|
|||||||
restore-keys: ${{ runner.os }}-go
|
restore-keys: ${{ runner.os }}-go
|
||||||
|
|
||||||
- name: Setup Submodule Caches
|
- name: Setup Submodule Caches
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |-
|
path: |-
|
||||||
envoy
|
envoy
|
||||||
@@ -132,7 +132,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Calculate Docker metadata
|
- name: Calculate Docker metadata
|
||||||
id: docker-meta
|
id: docker-meta
|
||||||
uses: docker/metadata-action@v4
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
${{ env.PILOT_IMAGE_REGISTRY }}/${{ env.PILOT_IMAGE_NAME }}
|
${{ env.PILOT_IMAGE_REGISTRY }}/${{ env.PILOT_IMAGE_NAME }}
|
||||||
@@ -143,7 +143,7 @@ jobs:
|
|||||||
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
|
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
|
||||||
|
|
||||||
- name: Login to Docker Registry
|
- name: Login to Docker Registry
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.PILOT_IMAGE_REGISTRY }}
|
registry: ${{ env.PILOT_IMAGE_REGISTRY }}
|
||||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||||
@@ -169,7 +169,7 @@ jobs:
|
|||||||
GATEWAY_IMAGE_NAME: ${{ vars.GATEWAY_IMAGE_NAME || 'higress/gateway' }}
|
GATEWAY_IMAGE_NAME: ${{ vars.GATEWAY_IMAGE_NAME || 'higress/gateway' }}
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout ${{ github.ref }}"
|
- name: "Checkout ${{ github.ref }}"
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
|
|
||||||
@@ -184,12 +184,12 @@ jobs:
|
|||||||
swap-storage: true
|
swap-storage: true
|
||||||
|
|
||||||
- name: "Setup Go"
|
- name: "Setup Go"
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: 1.19
|
go-version: 1.19
|
||||||
|
|
||||||
- name: Setup Golang Caches
|
- name: Setup Golang Caches
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |-
|
path: |-
|
||||||
~/.cache/go-build
|
~/.cache/go-build
|
||||||
@@ -198,7 +198,7 @@ jobs:
|
|||||||
restore-keys: ${{ runner.os }}-go
|
restore-keys: ${{ runner.os }}-go
|
||||||
|
|
||||||
- name: Setup Submodule Caches
|
- name: Setup Submodule Caches
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |-
|
path: |-
|
||||||
envoy
|
envoy
|
||||||
@@ -209,7 +209,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Calculate Docker metadata
|
- name: Calculate Docker metadata
|
||||||
id: docker-meta
|
id: docker-meta
|
||||||
uses: docker/metadata-action@v4
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
${{ env.GATEWAY_IMAGE_REGISTRY }}/${{ env.GATEWAY_IMAGE_NAME }}
|
${{ env.GATEWAY_IMAGE_REGISTRY }}/${{ env.GATEWAY_IMAGE_NAME }}
|
||||||
@@ -220,7 +220,7 @@ jobs:
|
|||||||
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
|
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
|
||||||
|
|
||||||
- name: Login to Docker Registry
|
- name: Login to Docker Registry
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.GATEWAY_IMAGE_REGISTRY }}
|
registry: ${{ env.GATEWAY_IMAGE_REGISTRY }}
|
||||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||||
|
|||||||
8
.github/workflows/codeql-analysis.yaml
vendored
8
.github/workflows/codeql-analysis.yaml
vendored
@@ -34,11 +34,11 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
# step 1
|
# step 1
|
||||||
- name: "Checkout repository"
|
- name: "Checkout repository"
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
# step 2: Initializes the CodeQL tools for scanning.
|
# step 2: Initializes the CodeQL tools for scanning.
|
||||||
- name: "Initialize CodeQL"
|
- name: "Initialize CodeQL"
|
||||||
uses: github/codeql-action/init@v1
|
uses: github/codeql-action/init@v2
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
@@ -50,7 +50,7 @@ jobs:
|
|||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: "Autobuild"
|
- name: "Autobuild"
|
||||||
uses: github/codeql-action/autobuild@v1
|
uses: github/codeql-action/autobuild@v2
|
||||||
|
|
||||||
# step 4
|
# step 4
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
@@ -66,4 +66,4 @@ jobs:
|
|||||||
|
|
||||||
# step 5
|
# step 5
|
||||||
- name: "Perform CodeQL Analysis"
|
- name: "Perform CodeQL Analysis"
|
||||||
uses: github/codeql-action/analyze@v1
|
uses: github/codeql-action/analyze@v2
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
# Step 1
|
# Step 1
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
# Step 2
|
# Step 2
|
||||||
- id: package
|
- id: package
|
||||||
name: Prepare Standalone Package
|
name: Prepare Standalone Package
|
||||||
|
|||||||
2
.github/workflows/deploy-to-oss.yaml
vendored
2
.github/workflows/deploy-to-oss.yaml
vendored
@@ -14,7 +14,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
# Step 1
|
# Step 1
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
# Step 2
|
# Step 2
|
||||||
- name: Download Helm Charts Index
|
- name: Download Helm Charts Index
|
||||||
uses: doggycool/ossutil-github-action@master
|
uses: doggycool/ossutil-github-action@master
|
||||||
|
|||||||
4
.github/workflows/latest-release.yaml
vendored
4
.github/workflows/latest-release.yaml
vendored
@@ -9,7 +9,7 @@ jobs:
|
|||||||
latest-release:
|
latest-release:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Build hgctl latest multiarch binaries
|
- name: Build hgctl latest multiarch binaries
|
||||||
run: |
|
run: |
|
||||||
@@ -46,7 +46,7 @@ jobs:
|
|||||||
GITHUB_REPOSITORY: ${{ github.repository_owner }}/${{ github.event.repository.name }}
|
GITHUB_REPOSITORY: ${{ github.repository_owner }}/${{ github.event.repository.name }}
|
||||||
|
|
||||||
- name: Recreate the Latest Release and Tag
|
- name: Recreate the Latest Release and Tag
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
draft: false
|
draft: false
|
||||||
prerelease: true
|
prerelease: true
|
||||||
|
|||||||
4
.github/workflows/license-checker.yaml
vendored
4
.github/workflows/license-checker.yaml
vendored
@@ -10,7 +10,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
# step 1
|
# step 1
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2.4.0
|
uses: actions/checkout@v4
|
||||||
# step 2
|
# step 2
|
||||||
- name: Check License Header
|
- name: Check License Header
|
||||||
uses: apache/skywalking-eyes/header@25edfc2fd8d52fb266653fb5f6c42da633d85c07
|
uses: apache/skywalking-eyes/header@25edfc2fd8d52fb266653fb5f6c42da633d85c07
|
||||||
@@ -24,4 +24,4 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
log: info
|
log: info
|
||||||
config: .licenserc.yaml
|
config: .licenserc.yaml
|
||||||
mode: check
|
mode: check
|
||||||
|
|||||||
6
.github/workflows/release-hgctl.yaml
vendored
6
.github/workflows/release-hgctl.yaml
vendored
@@ -12,7 +12,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
HGCTL_VERSION: ${{github.ref_name}}
|
HGCTL_VERSION: ${{github.ref_name}}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Build hgctl latest multiarch binaries
|
- name: Build hgctl latest multiarch binaries
|
||||||
run: |
|
run: |
|
||||||
@@ -25,7 +25,7 @@ jobs:
|
|||||||
zip -q -r hgctl_${{ env.HGCTL_VERSION }}_windows_arm64.zip out/windows_arm64/
|
zip -q -r hgctl_${{ env.HGCTL_VERSION }}_windows_arm64.zip out/windows_arm64/
|
||||||
|
|
||||||
- name: Upload hgctl packages to the GitHub release
|
- name: Upload hgctl packages to the GitHub release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v2
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
with:
|
with:
|
||||||
files: |
|
files: |
|
||||||
@@ -34,4 +34,4 @@ jobs:
|
|||||||
hgctl_${{ env.HGCTL_VERSION }}_darwin_amd64.tar.gz
|
hgctl_${{ env.HGCTL_VERSION }}_darwin_amd64.tar.gz
|
||||||
hgctl_${{ env.HGCTL_VERSION }}_darwin_arm64.tar.gz
|
hgctl_${{ env.HGCTL_VERSION }}_darwin_arm64.tar.gz
|
||||||
hgctl_${{ env.HGCTL_VERSION }}_windows_amd64.zip
|
hgctl_${{ env.HGCTL_VERSION }}_windows_amd64.zip
|
||||||
hgctl_${{ env.HGCTL_VERSION }}_windows_arm64.zip
|
hgctl_${{ env.HGCTL_VERSION }}_windows_arm64.zip
|
||||||
|
|||||||
@@ -138,11 +138,11 @@ export ENVOY_TAR_PATH:=/home/package/envoy.tar.gz
|
|||||||
|
|
||||||
external/package/envoy-amd64.tar.gz:
|
external/package/envoy-amd64.tar.gz:
|
||||||
# cd external/proxy; BUILD_WITH_CONTAINER=1 make test_release
|
# cd external/proxy; BUILD_WITH_CONTAINER=1 make test_release
|
||||||
cd external/package; wget -O envoy-amd64.tar.gz "https://github.com/alibaba/higress/releases/download/v1.4.0/envoy-symbol-amd64.tar.gz"
|
cd external/package; wget -O envoy-amd64.tar.gz "https://github.com/alibaba/higress/releases/download/v1.4.1/envoy-symbol-amd64.tar.gz"
|
||||||
|
|
||||||
external/package/envoy-arm64.tar.gz:
|
external/package/envoy-arm64.tar.gz:
|
||||||
# cd external/proxy; BUILD_WITH_CONTAINER=1 make test_release
|
# cd external/proxy; BUILD_WITH_CONTAINER=1 make test_release
|
||||||
cd external/package; wget -O envoy-arm64.tar.gz "https://github.com/alibaba/higress/releases/download/v1.4.0/envoy-symbol-arm64.tar.gz"
|
cd external/package; wget -O envoy-arm64.tar.gz "https://github.com/alibaba/higress/releases/download/v1.4.1/envoy-symbol-arm64.tar.gz"
|
||||||
|
|
||||||
build-pilot:
|
build-pilot:
|
||||||
cd external/istio; rm -rf out/linux_amd64; GOOS_LOCAL=linux TARGET_OS=linux TARGET_ARCH=amd64 BUILD_WITH_CONTAINER=1 make build-linux
|
cd external/istio; rm -rf out/linux_amd64; GOOS_LOCAL=linux TARGET_OS=linux TARGET_ARCH=amd64 BUILD_WITH_CONTAINER=1 make build-linux
|
||||||
@@ -177,8 +177,8 @@ install: pre-install
|
|||||||
cd helm/higress; helm dependency build
|
cd helm/higress; helm dependency build
|
||||||
helm install higress helm/higress -n higress-system --create-namespace --set 'global.local=true'
|
helm install higress helm/higress -n higress-system --create-namespace --set 'global.local=true'
|
||||||
|
|
||||||
ENVOY_LATEST_IMAGE_TAG ?= sha-93966bf
|
ENVOY_LATEST_IMAGE_TAG ?= sha-63539ca
|
||||||
ISTIO_LATEST_IMAGE_TAG ?= sha-b00f79f
|
ISTIO_LATEST_IMAGE_TAG ?= sha-63539ca
|
||||||
|
|
||||||
install-dev: pre-install
|
install-dev: pre-install
|
||||||
helm install higress helm/core -n higress-system --create-namespace --set 'controller.tag=$(TAG)' --set 'gateway.replicas=1' --set 'pilot.tag=$(ISTIO_LATEST_IMAGE_TAG)' --set 'gateway.tag=$(ENVOY_LATEST_IMAGE_TAG)' --set 'global.local=true'
|
helm install higress helm/core -n higress-system --create-namespace --set 'controller.tag=$(TAG)' --set 'gateway.replicas=1' --set 'pilot.tag=$(ISTIO_LATEST_IMAGE_TAG)' --set 'gateway.tag=$(ENVOY_LATEST_IMAGE_TAG)' --set 'global.local=true'
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
diff --git a/source/common/http/headers.h b/source/common/http/headers.h
|
||||||
|
index a7a8a3393e..6af4a2852d 100644
|
||||||
|
--- a/source/common/http/headers.h
|
||||||
|
+++ b/source/common/http/headers.h
|
||||||
|
@@ -123,7 +123,7 @@ public:
|
||||||
|
const LowerCaseString TriCostTime{"req-cost-time"};
|
||||||
|
const LowerCaseString TriStartTime{"req-start-time"};
|
||||||
|
const LowerCaseString TriRespStartTime{"resp-start-time"};
|
||||||
|
- const LowerCaseString EnvoyOriginalHost{"original-host"};
|
||||||
|
+ const LowerCaseString EnvoyOriginalHost{"x-envoy-original-host"};
|
||||||
|
const LowerCaseString HigressOriginalService{"x-higress-original-service"};
|
||||||
|
} AliExtendedValues;
|
||||||
|
#endif
|
||||||
43
envoy/1.20/patches/envoy/20240725-set-buffer-limit.patch
Normal file
43
envoy/1.20/patches/envoy/20240725-set-buffer-limit.patch
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
diff --git a/source/extensions/common/wasm/context.cc b/source/extensions/common/wasm/context.cc
|
||||||
|
index 9642d8abd3..410baa856f 100644
|
||||||
|
--- a/source/extensions/common/wasm/context.cc
|
||||||
|
+++ b/source/extensions/common/wasm/context.cc
|
||||||
|
@@ -62,6 +62,21 @@ constexpr absl::string_view CelStateKeyPrefix = "wasm.";
|
||||||
|
#if defined(ALIMESH)
|
||||||
|
constexpr std::string_view ClearRouteCacheKey = "clear_route_cache";
|
||||||
|
constexpr std::string_view DisableClearRouteCache = "off";
|
||||||
|
+constexpr std::string_view SetDecoderBufferLimit = "set_decoder_buffer_limit";
|
||||||
|
+constexpr std::string_view SetEncoderBufferLimit = "set_encoder_buffer_limit";
|
||||||
|
+
|
||||||
|
+bool stringViewToUint32(std::string_view str, uint32_t& out_value) {
|
||||||
|
+ try {
|
||||||
|
+ unsigned long temp = std::stoul(std::string(str));
|
||||||
|
+ if (temp <= std::numeric_limits<uint32_t>::max()) {
|
||||||
|
+ out_value = static_cast<uint32_t>(temp);
|
||||||
|
+ return true;
|
||||||
|
+ }
|
||||||
|
+ } catch (const std::exception& e) {
|
||||||
|
+ ENVOY_LOG_MISC(critical, "stringToUint exception '{}'", e.what());
|
||||||
|
+ }
|
||||||
|
+ return false;
|
||||||
|
+}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using HashPolicy = envoy::config::route::v3::RouteAction::HashPolicy;
|
||||||
|
@@ -1280,6 +1295,16 @@ WasmResult Context::setProperty(std::string_view path, std::string_view value) {
|
||||||
|
} else {
|
||||||
|
disable_clear_route_cache_ = false;
|
||||||
|
}
|
||||||
|
+ } else if (path == SetDecoderBufferLimit && decoder_callbacks_) {
|
||||||
|
+ uint32_t buffer_limit;
|
||||||
|
+ if (stringViewToUint32(value, buffer_limit)) {
|
||||||
|
+ decoder_callbacks_->setDecoderBufferLimit(buffer_limit);
|
||||||
|
+ }
|
||||||
|
+ } else if (path == SetEncoderBufferLimit && encoder_callbacks_) {
|
||||||
|
+ uint32_t buffer_limit;
|
||||||
|
+ if (stringViewToUint32(value, buffer_limit)) {
|
||||||
|
+ encoder_callbacks_->setEncoderBufferLimit(buffer_limit);
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (!state->setValue(toAbslStringView(value))) {
|
||||||
106
envoy/1.20/patches/envoy/20240726-custom-span-tag.patch
Normal file
106
envoy/1.20/patches/envoy/20240726-custom-span-tag.patch
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
diff --git a/envoy/stream_info/stream_info.h b/envoy/stream_info/stream_info.h
|
||||||
|
index c6d82db4f4..09717673b0 100644
|
||||||
|
--- a/envoy/stream_info/stream_info.h
|
||||||
|
+++ b/envoy/stream_info/stream_info.h
|
||||||
|
@@ -613,7 +613,21 @@ public:
|
||||||
|
* @return the number of times the request was attempted upstream, absl::nullopt if the request
|
||||||
|
* was never attempted upstream.
|
||||||
|
*/
|
||||||
|
+
|
||||||
|
virtual absl::optional<uint32_t> attemptCount() const PURE;
|
||||||
|
+
|
||||||
|
+#ifdef ALIMESH
|
||||||
|
+ /**
|
||||||
|
+ * @param key the filter state key set by wasm filter.
|
||||||
|
+ * @param value the filter state value set by wasm filter.
|
||||||
|
+ */
|
||||||
|
+ virtual void setCustomSpanTag(const std::string& key, const std::string& value) PURE;
|
||||||
|
+
|
||||||
|
+ /**
|
||||||
|
+ * @return the key-value map of filter states set by wasm filter.
|
||||||
|
+ */
|
||||||
|
+ virtual const std::unordered_map<std::string, std::string>& getCustomSpanTagMap() const PURE;
|
||||||
|
+#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace StreamInfo
|
||||||
|
diff --git a/source/common/stream_info/stream_info_impl.h b/source/common/stream_info/stream_info_impl.h
|
||||||
|
index 6ce2afe773..d5e7a80b37 100644
|
||||||
|
--- a/source/common/stream_info/stream_info_impl.h
|
||||||
|
+++ b/source/common/stream_info/stream_info_impl.h
|
||||||
|
@@ -291,6 +291,20 @@ struct StreamInfoImpl : public StreamInfo {
|
||||||
|
|
||||||
|
absl::optional<uint32_t> attemptCount() const override { return attempt_count_; }
|
||||||
|
|
||||||
|
+#ifdef ALIMESH
|
||||||
|
+ void setCustomSpanTag(const std::string& key, const std::string& value) override {
|
||||||
|
+ auto it = custom_span_tags_.find(key);
|
||||||
|
+ if (it != custom_span_tags_.end()) {
|
||||||
|
+ it->second = value;
|
||||||
|
+ } else {
|
||||||
|
+ custom_span_tags_.emplace(key, value);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ const std::unordered_map<std::string, std::string>& getCustomSpanTagMap() const override {
|
||||||
|
+ return custom_span_tags_;
|
||||||
|
+ }
|
||||||
|
+#endif
|
||||||
|
TimeSource& time_source_;
|
||||||
|
const SystemTime start_time_;
|
||||||
|
const MonotonicTime start_time_monotonic_;
|
||||||
|
@@ -350,6 +364,9 @@ private:
|
||||||
|
absl::optional<Upstream::ClusterInfoConstSharedPtr> upstream_cluster_info_;
|
||||||
|
std::string filter_chain_name_;
|
||||||
|
Tracing::Reason trace_reason_;
|
||||||
|
+#ifdef ALIMESH
|
||||||
|
+ std::unordered_map<std::string, std::string> custom_span_tags_;
|
||||||
|
+#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace StreamInfo
|
||||||
|
diff --git a/source/common/tracing/http_tracer_impl.cc b/source/common/tracing/http_tracer_impl.cc
|
||||||
|
index e55cf00e0a..f94e9101d7 100644
|
||||||
|
--- a/source/common/tracing/http_tracer_impl.cc
|
||||||
|
+++ b/source/common/tracing/http_tracer_impl.cc
|
||||||
|
@@ -214,6 +214,14 @@ void HttpTracerUtility::setCommonTags(Span& span, const Http::ResponseHeaderMap*
|
||||||
|
|
||||||
|
span.setTag(Tracing::Tags::get().Component, Tracing::Tags::get().Proxy);
|
||||||
|
|
||||||
|
+#ifdef ALIMESH
|
||||||
|
+ // Wasm filter state
|
||||||
|
+ const auto& custom_span_tags = stream_info.getCustomSpanTagMap();
|
||||||
|
+ for (const auto& it : custom_span_tags) {
|
||||||
|
+ span.setTag(it.first, it.second);
|
||||||
|
+ }
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
if (nullptr != stream_info.upstreamHost()) {
|
||||||
|
span.setTag(Tracing::Tags::get().UpstreamCluster, stream_info.upstreamHost()->cluster().name());
|
||||||
|
span.setTag(Tracing::Tags::get().UpstreamClusterName,
|
||||||
|
diff --git a/source/extensions/common/wasm/context.cc b/source/extensions/common/wasm/context.cc
|
||||||
|
index 410baa856f..b11ecf1cd6 100644
|
||||||
|
--- a/source/extensions/common/wasm/context.cc
|
||||||
|
+++ b/source/extensions/common/wasm/context.cc
|
||||||
|
@@ -60,6 +60,7 @@ namespace {
|
||||||
|
constexpr absl::string_view CelStateKeyPrefix = "wasm.";
|
||||||
|
|
||||||
|
#if defined(ALIMESH)
|
||||||
|
+constexpr absl::string_view CustomeTraceSpanTagPrefix = "trace_span_tag.";
|
||||||
|
constexpr std::string_view ClearRouteCacheKey = "clear_route_cache";
|
||||||
|
constexpr std::string_view DisableClearRouteCache = "off";
|
||||||
|
constexpr std::string_view SetDecoderBufferLimit = "set_decoder_buffer_limit";
|
||||||
|
@@ -1271,6 +1272,13 @@ WasmResult Context::setProperty(std::string_view path, std::string_view value) {
|
||||||
|
if (!stream_info) {
|
||||||
|
return WasmResult::NotFound;
|
||||||
|
}
|
||||||
|
+#ifdef ALIMESH
|
||||||
|
+ if (absl::StartsWith(absl::string_view{path.data(), path.size()}, CustomeTraceSpanTagPrefix)) {
|
||||||
|
+ stream_info->setCustomSpanTag(std::string(path.substr(CustomeTraceSpanTagPrefix.size())),
|
||||||
|
+ std::string(value));
|
||||||
|
+ return WasmResult::Ok;
|
||||||
|
+ }
|
||||||
|
+#endif
|
||||||
|
std::string key;
|
||||||
|
absl::StrAppend(&key, CelStateKeyPrefix, toAbslStringView(path));
|
||||||
|
CelState* state;
|
||||||
341
get_helm.sh
Executable file
341
get_helm.sh
Executable file
@@ -0,0 +1,341 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Copyright The Helm Authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
# The install script is based off of the MIT-licensed script from glide,
|
||||||
|
# the package manager for Go: https://github.com/Masterminds/glide.sh/blob/master/get
|
||||||
|
|
||||||
|
: ${BINARY_NAME:="helm"}
|
||||||
|
: ${USE_SUDO:="true"}
|
||||||
|
: ${DEBUG:="false"}
|
||||||
|
: ${VERIFY_CHECKSUM:="true"}
|
||||||
|
: ${VERIFY_SIGNATURES:="false"}
|
||||||
|
: ${HELM_INSTALL_DIR:="/usr/local/bin"}
|
||||||
|
: ${GPG_PUBRING:="pubring.kbx"}
|
||||||
|
|
||||||
|
HAS_CURL="$(type "curl" &> /dev/null && echo true || echo false)"
|
||||||
|
HAS_WGET="$(type "wget" &> /dev/null && echo true || echo false)"
|
||||||
|
HAS_OPENSSL="$(type "openssl" &> /dev/null && echo true || echo false)"
|
||||||
|
HAS_GPG="$(type "gpg" &> /dev/null && echo true || echo false)"
|
||||||
|
HAS_GIT="$(type "git" &> /dev/null && echo true || echo false)"
|
||||||
|
|
||||||
|
# initArch discovers the architecture for this system.
|
||||||
|
initArch() {
|
||||||
|
ARCH=$(uname -m)
|
||||||
|
case $ARCH in
|
||||||
|
armv5*) ARCH="armv5";;
|
||||||
|
armv6*) ARCH="armv6";;
|
||||||
|
armv7*) ARCH="arm";;
|
||||||
|
aarch64) ARCH="arm64";;
|
||||||
|
x86) ARCH="386";;
|
||||||
|
x86_64) ARCH="amd64";;
|
||||||
|
i686) ARCH="386";;
|
||||||
|
i386) ARCH="386";;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# initOS discovers the operating system for this system.
|
||||||
|
initOS() {
|
||||||
|
OS=$(echo `uname`|tr '[:upper:]' '[:lower:]')
|
||||||
|
|
||||||
|
case "$OS" in
|
||||||
|
# Minimalist GNU for Windows
|
||||||
|
mingw*|cygwin*) OS='windows';;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# runs the given command as root (detects if we are root already)
|
||||||
|
runAsRoot() {
|
||||||
|
if [ $EUID -ne 0 -a "$USE_SUDO" = "true" ]; then
|
||||||
|
sudo "${@}"
|
||||||
|
else
|
||||||
|
"${@}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# verifySupported checks that the os/arch combination is supported for
|
||||||
|
# binary builds, as well whether or not necessary tools are present.
|
||||||
|
verifySupported() {
|
||||||
|
local supported="darwin-amd64\ndarwin-arm64\nlinux-386\nlinux-amd64\nlinux-arm\nlinux-arm64\nlinux-ppc64le\nlinux-s390x\nlinux-riscv64\nwindows-amd64\nwindows-arm64"
|
||||||
|
if ! echo "${supported}" | grep -q "${OS}-${ARCH}"; then
|
||||||
|
echo "No prebuilt binary for ${OS}-${ARCH}."
|
||||||
|
echo "To build from source, go to https://github.com/helm/helm"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${HAS_CURL}" != "true" ] && [ "${HAS_WGET}" != "true" ]; then
|
||||||
|
echo "Either curl or wget is required"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${VERIFY_CHECKSUM}" == "true" ] && [ "${HAS_OPENSSL}" != "true" ]; then
|
||||||
|
echo "In order to verify checksum, openssl must first be installed."
|
||||||
|
echo "Please install openssl or set VERIFY_CHECKSUM=false in your environment."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${VERIFY_SIGNATURES}" == "true" ]; then
|
||||||
|
if [ "${HAS_GPG}" != "true" ]; then
|
||||||
|
echo "In order to verify signatures, gpg must first be installed."
|
||||||
|
echo "Please install gpg or set VERIFY_SIGNATURES=false in your environment."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ "${OS}" != "linux" ]; then
|
||||||
|
echo "Signature verification is currently only supported on Linux."
|
||||||
|
echo "Please set VERIFY_SIGNATURES=false or verify the signatures manually."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${HAS_GIT}" != "true" ]; then
|
||||||
|
echo "[WARNING] Could not find git. It is required for plugin installation."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# checkDesiredVersion checks if the desired version is available.
|
||||||
|
checkDesiredVersion() {
|
||||||
|
if [ "x$DESIRED_VERSION" == "x" ]; then
|
||||||
|
# Get tag from release URL
|
||||||
|
local latest_release_url="https://get.helm.sh/helm-latest-version"
|
||||||
|
local latest_release_response=""
|
||||||
|
if [ "${HAS_CURL}" == "true" ]; then
|
||||||
|
latest_release_response=$( curl -L --silent --show-error --fail "$latest_release_url" 2>&1 || true )
|
||||||
|
elif [ "${HAS_WGET}" == "true" ]; then
|
||||||
|
latest_release_response=$( wget "$latest_release_url" -q -O - 2>&1 || true )
|
||||||
|
fi
|
||||||
|
TAG=$( echo "$latest_release_response" | grep '^v[0-9]' )
|
||||||
|
if [ "x$TAG" == "x" ]; then
|
||||||
|
printf "Could not retrieve the latest release tag information from %s: %s\n" "${latest_release_url}" "${latest_release_response}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
TAG=$DESIRED_VERSION
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# checkHelmInstalledVersion checks which version of helm is installed and
|
||||||
|
# if it needs to be changed.
|
||||||
|
checkHelmInstalledVersion() {
|
||||||
|
if [[ -f "${HELM_INSTALL_DIR}/${BINARY_NAME}" ]]; then
|
||||||
|
local version=$("${HELM_INSTALL_DIR}/${BINARY_NAME}" version --template="{{ .Version }}")
|
||||||
|
if [[ "$version" == "$TAG" ]]; then
|
||||||
|
echo "Helm ${version} is already ${DESIRED_VERSION:-latest}"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo "Helm ${TAG} is available. Changing from version ${version}."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# downloadFile downloads the latest binary package and also the checksum
|
||||||
|
# for that binary.
|
||||||
|
downloadFile() {
|
||||||
|
HELM_DIST="helm-$TAG-$OS-$ARCH.tar.gz"
|
||||||
|
DOWNLOAD_URL="https://get.helm.sh/$HELM_DIST"
|
||||||
|
CHECKSUM_URL="$DOWNLOAD_URL.sha256"
|
||||||
|
HELM_TMP_ROOT="$(mktemp -dt helm-installer-XXXXXX)"
|
||||||
|
HELM_TMP_FILE="$HELM_TMP_ROOT/$HELM_DIST"
|
||||||
|
HELM_SUM_FILE="$HELM_TMP_ROOT/$HELM_DIST.sha256"
|
||||||
|
echo "Downloading $DOWNLOAD_URL"
|
||||||
|
if [ "${HAS_CURL}" == "true" ]; then
|
||||||
|
curl -SsL "$CHECKSUM_URL" -o "$HELM_SUM_FILE"
|
||||||
|
curl -SsL "$DOWNLOAD_URL" -o "$HELM_TMP_FILE"
|
||||||
|
elif [ "${HAS_WGET}" == "true" ]; then
|
||||||
|
wget -q -O "$HELM_SUM_FILE" "$CHECKSUM_URL"
|
||||||
|
wget -q -O "$HELM_TMP_FILE" "$DOWNLOAD_URL"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# verifyFile verifies the SHA256 checksum of the binary package
|
||||||
|
# and the GPG signatures for both the package and checksum file
|
||||||
|
# (depending on settings in environment).
|
||||||
|
verifyFile() {
|
||||||
|
if [ "${VERIFY_CHECKSUM}" == "true" ]; then
|
||||||
|
verifyChecksum
|
||||||
|
fi
|
||||||
|
if [ "${VERIFY_SIGNATURES}" == "true" ]; then
|
||||||
|
verifySignatures
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# installFile installs the Helm binary.
|
||||||
|
installFile() {
|
||||||
|
HELM_TMP="$HELM_TMP_ROOT/$BINARY_NAME"
|
||||||
|
mkdir -p "$HELM_TMP"
|
||||||
|
tar xf "$HELM_TMP_FILE" -C "$HELM_TMP"
|
||||||
|
HELM_TMP_BIN="$HELM_TMP/$OS-$ARCH/helm"
|
||||||
|
echo "Preparing to install $BINARY_NAME into ${HELM_INSTALL_DIR}"
|
||||||
|
runAsRoot cp "$HELM_TMP_BIN" "$HELM_INSTALL_DIR/$BINARY_NAME"
|
||||||
|
echo "$BINARY_NAME installed into $HELM_INSTALL_DIR/$BINARY_NAME"
|
||||||
|
}
|
||||||
|
|
||||||
|
# verifyChecksum verifies the SHA256 checksum of the binary package.
|
||||||
|
verifyChecksum() {
|
||||||
|
printf "Verifying checksum... "
|
||||||
|
local sum=$(openssl sha1 -sha256 ${HELM_TMP_FILE} | awk '{print $2}')
|
||||||
|
local expected_sum=$(cat ${HELM_SUM_FILE})
|
||||||
|
if [ "$sum" != "$expected_sum" ]; then
|
||||||
|
echo "SHA sum of ${HELM_TMP_FILE} does not match. Aborting."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Done."
|
||||||
|
}
|
||||||
|
|
||||||
|
# verifySignatures obtains the latest KEYS file from GitHub main branch
|
||||||
|
# as well as the signature .asc files from the specific GitHub release,
|
||||||
|
# then verifies that the release artifacts were signed by a maintainer's key.
|
||||||
|
verifySignatures() {
|
||||||
|
printf "Verifying signatures... "
|
||||||
|
local keys_filename="KEYS"
|
||||||
|
local github_keys_url="https://raw.githubusercontent.com/helm/helm/main/${keys_filename}"
|
||||||
|
if [ "${HAS_CURL}" == "true" ]; then
|
||||||
|
curl -SsL "${github_keys_url}" -o "${HELM_TMP_ROOT}/${keys_filename}"
|
||||||
|
elif [ "${HAS_WGET}" == "true" ]; then
|
||||||
|
wget -q -O "${HELM_TMP_ROOT}/${keys_filename}" "${github_keys_url}"
|
||||||
|
fi
|
||||||
|
local gpg_keyring="${HELM_TMP_ROOT}/keyring.gpg"
|
||||||
|
local gpg_homedir="${HELM_TMP_ROOT}/gnupg"
|
||||||
|
mkdir -p -m 0700 "${gpg_homedir}"
|
||||||
|
local gpg_stderr_device="/dev/null"
|
||||||
|
if [ "${DEBUG}" == "true" ]; then
|
||||||
|
gpg_stderr_device="/dev/stderr"
|
||||||
|
fi
|
||||||
|
gpg --batch --quiet --homedir="${gpg_homedir}" --import "${HELM_TMP_ROOT}/${keys_filename}" 2> "${gpg_stderr_device}"
|
||||||
|
gpg --batch --no-default-keyring --keyring "${gpg_homedir}/${GPG_PUBRING}" --export > "${gpg_keyring}"
|
||||||
|
local github_release_url="https://github.com/helm/helm/releases/download/${TAG}"
|
||||||
|
if [ "${HAS_CURL}" == "true" ]; then
|
||||||
|
curl -SsL "${github_release_url}/helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256.asc" -o "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256.asc"
|
||||||
|
curl -SsL "${github_release_url}/helm-${TAG}-${OS}-${ARCH}.tar.gz.asc" -o "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.asc"
|
||||||
|
elif [ "${HAS_WGET}" == "true" ]; then
|
||||||
|
wget -q -O "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256.asc" "${github_release_url}/helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256.asc"
|
||||||
|
wget -q -O "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.asc" "${github_release_url}/helm-${TAG}-${OS}-${ARCH}.tar.gz.asc"
|
||||||
|
fi
|
||||||
|
local error_text="If you think this might be a potential security issue,"
|
||||||
|
error_text="${error_text}\nplease see here: https://github.com/helm/community/blob/master/SECURITY.md"
|
||||||
|
local num_goodlines_sha=$(gpg --verify --keyring="${gpg_keyring}" --status-fd=1 "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256.asc" 2> "${gpg_stderr_device}" | grep -c -E '^\[GNUPG:\] (GOODSIG|VALIDSIG)')
|
||||||
|
if [[ ${num_goodlines_sha} -lt 2 ]]; then
|
||||||
|
echo "Unable to verify the signature of helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256!"
|
||||||
|
echo -e "${error_text}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
local num_goodlines_tar=$(gpg --verify --keyring="${gpg_keyring}" --status-fd=1 "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.asc" 2> "${gpg_stderr_device}" | grep -c -E '^\[GNUPG:\] (GOODSIG|VALIDSIG)')
|
||||||
|
if [[ ${num_goodlines_tar} -lt 2 ]]; then
|
||||||
|
echo "Unable to verify the signature of helm-${TAG}-${OS}-${ARCH}.tar.gz!"
|
||||||
|
echo -e "${error_text}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Done."
|
||||||
|
}
|
||||||
|
|
||||||
|
# fail_trap is executed if an error occurs.
|
||||||
|
fail_trap() {
|
||||||
|
result=$?
|
||||||
|
if [ "$result" != "0" ]; then
|
||||||
|
if [[ -n "$INPUT_ARGUMENTS" ]]; then
|
||||||
|
echo "Failed to install $BINARY_NAME with the arguments provided: $INPUT_ARGUMENTS"
|
||||||
|
help
|
||||||
|
else
|
||||||
|
echo "Failed to install $BINARY_NAME"
|
||||||
|
fi
|
||||||
|
echo -e "\tFor support, go to https://github.com/helm/helm."
|
||||||
|
fi
|
||||||
|
cleanup
|
||||||
|
exit $result
|
||||||
|
}
|
||||||
|
|
||||||
|
# testVersion tests the installed client to make sure it is working.
|
||||||
|
testVersion() {
|
||||||
|
set +e
|
||||||
|
HELM="$(command -v $BINARY_NAME)"
|
||||||
|
if [ "$?" = "1" ]; then
|
||||||
|
echo "$BINARY_NAME not found. Is $HELM_INSTALL_DIR on your "'$PATH?'
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
set -e
|
||||||
|
}
|
||||||
|
|
||||||
|
# help provides possible cli installation arguments
|
||||||
|
help () {
|
||||||
|
echo "Accepted cli arguments are:"
|
||||||
|
echo -e "\t[--help|-h ] ->> prints this help"
|
||||||
|
echo -e "\t[--version|-v <desired_version>] . When not defined it fetches the latest release from GitHub"
|
||||||
|
echo -e "\te.g. --version v3.0.0 or -v canary"
|
||||||
|
echo -e "\t[--no-sudo] ->> install without sudo"
|
||||||
|
}
|
||||||
|
|
||||||
|
# cleanup temporary files to avoid https://github.com/helm/helm/issues/2977
|
||||||
|
cleanup() {
|
||||||
|
if [[ -d "${HELM_TMP_ROOT:-}" ]]; then
|
||||||
|
rm -rf "$HELM_TMP_ROOT"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Execution
|
||||||
|
|
||||||
|
#Stop execution on any error
|
||||||
|
trap "fail_trap" EXIT
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Set debug if desired
|
||||||
|
if [ "${DEBUG}" == "true" ]; then
|
||||||
|
set -x
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Parsing input arguments (if any)
|
||||||
|
export INPUT_ARGUMENTS="${@}"
|
||||||
|
set -u
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case $1 in
|
||||||
|
'--version'|-v)
|
||||||
|
shift
|
||||||
|
if [[ $# -ne 0 ]]; then
|
||||||
|
export DESIRED_VERSION="${1}"
|
||||||
|
if [[ "$1" != "v"* ]]; then
|
||||||
|
echo "Expected version arg ('${DESIRED_VERSION}') to begin with 'v', fixing..."
|
||||||
|
export DESIRED_VERSION="v${1}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "Please provide the desired version. e.g. --version v3.0.0 or -v canary"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
'--no-sudo')
|
||||||
|
USE_SUDO="false"
|
||||||
|
;;
|
||||||
|
'--help'|-h)
|
||||||
|
help
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*) exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
set +u
|
||||||
|
|
||||||
|
initArch
|
||||||
|
initOS
|
||||||
|
verifySupported
|
||||||
|
checkDesiredVersion
|
||||||
|
if ! checkHelmInstalledVersion; then
|
||||||
|
downloadFile
|
||||||
|
verifyFile
|
||||||
|
installFile
|
||||||
|
fi
|
||||||
|
testVersion
|
||||||
|
cleanup
|
||||||
4
go.mod
4
go.mod
@@ -255,7 +255,6 @@ require (
|
|||||||
go.opentelemetry.io/proto/otlp v0.12.0 // indirect
|
go.opentelemetry.io/proto/otlp v0.12.0 // indirect
|
||||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
|
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.uber.org/zap v1.24.0 // indirect
|
|
||||||
golang.org/x/crypto v0.17.0 // indirect
|
golang.org/x/crypto v0.17.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
|
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
|
||||||
golang.org/x/mod v0.11.0 // indirect
|
golang.org/x/mod v0.11.0 // indirect
|
||||||
@@ -304,7 +303,7 @@ replace istio.io/client-go => ./external/client-go
|
|||||||
|
|
||||||
replace istio.io/istio => ./external/istio
|
replace istio.io/istio => ./external/istio
|
||||||
|
|
||||||
replace github.com/caddyserver/certmagic => github.com/2456868764/certmagic v1.0.1
|
replace github.com/caddyserver/certmagic => github.com/2456868764/certmagic v1.0.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/caddyserver/certmagic v0.20.0
|
github.com/caddyserver/certmagic v0.20.0
|
||||||
@@ -313,6 +312,7 @@ require (
|
|||||||
github.com/kylelemons/godebug v1.1.0
|
github.com/kylelemons/godebug v1.1.0
|
||||||
github.com/mholt/acmez v1.2.0
|
github.com/mholt/acmez v1.2.0
|
||||||
github.com/tidwall/gjson v1.17.0
|
github.com/tidwall/gjson v1.17.0
|
||||||
|
go.uber.org/zap v1.24.0
|
||||||
golang.org/x/net v0.17.0
|
golang.org/x/net v0.17.0
|
||||||
helm.sh/helm/v3 v3.7.1
|
helm.sh/helm/v3 v3.7.1
|
||||||
k8s.io/apiextensions-apiserver v0.25.4
|
k8s.io/apiextensions-apiserver v0.25.4
|
||||||
|
|||||||
4
go.sum
4
go.sum
@@ -61,8 +61,8 @@ dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBr
|
|||||||
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
|
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
|
||||||
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
|
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
|
||||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||||
github.com/2456868764/certmagic v1.0.1 h1:dRzow2Npe9llFTBhNVl0fVe8Yi/Q14ygNonlaZUyDZQ=
|
github.com/2456868764/certmagic v1.0.2 h1:xYoN4z6seONwT85llWXZcASvQME8TOSiSWQvLJsGGsE=
|
||||||
github.com/2456868764/certmagic v1.0.1/go.mod h1:LOn81EQYMPajdew6Ln6SVdHPxPqPv6jwsUg92kiNlcQ=
|
github.com/2456868764/certmagic v1.0.2/go.mod h1:LOn81EQYMPajdew6Ln6SVdHPxPqPv6jwsUg92kiNlcQ=
|
||||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20210929163055-e81b3f25be97/go.mod h1:WpB7kf89yJUETZxQnP1kgYPNwlT2jjdDYUCoxVggM3g=
|
github.com/AdaLogics/go-fuzz-headers v0.0.0-20210929163055-e81b3f25be97/go.mod h1:WpB7kf89yJUETZxQnP1kgYPNwlT2jjdDYUCoxVggM3g=
|
||||||
github.com/AlecAivazis/survey/v2 v2.3.6 h1:NvTuVHISgTHEHeBFqt6BHOe4Ny/NwGZr7w+F8S9ziyw=
|
github.com/AlecAivazis/survey/v2 v2.3.6 h1:NvTuVHISgTHEHeBFqt6BHOe4Ny/NwGZr7w+F8S9ziyw=
|
||||||
github.com/AlecAivazis/survey/v2 v2.3.6/go.mod h1:4AuI9b7RjAR+G7v9+C4YSlX/YL3K3cWNXgWXOhllqvI=
|
github.com/AlecAivazis/survey/v2 v2.3.6/go.mod h1:4AuI9b7RjAR+G7v9+C4YSlX/YL3K3cWNXgWXOhllqvI=
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
apiVersion: v2
|
apiVersion: v2
|
||||||
appVersion: 1.4.1
|
appVersion: 1.4.2
|
||||||
description: Helm chart for deploying higress gateways
|
description: Helm chart for deploying higress gateways
|
||||||
icon: https://higress.io/img/higress_logo_small.png
|
icon: https://higress.io/img/higress_logo_small.png
|
||||||
home: http://higress.io/
|
home: http://higress.io/
|
||||||
@@ -10,4 +10,4 @@ name: higress-core
|
|||||||
sources:
|
sources:
|
||||||
- http://github.com/alibaba/higress
|
- http://github.com/alibaba/higress
|
||||||
type: application
|
type: application
|
||||||
version: 1.4.1
|
version: 1.4.2
|
||||||
|
|||||||
@@ -88,7 +88,7 @@
|
|||||||
{{- if .Values.global.enableHigressIstio }}
|
{{- if .Values.global.enableHigressIstio }}
|
||||||
discoveryAddress: {{ printf "istiod.%s.svc" .Values.global.istioNamespace }}:15012
|
discoveryAddress: {{ printf "istiod.%s.svc" .Values.global.istioNamespace }}:15012
|
||||||
{{- else }}
|
{{- else }}
|
||||||
discoveryAddress: higress-controller.{{.Release.Namespace}}.svc:15012
|
discoveryAddress: {{ include "controller.name" . }}.{{.Release.Namespace}}.svc:15012
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
proxyStatsMatcher:
|
proxyStatsMatcher:
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ rules:
|
|||||||
# ingress controller
|
# ingress controller
|
||||||
- apiGroups: ["extensions", "networking.k8s.io"]
|
- apiGroups: ["extensions", "networking.k8s.io"]
|
||||||
resources: ["ingresses"]
|
resources: ["ingresses"]
|
||||||
verbs: ["get", "list", "watch"]
|
verbs: ["create", "get", "list", "watch", "update", "delete", "patch"]
|
||||||
- apiGroups: ["extensions", "networking.k8s.io"]
|
- apiGroups: ["extensions", "networking.k8s.io"]
|
||||||
resources: ["ingresses/status"]
|
resources: ["ingresses/status"]
|
||||||
verbs: ["*"]
|
verbs: ["*"]
|
||||||
@@ -36,7 +36,7 @@ rules:
|
|||||||
# Needed for multicluster secret reading, possibly ingress certs in the future
|
# Needed for multicluster secret reading, possibly ingress certs in the future
|
||||||
- apiGroups: [""]
|
- apiGroups: [""]
|
||||||
resources: ["secrets"]
|
resources: ["secrets"]
|
||||||
verbs: ["get", "watch", "list"]
|
verbs: ["get", "watch", "list", "create", "update", "delete", "patch"]
|
||||||
|
|
||||||
- apiGroups: ["networking.higress.io"]
|
- apiGroups: ["networking.higress.io"]
|
||||||
resources: ["mcpbridges"]
|
resources: ["mcpbridges"]
|
||||||
@@ -61,12 +61,12 @@ rules:
|
|||||||
|
|
||||||
# discovery and routing
|
# discovery and routing
|
||||||
- apiGroups: [""]
|
- apiGroups: [""]
|
||||||
resources: ["pods", "nodes", "services", "namespaces", "endpoints"]
|
resources: ["pods", "nodes", "services", "namespaces", "endpoints", "deployments"]
|
||||||
verbs: ["get", "list", "watch"]
|
verbs: ["get", "list", "watch"]
|
||||||
- apiGroups: ["discovery.k8s.io"]
|
- apiGroups: ["discovery.k8s.io"]
|
||||||
resources: ["endpointslices"]
|
resources: ["endpointslices"]
|
||||||
verbs: ["get", "list", "watch"]
|
verbs: ["get", "list", "watch"]
|
||||||
|
|
||||||
# Istiod and bootstrap.
|
# Istiod and bootstrap.
|
||||||
- apiGroups: ["certificates.k8s.io"]
|
- apiGroups: ["certificates.k8s.io"]
|
||||||
resources:
|
resources:
|
||||||
@@ -100,7 +100,7 @@ rules:
|
|||||||
- apiGroups: ["multicluster.x-k8s.io"]
|
- apiGroups: ["multicluster.x-k8s.io"]
|
||||||
resources: ["serviceimports"]
|
resources: ["serviceimports"]
|
||||||
verbs: ["get", "watch", "list"]
|
verbs: ["get", "watch", "list"]
|
||||||
|
|
||||||
# sidecar injection controller
|
# sidecar injection controller
|
||||||
- apiGroups: ["admissionregistration.k8s.io"]
|
- apiGroups: ["admissionregistration.k8s.io"]
|
||||||
resources: ["mutatingwebhookconfigurations"]
|
resources: ["mutatingwebhookconfigurations"]
|
||||||
|
|||||||
@@ -26,9 +26,70 @@ spec:
|
|||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
serviceAccountName: {{ include "controller.serviceAccountName" . }}
|
serviceAccountName: {{ include "controller.serviceAccountName" . }}
|
||||||
|
{{- if .Values.global.priorityClassName }}
|
||||||
|
priorityClassName: "{{ .Values.global.priorityClassName }}"
|
||||||
|
{{- end }}
|
||||||
securityContext:
|
securityContext:
|
||||||
{{- toYaml .Values.controller.podSecurityContext | nindent 8 }}
|
{{- toYaml .Values.controller.podSecurityContext | nindent 8 }}
|
||||||
containers:
|
containers:
|
||||||
|
- name: {{ .Chart.Name }}
|
||||||
|
securityContext:
|
||||||
|
{{- toYaml .Values.controller.securityContext | nindent 12 }}
|
||||||
|
image: "{{ .Values.controller.hub | default .Values.global.hub }}/{{ .Values.controller.image | default "higress" }}:{{ .Values.controller.tag | default .Chart.AppVersion }}"
|
||||||
|
args:
|
||||||
|
- "serve"
|
||||||
|
- --gatewaySelectorKey=higress
|
||||||
|
- --gatewaySelectorValue={{ .Release.Namespace }}-{{ include "gateway.name" . }}
|
||||||
|
- --gatewayHttpPort={{ .Values.gateway.httpPort }}
|
||||||
|
- --gatewayHttpsPort={{ .Values.gateway.httpsPort }}
|
||||||
|
{{- if not .Values.global.enableStatus }}
|
||||||
|
- --enableStatus={{ .Values.global.enableStatus }}
|
||||||
|
{{- end }}
|
||||||
|
- --ingressClass={{ .Values.global.ingressClass }}
|
||||||
|
{{- if .Values.global.watchNamespace }}
|
||||||
|
- --watchNamespace={{ .Values.global.watchNamespace }}
|
||||||
|
{{- end }}
|
||||||
|
- --enableAutomaticHttps={{ .Values.controller.automaticHttps.enabled }}
|
||||||
|
- --automaticHttpsEmail={{ .Values.controller.automaticHttps.email }}
|
||||||
|
env:
|
||||||
|
- name: POD_NAME
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
apiVersion: v1
|
||||||
|
fieldPath: metadata.name
|
||||||
|
- name: POD_NAMESPACE
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
apiVersion: v1
|
||||||
|
fieldPath: metadata.namespace
|
||||||
|
- name: SERVICE_ACCOUNT
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
apiVersion: v1
|
||||||
|
fieldPath: spec.serviceAccountName
|
||||||
|
- name: DOMAIN_SUFFIX
|
||||||
|
value: {{ .Values.global.proxy.clusterDomain }}
|
||||||
|
{{- if .Values.controller.env }}
|
||||||
|
{{- range $key, $val := .Values.controller.env }}
|
||||||
|
- name: {{ $key }}
|
||||||
|
value: "{{ $val }}"
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
ports:
|
||||||
|
{{- range $idx, $port := .Values.controller.ports }}
|
||||||
|
- name: {{ $port.name }}
|
||||||
|
containerPort: {{ $port.port }}
|
||||||
|
protocol: {{ $port.protocol }}
|
||||||
|
{{- end }}
|
||||||
|
readinessProbe:
|
||||||
|
{{- toYaml .Values.controller.probe | nindent 12 }}
|
||||||
|
{{- if not (or .Values.global.local .Values.global.kind) }}
|
||||||
|
resources:
|
||||||
|
{{- toYaml .Values.controller.resources | nindent 12 }}
|
||||||
|
{{- end }}
|
||||||
|
volumeMounts:
|
||||||
|
- name: log
|
||||||
|
mountPath: /var/log
|
||||||
{{- if not .Values.global.enableHigressIstio }}
|
{{- if not .Values.global.enableHigressIstio }}
|
||||||
- name: discovery
|
- name: discovery
|
||||||
image: "{{ .Values.pilot.hub | default .Values.global.hub }}/{{ .Values.pilot.image | default "pilot" }}:{{ .Values.pilot.tag | default .Chart.AppVersion }}"
|
image: "{{ .Values.pilot.hub | default .Values.global.hub }}/{{ .Values.pilot.image | default "pilot" }}:{{ .Values.pilot.tag | default .Chart.AppVersion }}"
|
||||||
@@ -191,64 +252,6 @@ spec:
|
|||||||
mountPath: /cacerts
|
mountPath: /cacerts
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
- name: {{ .Chart.Name }}
|
|
||||||
securityContext:
|
|
||||||
{{- toYaml .Values.controller.securityContext | nindent 12 }}
|
|
||||||
image: "{{ .Values.controller.hub | default .Values.global.hub }}/{{ .Values.controller.image | default "higress" }}:{{ .Values.controller.tag | default .Chart.AppVersion }}"
|
|
||||||
args:
|
|
||||||
- "serve"
|
|
||||||
- --gatewaySelectorKey=higress
|
|
||||||
- --gatewaySelectorValue={{ .Release.Namespace }}-{{ include "gateway.name" . }}
|
|
||||||
- --gatewayHttpPort={{ .Values.gateway.httpPort }}
|
|
||||||
- --gatewayHttpsPort={{ .Values.gateway.httpsPort }}
|
|
||||||
{{- if not .Values.global.enableStatus }}
|
|
||||||
- --enableStatus={{ .Values.global.enableStatus }}
|
|
||||||
{{- end }}
|
|
||||||
- --ingressClass={{ .Values.global.ingressClass }}
|
|
||||||
{{- if .Values.global.watchNamespace }}
|
|
||||||
- --watchNamespace={{ .Values.global.watchNamespace }}
|
|
||||||
{{- end }}
|
|
||||||
- --enableAutomaticHttps={{ .Values.controller.automaticHttps.enabled }}
|
|
||||||
- --automaticHttpsEmail={{ .Values.controller.automaticHttps.email }}
|
|
||||||
env:
|
|
||||||
- name: POD_NAME
|
|
||||||
valueFrom:
|
|
||||||
fieldRef:
|
|
||||||
apiVersion: v1
|
|
||||||
fieldPath: metadata.name
|
|
||||||
- name: POD_NAMESPACE
|
|
||||||
valueFrom:
|
|
||||||
fieldRef:
|
|
||||||
apiVersion: v1
|
|
||||||
fieldPath: metadata.namespace
|
|
||||||
- name: SERVICE_ACCOUNT
|
|
||||||
valueFrom:
|
|
||||||
fieldRef:
|
|
||||||
apiVersion: v1
|
|
||||||
fieldPath: spec.serviceAccountName
|
|
||||||
- name: DOMAIN_SUFFIX
|
|
||||||
value: {{ .Values.global.proxy.clusterDomain }}
|
|
||||||
{{- if .Values.controller.env }}
|
|
||||||
{{- range $key, $val := .Values.controller.env }}
|
|
||||||
- name: {{ $key }}
|
|
||||||
value: "{{ $val }}"
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
ports:
|
|
||||||
{{- range $idx, $port := .Values.controller.ports }}
|
|
||||||
- name: {{ $port.name }}
|
|
||||||
containerPort: {{ $port.port }}
|
|
||||||
protocol: {{ $port.protocol }}
|
|
||||||
{{- end }}
|
|
||||||
readinessProbe:
|
|
||||||
{{- toYaml .Values.controller.probe | nindent 12 }}
|
|
||||||
{{- if not (or .Values.global.local .Values.global.kind) }}
|
|
||||||
resources:
|
|
||||||
{{- toYaml .Values.controller.resources | nindent 12 }}
|
|
||||||
{{- end }}
|
|
||||||
volumeMounts:
|
|
||||||
- name: log
|
|
||||||
mountPath: /var/log
|
|
||||||
{{- with .Values.controller.nodeSelector }}
|
{{- with .Values.controller.nodeSelector }}
|
||||||
nodeSelector:
|
nodeSelector:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
|
|||||||
332
helm/core/templates/daemonset.yaml
Normal file
332
helm/core/templates/daemonset.yaml
Normal file
@@ -0,0 +1,332 @@
|
|||||||
|
{{- if eq .Values.gateway.kind "DaemonSet" -}}
|
||||||
|
{{- $o11y := .Values.global.o11y }}
|
||||||
|
{{- $unprivilegedPortSupported := true }}
|
||||||
|
{{- range $index, $node := (lookup "v1" "Node" "default" "").items }}
|
||||||
|
{{- $kernelVersion := $node.status.nodeInfo.kernelVersion }}
|
||||||
|
{{- if $kernelVersion }}
|
||||||
|
{{- $kernelVersion = regexFind "^(\\d+\\.\\d+\\.\\d+)" $kernelVersion }}
|
||||||
|
{{- if and $kernelVersion (semverCompare "<4.11.0" $kernelVersion) }}
|
||||||
|
{{- $unprivilegedPortSupported = false }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end -}}
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: DaemonSet
|
||||||
|
metadata:
|
||||||
|
name: {{ include "gateway.name" . }}
|
||||||
|
namespace: {{ .Release.Namespace }}
|
||||||
|
labels:
|
||||||
|
{{- include "gateway.labels" . | nindent 4}}
|
||||||
|
annotations:
|
||||||
|
{{- .Values.gateway.annotations | toYaml | nindent 4 }}
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
{{- include "gateway.selectorLabels" . | nindent 6 }}
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
{{- if .Values.global.enableHigressIstio }}
|
||||||
|
"enableHigressIstio": "true"
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.gateway.podAnnotations }}
|
||||||
|
{{- toYaml .Values.gateway.podAnnotations | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
labels:
|
||||||
|
sidecar.istio.io/inject: "false"
|
||||||
|
{{- with .Values.gateway.revision }}
|
||||||
|
istio.io/rev: {{ . }}
|
||||||
|
{{- end }}
|
||||||
|
{{- include "gateway.selectorLabels" . | nindent 8 }}
|
||||||
|
spec:
|
||||||
|
{{- with .Values.gateway.imagePullSecrets }}
|
||||||
|
imagePullSecrets:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
serviceAccountName: {{ include "gateway.serviceAccountName" . }}
|
||||||
|
{{- if .Values.global.priorityClassName }}
|
||||||
|
priorityClassName: "{{ .Values.global.priorityClassName }}"
|
||||||
|
{{- end }}
|
||||||
|
securityContext:
|
||||||
|
{{- if .Values.gateway.securityContext }}
|
||||||
|
{{- toYaml .Values.gateway.securityContext | nindent 8 }}
|
||||||
|
{{- else if and $unprivilegedPortSupported (and (not .Values.gateway.hostNetwork) (semverCompare ">=1.22-0" .Capabilities.KubeVersion.GitVersion)) }}
|
||||||
|
# Safe since 1.22: https://github.com/kubernetes/kubernetes/pull/103326
|
||||||
|
sysctls:
|
||||||
|
- name: net.ipv4.ip_unprivileged_port_start
|
||||||
|
value: "0"
|
||||||
|
{{- end }}
|
||||||
|
containers:
|
||||||
|
{{- if $o11y.enabled }}
|
||||||
|
{{- $config := $o11y.promtail }}
|
||||||
|
- name: promtail
|
||||||
|
image: {{ $config.image.repository }}:{{ $config.image.tag }}
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
args:
|
||||||
|
- -config.file=/etc/promtail/promtail.yaml
|
||||||
|
env:
|
||||||
|
- name: 'HOSTNAME'
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: 'spec.nodeName'
|
||||||
|
ports:
|
||||||
|
- containerPort: {{ $config.port }}
|
||||||
|
name: http-metrics
|
||||||
|
protocol: TCP
|
||||||
|
readinessProbe:
|
||||||
|
failureThreshold: 3
|
||||||
|
httpGet:
|
||||||
|
path: /ready
|
||||||
|
port: {{ $config.port }}
|
||||||
|
scheme: HTTP
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 10
|
||||||
|
successThreshold: 1
|
||||||
|
timeoutSeconds: 1
|
||||||
|
volumeMounts:
|
||||||
|
- name: promtail-config
|
||||||
|
mountPath: "/etc/promtail"
|
||||||
|
- name: log
|
||||||
|
mountPath: /var/log/proxy
|
||||||
|
- name: tmp
|
||||||
|
mountPath: /tmp
|
||||||
|
{{- end }}
|
||||||
|
- name: higress-gateway
|
||||||
|
image: "{{ .Values.gateway.hub | default .Values.global.hub }}/{{ .Values.gateway.image | default "gateway" }}:{{ .Values.gateway.tag | default .Chart.AppVersion }}"
|
||||||
|
args:
|
||||||
|
- proxy
|
||||||
|
- router
|
||||||
|
- --domain
|
||||||
|
- $(POD_NAMESPACE).svc.cluster.local
|
||||||
|
- --proxyLogLevel=warning
|
||||||
|
- --proxyComponentLogLevel=misc:error
|
||||||
|
- --log_output_level=all:info
|
||||||
|
- --serviceCluster=higress-gateway
|
||||||
|
securityContext:
|
||||||
|
{{- if .Values.gateway.containerSecurityContext }}
|
||||||
|
{{- toYaml .Values.gateway.containerSecurityContext | nindent 12 }}
|
||||||
|
{{- else if and $unprivilegedPortSupported (and (not .Values.gateway.hostNetwork) (semverCompare ">=1.22-0" .Capabilities.KubeVersion.GitVersion)) }}
|
||||||
|
# Safe since 1.22: https://github.com/kubernetes/kubernetes/pull/103326
|
||||||
|
capabilities:
|
||||||
|
drop:
|
||||||
|
- ALL
|
||||||
|
allowPrivilegeEscalation: false
|
||||||
|
privileged: false
|
||||||
|
# When enabling lite metrics, the configuration template files need to be replaced.
|
||||||
|
{{- if not .Values.global.liteMetrics }}
|
||||||
|
readOnlyRootFilesystem: true
|
||||||
|
{{- end }}
|
||||||
|
runAsUser: 1337
|
||||||
|
runAsGroup: 1337
|
||||||
|
runAsNonRoot: true
|
||||||
|
{{- else }}
|
||||||
|
capabilities:
|
||||||
|
drop:
|
||||||
|
- ALL
|
||||||
|
add:
|
||||||
|
- NET_BIND_SERVICE
|
||||||
|
runAsUser: 0
|
||||||
|
runAsGroup: 1337
|
||||||
|
runAsNonRoot: false
|
||||||
|
allowPrivilegeEscalation: true
|
||||||
|
{{- end }}
|
||||||
|
env:
|
||||||
|
- name: NODE_NAME
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
apiVersion: v1
|
||||||
|
fieldPath: spec.nodeName
|
||||||
|
- name: POD_NAME
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
apiVersion: v1
|
||||||
|
fieldPath: metadata.name
|
||||||
|
- name: POD_NAMESPACE
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
apiVersion: v1
|
||||||
|
fieldPath: metadata.namespace
|
||||||
|
- name: INSTANCE_IP
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
apiVersion: v1
|
||||||
|
fieldPath: status.podIP
|
||||||
|
- name: HOST_IP
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
apiVersion: v1
|
||||||
|
fieldPath: status.hostIP
|
||||||
|
- name: SERVICE_ACCOUNT
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: spec.serviceAccountName
|
||||||
|
- name: PILOT_XDS_SEND_TIMEOUT
|
||||||
|
value: 60s
|
||||||
|
- name: PROXY_XDS_VIA_AGENT
|
||||||
|
value: "true"
|
||||||
|
- name: ENABLE_INGRESS_GATEWAY_SDS
|
||||||
|
value: "false"
|
||||||
|
- name: JWT_POLICY
|
||||||
|
value: {{ include "controller.jwtPolicy" . }}
|
||||||
|
- name: ISTIO_META_HTTP10
|
||||||
|
value: "1"
|
||||||
|
- name: ISTIO_META_CLUSTER_ID
|
||||||
|
value: "{{ $.Values.clusterName | default `Kubernetes` }}"
|
||||||
|
- name: INSTANCE_NAME
|
||||||
|
value: "higress-gateway"
|
||||||
|
{{- if .Values.global.liteMetrics }}
|
||||||
|
- name: LITE_METRICS
|
||||||
|
value: "on"
|
||||||
|
{{- end }}
|
||||||
|
{{- if include "skywalking.enabled" . }}
|
||||||
|
- name: ISTIO_BOOTSTRAP_OVERRIDE
|
||||||
|
value: /etc/istio/custom-bootstrap/custom_bootstrap.json
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.gateway.networkGateway }}
|
||||||
|
- name: ISTIO_META_REQUESTED_NETWORK_VIEW
|
||||||
|
value: "{{.}}"
|
||||||
|
{{- end }}
|
||||||
|
{{- range $key, $val := .Values.env }}
|
||||||
|
- name: {{ $key }}
|
||||||
|
value: {{ $val | quote }}
|
||||||
|
{{- end }}
|
||||||
|
ports:
|
||||||
|
- containerPort: 15090
|
||||||
|
protocol: TCP
|
||||||
|
name: http-envoy-prom
|
||||||
|
{{- if or .Values.global.local .Values.global.kind }}
|
||||||
|
- containerPort: {{ .Values.gateway.httpPort }}
|
||||||
|
hostPort: {{ .Values.gateway.httpPort }}
|
||||||
|
name: http
|
||||||
|
protocol: TCP
|
||||||
|
- containerPort: {{ .Values.gateway.httpsPort }}
|
||||||
|
hostPort: {{ .Values.gateway.httpsPort }}
|
||||||
|
name: https
|
||||||
|
protocol: TCP
|
||||||
|
{{- end }}
|
||||||
|
readinessProbe:
|
||||||
|
failureThreshold: {{ .Values.gateway.readinessFailureThreshold }}
|
||||||
|
httpGet:
|
||||||
|
path: /healthz/ready
|
||||||
|
port: 15021
|
||||||
|
scheme: HTTP
|
||||||
|
initialDelaySeconds: {{ .Values.gateway.readinessInitialDelaySeconds }}
|
||||||
|
periodSeconds: {{ .Values.gateway.readinessPeriodSeconds }}
|
||||||
|
successThreshold: {{ .Values.gateway.readinessSuccessThreshold }}
|
||||||
|
timeoutSeconds: {{ .Values.gateway.readinessTimeoutSeconds }}
|
||||||
|
{{- if not (or .Values.global.local .Values.global.kind) }}
|
||||||
|
resources:
|
||||||
|
{{- toYaml .Values.gateway.resources | nindent 12 }}
|
||||||
|
{{- end }}
|
||||||
|
volumeMounts:
|
||||||
|
{{- if eq (include "controller.jwtPolicy" .) "third-party-jwt" }}
|
||||||
|
- name: istio-token
|
||||||
|
mountPath: /var/run/secrets/tokens
|
||||||
|
readOnly: true
|
||||||
|
{{- end }}
|
||||||
|
- name: config
|
||||||
|
mountPath: /etc/istio/config
|
||||||
|
- name: istio-ca-root-cert
|
||||||
|
mountPath: /var/run/secrets/istio
|
||||||
|
- name: istio-data
|
||||||
|
mountPath: /var/lib/istio/data
|
||||||
|
- name: podinfo
|
||||||
|
mountPath: /etc/istio/pod
|
||||||
|
- name: proxy-socket
|
||||||
|
mountPath: /etc/istio/proxy
|
||||||
|
{{- 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 $o11y.enabled }}
|
||||||
|
- mountPath: /var/log/proxy
|
||||||
|
name: log
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.gateway.hostNetwork }}
|
||||||
|
hostNetwork: {{ .Values.gateway.hostNetwork }}
|
||||||
|
dnsPolicy: ClusterFirstWithHostNet
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.gateway.nodeSelector }}
|
||||||
|
nodeSelector:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.gateway.affinity }}
|
||||||
|
affinity:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.gateway.tolerations }}
|
||||||
|
tolerations:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
volumes:
|
||||||
|
{{- if eq (include "controller.jwtPolicy" .) "third-party-jwt" }}
|
||||||
|
- name: istio-token
|
||||||
|
projected:
|
||||||
|
sources:
|
||||||
|
- serviceAccountToken:
|
||||||
|
audience: istio-ca
|
||||||
|
expirationSeconds: 43200
|
||||||
|
path: istio-token
|
||||||
|
{{- end }}
|
||||||
|
- name: istio-ca-root-cert
|
||||||
|
configMap:
|
||||||
|
{{- if .Values.global.enableHigressIstio }}
|
||||||
|
name: istio-ca-root-cert
|
||||||
|
{{- else }}
|
||||||
|
name: higress-ca-root-cert
|
||||||
|
{{- end }}
|
||||||
|
- name: config
|
||||||
|
configMap:
|
||||||
|
name: higress-config
|
||||||
|
{{- if include "skywalking.enabled" . }}
|
||||||
|
- configMap:
|
||||||
|
defaultMode: 420
|
||||||
|
name: higress-custom-bootstrap
|
||||||
|
name: custom-bootstrap-volume
|
||||||
|
{{- end }}
|
||||||
|
- name: istio-data
|
||||||
|
emptyDir: {}
|
||||||
|
- name: proxy-socket
|
||||||
|
emptyDir: {}
|
||||||
|
{{- if $o11y.enabled }}
|
||||||
|
- name: log
|
||||||
|
emptyDir: {}
|
||||||
|
- name: tmp
|
||||||
|
emptyDir: {}
|
||||||
|
- name: promtail-config
|
||||||
|
configMap:
|
||||||
|
name: higress-promtail
|
||||||
|
{{- end }}
|
||||||
|
- name: podinfo
|
||||||
|
downwardAPI:
|
||||||
|
defaultMode: 420
|
||||||
|
items:
|
||||||
|
- fieldRef:
|
||||||
|
apiVersion: v1
|
||||||
|
fieldPath: metadata.labels
|
||||||
|
path: labels
|
||||||
|
- fieldRef:
|
||||||
|
apiVersion: v1
|
||||||
|
fieldPath: metadata.annotations
|
||||||
|
path: annotations
|
||||||
|
- path: cpu-request
|
||||||
|
resourceFieldRef:
|
||||||
|
containerName: higress-gateway
|
||||||
|
divisor: 1m
|
||||||
|
resource: requests.cpu
|
||||||
|
- path: cpu-limit
|
||||||
|
resourceFieldRef:
|
||||||
|
containerName: higress-gateway
|
||||||
|
divisor: 1m
|
||||||
|
resource: limits.cpu
|
||||||
|
{{- if .Values.global.volumeWasmPlugins }}
|
||||||
|
- name: local-wasmplugins-volume
|
||||||
|
hostPath:
|
||||||
|
path: /opt/plugins
|
||||||
|
type: Directory
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
{{- if eq .Values.gateway.kind "Deployment" -}}
|
||||||
{{- $o11y := .Values.global.o11y }}
|
{{- $o11y := .Values.global.o11y }}
|
||||||
{{- $unprivilegedPortSupported := true }}
|
{{- $unprivilegedPortSupported := true }}
|
||||||
{{- range $index, $node := (lookup "v1" "Node" "default" "").items }}
|
{{- range $index, $node := (lookup "v1" "Node" "default" "").items }}
|
||||||
@@ -58,6 +59,9 @@ spec:
|
|||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
serviceAccountName: {{ include "gateway.serviceAccountName" . }}
|
serviceAccountName: {{ include "gateway.serviceAccountName" . }}
|
||||||
|
{{- if .Values.global.priorityClassName }}
|
||||||
|
priorityClassName: "{{ .Values.global.priorityClassName }}"
|
||||||
|
{{- end }}
|
||||||
securityContext:
|
securityContext:
|
||||||
{{- if .Values.gateway.securityContext }}
|
{{- if .Values.gateway.securityContext }}
|
||||||
{{- toYaml .Values.gateway.securityContext | nindent 8 }}
|
{{- toYaml .Values.gateway.securityContext | nindent 8 }}
|
||||||
@@ -68,40 +72,6 @@ spec:
|
|||||||
value: "0"
|
value: "0"
|
||||||
{{- end }}
|
{{- end }}
|
||||||
containers:
|
containers:
|
||||||
{{- if $o11y.enabled }}
|
|
||||||
{{- $config := $o11y.promtail }}
|
|
||||||
- name: promtail
|
|
||||||
image: {{ $config.image.repository }}:{{ $config.image.tag }}
|
|
||||||
imagePullPolicy: IfNotPresent
|
|
||||||
args:
|
|
||||||
- -config.file=/etc/promtail/promtail.yaml
|
|
||||||
env:
|
|
||||||
- name: 'HOSTNAME'
|
|
||||||
valueFrom:
|
|
||||||
fieldRef:
|
|
||||||
fieldPath: 'spec.nodeName'
|
|
||||||
ports:
|
|
||||||
- containerPort: {{ $config.port }}
|
|
||||||
name: http-metrics
|
|
||||||
protocol: TCP
|
|
||||||
readinessProbe:
|
|
||||||
failureThreshold: 3
|
|
||||||
httpGet:
|
|
||||||
path: /ready
|
|
||||||
port: {{ $config.port }}
|
|
||||||
scheme: HTTP
|
|
||||||
initialDelaySeconds: 10
|
|
||||||
periodSeconds: 10
|
|
||||||
successThreshold: 1
|
|
||||||
timeoutSeconds: 1
|
|
||||||
volumeMounts:
|
|
||||||
- name: promtail-config
|
|
||||||
mountPath: "/etc/promtail"
|
|
||||||
- name: log
|
|
||||||
mountPath: /var/log/proxy
|
|
||||||
- name: tmp
|
|
||||||
mountPath: /tmp
|
|
||||||
{{- end }}
|
|
||||||
- name: higress-gateway
|
- name: higress-gateway
|
||||||
image: "{{ .Values.gateway.hub | default .Values.global.hub }}/{{ .Values.gateway.image | default "gateway" }}:{{ .Values.gateway.tag | default .Chart.AppVersion }}"
|
image: "{{ .Values.gateway.hub | default .Values.global.hub }}/{{ .Values.gateway.image | default "gateway" }}:{{ .Values.gateway.tag | default .Chart.AppVersion }}"
|
||||||
args:
|
args:
|
||||||
@@ -202,6 +172,9 @@ spec:
|
|||||||
value: {{ $val | quote }}
|
value: {{ $val | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
ports:
|
ports:
|
||||||
|
- containerPort: 15020
|
||||||
|
protocol: TCP
|
||||||
|
name: istio-prom
|
||||||
- containerPort: 15090
|
- containerPort: 15090
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
name: http-envoy-prom
|
name: http-envoy-prom
|
||||||
@@ -241,7 +214,7 @@ spec:
|
|||||||
mountPath: /var/run/secrets/istio
|
mountPath: /var/run/secrets/istio
|
||||||
- name: istio-data
|
- name: istio-data
|
||||||
mountPath: /var/lib/istio/data
|
mountPath: /var/lib/istio/data
|
||||||
- name: podinfo
|
- name: podinfo
|
||||||
mountPath: /etc/istio/pod
|
mountPath: /etc/istio/pod
|
||||||
- name: proxy-socket
|
- name: proxy-socket
|
||||||
mountPath: /etc/istio/proxy
|
mountPath: /etc/istio/proxy
|
||||||
@@ -257,6 +230,40 @@ spec:
|
|||||||
- mountPath: /var/log/proxy
|
- mountPath: /var/log/proxy
|
||||||
name: log
|
name: log
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{- if $o11y.enabled }}
|
||||||
|
{{- $config := $o11y.promtail }}
|
||||||
|
- name: promtail
|
||||||
|
image: {{ $config.image.repository }}:{{ $config.image.tag }}
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
args:
|
||||||
|
- -config.file=/etc/promtail/promtail.yaml
|
||||||
|
env:
|
||||||
|
- name: 'HOSTNAME'
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: 'spec.nodeName'
|
||||||
|
ports:
|
||||||
|
- containerPort: {{ $config.port }}
|
||||||
|
name: http-metrics
|
||||||
|
protocol: TCP
|
||||||
|
readinessProbe:
|
||||||
|
failureThreshold: 3
|
||||||
|
httpGet:
|
||||||
|
path: /ready
|
||||||
|
port: {{ $config.port }}
|
||||||
|
scheme: HTTP
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 10
|
||||||
|
successThreshold: 1
|
||||||
|
timeoutSeconds: 1
|
||||||
|
volumeMounts:
|
||||||
|
- name: promtail-config
|
||||||
|
mountPath: "/etc/promtail"
|
||||||
|
- name: log
|
||||||
|
mountPath: /var/log/proxy
|
||||||
|
- name: tmp
|
||||||
|
mountPath: /tmp
|
||||||
|
{{- end }}
|
||||||
{{- if .Values.gateway.hostNetwork }}
|
{{- if .Values.gateway.hostNetwork }}
|
||||||
hostNetwork: {{ .Values.gateway.hostNetwork }}
|
hostNetwork: {{ .Values.gateway.hostNetwork }}
|
||||||
dnsPolicy: ClusterFirstWithHostNet
|
dnsPolicy: ClusterFirstWithHostNet
|
||||||
@@ -340,3 +347,4 @@ spec:
|
|||||||
path: /opt/plugins
|
path: /opt/plugins
|
||||||
type: Directory
|
type: Directory
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|||||||
6
helm/core/templates/ingressclass.yaml
Normal file
6
helm/core/templates/ingressclass.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: IngressClass
|
||||||
|
metadata:
|
||||||
|
name: {{ .Values.global.ingressClass }}
|
||||||
|
spec:
|
||||||
|
controller: higress.io/higress-controller
|
||||||
@@ -15,6 +15,9 @@ spec:
|
|||||||
{{- with .Values.gateway.service.loadBalancerIP }}
|
{{- with .Values.gateway.service.loadBalancerIP }}
|
||||||
loadBalancerIP: "{{ . }}"
|
loadBalancerIP: "{{ . }}"
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{- with .Values.gateway.service.loadBalancerClass }}
|
||||||
|
loadBalancerClass: "{{ . }}"
|
||||||
|
{{- end }}
|
||||||
{{- with .Values.gateway.service.loadBalancerSourceRanges }}
|
{{- with .Values.gateway.service.loadBalancerSourceRanges }}
|
||||||
loadBalancerSourceRanges:
|
loadBalancerSourceRanges:
|
||||||
{{ toYaml . | indent 4 }}
|
{{ toYaml . | indent 4 }}
|
||||||
|
|||||||
@@ -343,7 +343,7 @@ global:
|
|||||||
enabled: false
|
enabled: false
|
||||||
promtail:
|
promtail:
|
||||||
image:
|
image:
|
||||||
repository: grafana/promtail
|
repository: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/promtail
|
||||||
tag: 2.9.4
|
tag: 2.9.4
|
||||||
port: 3101
|
port: 3101
|
||||||
resources:
|
resources:
|
||||||
@@ -396,6 +396,9 @@ gateway:
|
|||||||
replicas: 2
|
replicas: 2
|
||||||
image: gateway
|
image: gateway
|
||||||
|
|
||||||
|
# -- Use a `DaemonSet` or `Deployment`
|
||||||
|
kind: Deployment
|
||||||
|
|
||||||
# The number of successive failed probes before indicating readiness failure.
|
# The number of successive failed probes before indicating readiness failure.
|
||||||
readinessFailureThreshold: 30
|
readinessFailureThreshold: 30
|
||||||
|
|
||||||
@@ -468,6 +471,7 @@ gateway:
|
|||||||
targetPort: 443
|
targetPort: 443
|
||||||
annotations: {}
|
annotations: {}
|
||||||
loadBalancerIP: ""
|
loadBalancerIP: ""
|
||||||
|
loadBalancerClass: ""
|
||||||
loadBalancerSourceRanges: []
|
loadBalancerSourceRanges: []
|
||||||
externalTrafficPolicy: ""
|
externalTrafficPolicy: ""
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
- name: higress-core
|
- name: higress-core
|
||||||
repository: file://../core
|
repository: file://../core
|
||||||
version: 1.4.1
|
version: 1.4.2
|
||||||
- name: higress-console
|
- name: higress-console
|
||||||
repository: https://higress.io/helm-charts/
|
repository: https://higress.io/helm-charts/
|
||||||
version: 1.4.1
|
version: 1.4.2
|
||||||
digest: sha256:de41b8f771e869aef9b83d2334fea5d34492a1c5df37e5aaff383189877cba23
|
digest: sha256:31b557e55584e589b140ae9b89cfc8b99df91771c7d28465c3a2b06a4f35a192
|
||||||
generated: "2024-06-19T17:10:02.426994+08:00"
|
generated: "2024-07-26T13:53:23.225023+08:00"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
apiVersion: v2
|
apiVersion: v2
|
||||||
appVersion: 1.4.1
|
appVersion: 1.4.2
|
||||||
description: Helm chart for deploying Higress gateways
|
description: Helm chart for deploying Higress gateways
|
||||||
icon: https://higress.io/img/higress_logo_small.png
|
icon: https://higress.io/img/higress_logo_small.png
|
||||||
home: http://higress.io/
|
home: http://higress.io/
|
||||||
@@ -12,9 +12,9 @@ sources:
|
|||||||
dependencies:
|
dependencies:
|
||||||
- name: higress-core
|
- name: higress-core
|
||||||
repository: "file://../core"
|
repository: "file://../core"
|
||||||
version: 1.4.1
|
version: 1.4.2
|
||||||
- name: higress-console
|
- name: higress-console
|
||||||
repository: "https://higress.io/helm-charts/"
|
repository: "https://higress.io/helm-charts/"
|
||||||
version: 1.4.1
|
version: 1.4.2
|
||||||
type: application
|
type: application
|
||||||
version: 1.4.1
|
version: 1.4.2
|
||||||
|
|||||||
@@ -391,7 +391,7 @@ func (s *Server) initAutomaticHttps() error {
|
|||||||
ServerAddress: s.CertHttpAddress,
|
ServerAddress: s.CertHttpAddress,
|
||||||
Email: s.AutomaticHttpsEmail,
|
Email: s.AutomaticHttpsEmail,
|
||||||
}
|
}
|
||||||
certServer, err := cert.NewServer(s.kubeClient.Kube(), certOption)
|
certServer, err := cert.NewServer(s.kubeClient.Kube(), s.xdsServer, certOption)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,10 +17,15 @@ package cert
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/caddyserver/certmagic"
|
"github.com/caddyserver/certmagic"
|
||||||
"github.com/mholt/acmez"
|
"github.com/mholt/acmez"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
|
"istio.io/istio/pilot/pkg/model"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -28,6 +33,10 @@ const (
|
|||||||
EventCertObtained = "cert_obtained"
|
EventCertObtained = "cert_obtained"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
cfg *certmagic.Config
|
||||||
|
)
|
||||||
|
|
||||||
type CertMgr struct {
|
type CertMgr struct {
|
||||||
cfg *certmagic.Config
|
cfg *certmagic.Config
|
||||||
client kubernetes.Interface
|
client kubernetes.Interface
|
||||||
@@ -39,9 +48,10 @@ type CertMgr struct {
|
|||||||
ingressSolver acmez.Solver
|
ingressSolver acmez.Solver
|
||||||
configMgr *ConfigMgr
|
configMgr *ConfigMgr
|
||||||
secretMgr *SecretMgr
|
secretMgr *SecretMgr
|
||||||
|
XDSUpdater model.XDSUpdater
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitCertMgr(opts *Option, clientSet kubernetes.Interface, config *Config) (*CertMgr, error) {
|
func InitCertMgr(opts *Option, clientSet kubernetes.Interface, config *Config, XDSUpdater model.XDSUpdater, configMgr *ConfigMgr) (*CertMgr, error) {
|
||||||
CertLog.Infof("certmgr init config: %+v", config)
|
CertLog.Infof("certmgr init config: %+v", config)
|
||||||
// Init certmagic config
|
// Init certmagic config
|
||||||
// First make a pointer to a Cache as we need to reference the same Cache in
|
// First make a pointer to a Cache as we need to reference the same Cache in
|
||||||
@@ -49,21 +59,29 @@ func InitCertMgr(opts *Option, clientSet kubernetes.Interface, config *Config) (
|
|||||||
var cache *certmagic.Cache
|
var cache *certmagic.Cache
|
||||||
var storage certmagic.Storage
|
var storage certmagic.Storage
|
||||||
storage, _ = NewConfigmapStorage(opts.Namespace, clientSet)
|
storage, _ = NewConfigmapStorage(opts.Namespace, clientSet)
|
||||||
renewalWindowRatio := float64(config.RenewBeforeDays / RenewMaxDays)
|
renewalWindowRatio := float64(config.RenewBeforeDays) / float64(RenewMaxDays)
|
||||||
|
logger := zap.New(zapcore.NewCore(
|
||||||
|
zapcore.NewConsoleEncoder(zap.NewProductionEncoderConfig()),
|
||||||
|
os.Stderr,
|
||||||
|
zap.DebugLevel,
|
||||||
|
))
|
||||||
magicConfig := certmagic.Config{
|
magicConfig := certmagic.Config{
|
||||||
RenewalWindowRatio: renewalWindowRatio,
|
RenewalWindowRatio: renewalWindowRatio,
|
||||||
Storage: storage,
|
Storage: storage,
|
||||||
|
Logger: logger,
|
||||||
}
|
}
|
||||||
cache = certmagic.NewCache(certmagic.CacheOptions{
|
cache = certmagic.NewCache(certmagic.CacheOptions{
|
||||||
GetConfigForCert: func(cert certmagic.Certificate) (*certmagic.Config, error) {
|
GetConfigForCert: func(cert certmagic.Certificate) (*certmagic.Config, error) {
|
||||||
// Here we use New to get a valid Config associated with the same cache.
|
// Here we use New to get a valid Config associated with the same cache.
|
||||||
// The provided Config is used as a template and will be completed with
|
// The provided Config is used as a template and will be completed with
|
||||||
// any defaults that are set in the Default config.
|
// any defaults that are set in the Default config.
|
||||||
return certmagic.New(cache, magicConfig), nil
|
return cfg, nil
|
||||||
},
|
},
|
||||||
|
Logger: logger,
|
||||||
})
|
})
|
||||||
// init certmagic
|
// init certmagic
|
||||||
cfg := certmagic.New(cache, magicConfig)
|
cfg = certmagic.New(cache, magicConfig)
|
||||||
|
|
||||||
// Init certmagic acme
|
// Init certmagic acme
|
||||||
issuer := config.GetIssuer(IssuerTypeLetsencrypt)
|
issuer := config.GetIssuer(IssuerTypeLetsencrypt)
|
||||||
if issuer == nil {
|
if issuer == nil {
|
||||||
@@ -85,7 +103,6 @@ func InitCertMgr(opts *Option, clientSet kubernetes.Interface, config *Config) (
|
|||||||
// init issuers
|
// init issuers
|
||||||
cfg.Issuers = []certmagic.Issuer{myACME}
|
cfg.Issuers = []certmagic.Issuer{myACME}
|
||||||
|
|
||||||
configMgr, _ := NewConfigMgr(opts.Namespace, clientSet)
|
|
||||||
secretMgr, _ := NewSecretMgr(opts.Namespace, clientSet)
|
secretMgr, _ := NewSecretMgr(opts.Namespace, clientSet)
|
||||||
|
|
||||||
certMgr := &CertMgr{
|
certMgr := &CertMgr{
|
||||||
@@ -97,6 +114,7 @@ func InitCertMgr(opts *Option, clientSet kubernetes.Interface, config *Config) (
|
|||||||
configMgr: configMgr,
|
configMgr: configMgr,
|
||||||
secretMgr: secretMgr,
|
secretMgr: secretMgr,
|
||||||
cache: cache,
|
cache: cache,
|
||||||
|
XDSUpdater: XDSUpdater,
|
||||||
}
|
}
|
||||||
certMgr.cfg.OnEvent = certMgr.OnEvent
|
certMgr.cfg.OnEvent = certMgr.OnEvent
|
||||||
return certMgr, nil
|
return certMgr, nil
|
||||||
@@ -149,18 +167,31 @@ func (s *CertMgr) Reconcile(ctx context.Context, oldConfig *Config, newConfig *C
|
|||||||
// sync email
|
// sync email
|
||||||
s.myACME.Email = newIssuer.Email
|
s.myACME.Email = newIssuer.Email
|
||||||
// sync RenewalWindowRatio
|
// sync RenewalWindowRatio
|
||||||
s.cfg.RenewalWindowRatio = float64(newConfig.RenewBeforeDays / RenewMaxDays)
|
renewalWindowRatio := float64(newConfig.RenewBeforeDays) / float64(RenewMaxDays)
|
||||||
|
s.cfg.RenewalWindowRatio = renewalWindowRatio
|
||||||
// start cache
|
// start cache
|
||||||
s.cache.Start()
|
s.cache.Start()
|
||||||
// sync domains
|
// sync domains
|
||||||
s.manageSync(context.Background(), newDomains)
|
|
||||||
s.configMgr.SetConfig(newConfig)
|
s.configMgr.SetConfig(newConfig)
|
||||||
|
CertLog.Infof("certMgr start to manageSync domains:+v%", newDomains)
|
||||||
|
s.manageSync(context.Background(), newDomains)
|
||||||
|
CertLog.Infof("certMgr manageSync domains done")
|
||||||
} else {
|
} else {
|
||||||
// stop cache maintainAssets
|
// stop cache maintainAssets
|
||||||
s.cache.Stop()
|
s.cache.Stop()
|
||||||
s.configMgr.SetConfig(newConfig)
|
s.configMgr.SetConfig(newConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if oldConfig != nil && newConfig != nil {
|
||||||
|
if oldConfig.FallbackForInvalidSecret != newConfig.FallbackForInvalidSecret || !reflect.DeepEqual(oldConfig.CredentialConfig, newConfig.CredentialConfig) {
|
||||||
|
CertLog.Infof("ingress need to full push")
|
||||||
|
s.XDSUpdater.ConfigUpdate(&model.PushRequest{
|
||||||
|
Full: true,
|
||||||
|
Reason: []model.TriggerReason{"higress-https-updated"},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -86,22 +86,35 @@ func (c *Config) GetSecretNameByDomain(issuerName IssuerName, domain string) str
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ParseTLSSecret(tlsSecret string) (string, string) {
|
||||||
|
secrets := strings.Split(tlsSecret, "/")
|
||||||
|
switch len(secrets) {
|
||||||
|
case 1:
|
||||||
|
return "", tlsSecret
|
||||||
|
case 2:
|
||||||
|
return secrets[0], secrets[1]
|
||||||
|
}
|
||||||
|
return "", ""
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Config) Validate() error {
|
func (c *Config) Validate() error {
|
||||||
// check acmeIssuer
|
// check acmeIssuer
|
||||||
if len(c.ACMEIssuer) == 0 {
|
if c.AutomaticHttps {
|
||||||
return fmt.Errorf("acmeIssuer is empty")
|
if len(c.ACMEIssuer) == 0 {
|
||||||
}
|
return fmt.Errorf("no acmeIssuer configuration found when automaticHttps is enable")
|
||||||
for _, issuer := range c.ACMEIssuer {
|
}
|
||||||
switch issuer.Name {
|
for _, issuer := range c.ACMEIssuer {
|
||||||
case IssuerTypeLetsencrypt:
|
switch issuer.Name {
|
||||||
if issuer.Email == "" {
|
case IssuerTypeLetsencrypt:
|
||||||
return fmt.Errorf("acmeIssuer %s email is empty", issuer.Name)
|
if issuer.Email == "" {
|
||||||
|
return fmt.Errorf("acmeIssuer %s email is empty", issuer.Name)
|
||||||
|
}
|
||||||
|
if !ValidateEmail(issuer.Email) {
|
||||||
|
return fmt.Errorf("acmeIssuer %s email %s is invalid", issuer.Name, issuer.Email)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("acmeIssuer name %s is not supported", issuer.Name)
|
||||||
}
|
}
|
||||||
if !ValidateEmail(issuer.Email) {
|
|
||||||
return fmt.Errorf("acmeIssuer %s email %s is invalid", issuer.Name, issuer.Email)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("acmeIssuer name %s is not supported", issuer.Name)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// check credentialConfig
|
// check credentialConfig
|
||||||
@@ -111,14 +124,20 @@ func (c *Config) Validate() error {
|
|||||||
}
|
}
|
||||||
if credential.TLSSecret == "" {
|
if credential.TLSSecret == "" {
|
||||||
return fmt.Errorf("credentialConfig tlsSecret is empty")
|
return fmt.Errorf("credentialConfig tlsSecret is empty")
|
||||||
|
} else {
|
||||||
|
ns, secret := ParseTLSSecret(credential.TLSSecret)
|
||||||
|
if ns == "" && secret == "" {
|
||||||
|
return fmt.Errorf("credentialConfig tlsSecret %s is not supported", credential.TLSSecret)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if credential.TLSIssuer == IssuerTypeLetsencrypt {
|
if credential.TLSIssuer == IssuerTypeLetsencrypt {
|
||||||
if len(credential.Domains) > 1 {
|
if len(credential.Domains) > 1 {
|
||||||
return fmt.Errorf("credentialConfig tlsIssuer %s only support one domain", credential.TLSIssuer)
|
return fmt.Errorf("credentialConfig tlsIssuer %s only support one domain", credential.TLSIssuer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if credential.TLSIssuer != IssuerTypeLetsencrypt && len(credential.TLSIssuer) > 0 {
|
if credential.TLSIssuer != IssuerTypeLetsencrypt && len(credential.TLSIssuer) > 0 {
|
||||||
return fmt.Errorf("credential tls issuer %s is not support", credential.TLSIssuer)
|
return fmt.Errorf("credential tls issuer %s is not supported", credential.TLSIssuer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -120,3 +120,36 @@ func TestMatchSecretNameByDomain(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseTLSSecret(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
tlsSecret string
|
||||||
|
expectedNamespace string
|
||||||
|
expectedSecretName string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
tlsSecret: "example-com-tls",
|
||||||
|
expectedNamespace: "",
|
||||||
|
expectedSecretName: "example-com-tls",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
tlsSecret: "kube-system/example-com-tls",
|
||||||
|
expectedNamespace: "kube-system",
|
||||||
|
expectedSecretName: "example-com-tls",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tlsSecret: "kube-system/example-com/wildcard",
|
||||||
|
expectedNamespace: "",
|
||||||
|
expectedSecretName: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.tlsSecret, func(t *testing.T) {
|
||||||
|
resultNamespace, resultSecretName := ParseTLSSecret(tt.tlsSecret)
|
||||||
|
assert.Equal(t, tt.expectedNamespace, resultNamespace)
|
||||||
|
assert.Equal(t, tt.expectedSecretName, resultSecretName)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
@@ -27,10 +26,6 @@ import (
|
|||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
SecretNamePrefix = "higress-secret-"
|
|
||||||
)
|
|
||||||
|
|
||||||
type SecretMgr struct {
|
type SecretMgr struct {
|
||||||
client kubernetes.Interface
|
client kubernetes.Interface
|
||||||
namespace string
|
namespace string
|
||||||
@@ -46,13 +41,21 @@ func NewSecretMgr(namespace string, client kubernetes.Interface) (*SecretMgr, er
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *SecretMgr) Update(domain string, secretName string, privateKey []byte, certificate []byte, notBefore time.Time, notAfter time.Time, isRenew bool) error {
|
func (s *SecretMgr) Update(domain string, secretName string, privateKey []byte, certificate []byte, notBefore time.Time, notAfter time.Time, isRenew bool) error {
|
||||||
//secretName := s.getSecretName(domain)
|
CertLog.Infof("update secret, domain:%s, secretName:%s, notBefore:%v, notAfter:%v, isRenew:%t", domain, secretName, notBefore, notAfter, isRenew)
|
||||||
secret := s.constructSecret(domain, privateKey, certificate, notBefore, notAfter, isRenew)
|
name := secretName
|
||||||
_, err := s.client.CoreV1().Secrets(s.namespace).Get(context.Background(), secretName, metav1.GetOptions{})
|
namespace := s.namespace
|
||||||
|
namespaceP, secretP := ParseTLSSecret(secretName)
|
||||||
|
if namespaceP != "" {
|
||||||
|
namespace = namespaceP
|
||||||
|
name = secretP
|
||||||
|
}
|
||||||
|
|
||||||
|
secret := s.constructSecret(domain, name, namespace, privateKey, certificate, notBefore, notAfter, isRenew)
|
||||||
|
_, err := s.client.CoreV1().Secrets(namespace).Get(context.Background(), name, metav1.GetOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.IsNotFound(err) {
|
if errors.IsNotFound(err) {
|
||||||
// create secret
|
// create secret
|
||||||
_, err2 := s.client.CoreV1().Secrets(s.namespace).Create(context.Background(), secret, metav1.CreateOptions{})
|
_, err2 := s.client.CoreV1().Secrets(namespace).Create(context.Background(), secret, metav1.CreateOptions{})
|
||||||
return err2
|
return err2
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
@@ -61,7 +64,7 @@ func (s *SecretMgr) Update(domain string, secretName string, privateKey []byte,
|
|||||||
if _, ok := secret.Annotations["higress.io/cert-domain"]; !ok {
|
if _, ok := secret.Annotations["higress.io/cert-domain"]; !ok {
|
||||||
return fmt.Errorf("the secret name %s is not automatic https secret name for the domain:%s, please rename it in config", secretName, domain)
|
return fmt.Errorf("the secret name %s is not automatic https secret name for the domain:%s, please rename it in config", secretName, domain)
|
||||||
}
|
}
|
||||||
_, err1 := s.client.CoreV1().Secrets(s.namespace).Update(context.Background(), secret, metav1.UpdateOptions{})
|
_, err1 := s.client.CoreV1().Secrets(namespace).Update(context.Background(), secret, metav1.UpdateOptions{})
|
||||||
if err1 != nil {
|
if err1 != nil {
|
||||||
return err1
|
return err1
|
||||||
}
|
}
|
||||||
@@ -69,23 +72,13 @@ func (s *SecretMgr) Update(domain string, secretName string, privateKey []byte,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SecretMgr) Delete(domain string) error {
|
func (s *SecretMgr) constructSecret(domain string, name string, namespace string, privateKey []byte, certificate []byte, notBefore time.Time, notAfter time.Time, isRenew bool) *v1.Secret {
|
||||||
secretName := s.getSecretName(domain)
|
|
||||||
err := s.client.CoreV1().Secrets(s.namespace).Delete(context.Background(), secretName, metav1.DeleteOptions{})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SecretMgr) getSecretName(domain string) string {
|
|
||||||
return SecretNamePrefix + strings.ReplaceAll(strings.TrimSpace(domain), ".", "-")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SecretMgr) constructSecret(domain string, privateKey []byte, certificate []byte, notBefore time.Time, notAfter time.Time, isRenew bool) *v1.Secret {
|
|
||||||
secretName := s.getSecretName(domain)
|
|
||||||
annotationMap := make(map[string]string, 0)
|
annotationMap := make(map[string]string, 0)
|
||||||
annotationMap["higress.io/cert-domain"] = domain
|
annotationMap["higress.io/cert-domain"] = domain
|
||||||
annotationMap["higress.io/cert-notAfter"] = notAfter.Format("2006-01-02 15:04:05")
|
annotationMap["higress.io/cert-notAfter"] = notAfter.Format("2006-01-02 15:04:05")
|
||||||
annotationMap["higress.io/cert-notBefore"] = notBefore.Format("2006-01-02 15:04:05")
|
annotationMap["higress.io/cert-notBefore"] = notBefore.Format("2006-01-02 15:04:05")
|
||||||
annotationMap["higress.io/cert-renew"] = strconv.FormatBool(isRenew)
|
annotationMap["higress.io/cert-renew"] = strconv.FormatBool(isRenew)
|
||||||
|
annotationMap["higress.io/cert-source"] = string(IssuerTypeLetsencrypt)
|
||||||
if isRenew {
|
if isRenew {
|
||||||
annotationMap["higress.io/cert-renew-time"] = time.Now().Format("2006-01-02 15:04:05")
|
annotationMap["higress.io/cert-renew-time"] = time.Now().Format("2006-01-02 15:04:05")
|
||||||
}
|
}
|
||||||
@@ -97,8 +90,8 @@ func (s *SecretMgr) constructSecret(domain string, privateKey []byte, certificat
|
|||||||
dataMap["tls.crt"] = certificate
|
dataMap["tls.crt"] = certificate
|
||||||
secret := &v1.Secret{
|
secret := &v1.Secret{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: secretName,
|
Name: name,
|
||||||
Namespace: s.namespace,
|
Namespace: namespace,
|
||||||
Annotations: annotationMap,
|
Annotations: annotationMap,
|
||||||
},
|
},
|
||||||
Type: v1.SecretTypeTLS,
|
Type: v1.SecretTypeTLS,
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/caddyserver/certmagic"
|
"github.com/caddyserver/certmagic"
|
||||||
|
"istio.io/istio/pilot/pkg/model"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -37,12 +38,14 @@ type Server struct {
|
|||||||
clientSet kubernetes.Interface
|
clientSet kubernetes.Interface
|
||||||
controller *Controller
|
controller *Controller
|
||||||
certMgr *CertMgr
|
certMgr *CertMgr
|
||||||
|
XDSUpdater model.XDSUpdater
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServer(clientSet kubernetes.Interface, opts *Option) (*Server, error) {
|
func NewServer(clientSet kubernetes.Interface, XDSUpdater model.XDSUpdater, opts *Option) (*Server, error) {
|
||||||
server := &Server{
|
server := &Server{
|
||||||
clientSet: clientSet,
|
clientSet: clientSet,
|
||||||
opts: opts,
|
opts: opts,
|
||||||
|
XDSUpdater: XDSUpdater,
|
||||||
}
|
}
|
||||||
return server, nil
|
return server, nil
|
||||||
}
|
}
|
||||||
@@ -65,7 +68,7 @@ func (s *Server) InitServer() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// init certmgr
|
// init certmgr
|
||||||
certMgr, err := InitCertMgr(s.opts, s.clientSet, defaultConfig) // config and start
|
certMgr, err := InitCertMgr(s.opts, s.clientSet, defaultConfig, s.XDSUpdater, configMgr) // config and start
|
||||||
s.certMgr = certMgr
|
s.certMgr = certMgr
|
||||||
// init controller
|
// init controller
|
||||||
controller, err := NewController(s.clientSet, s.opts.Namespace, certMgr, configMgr)
|
controller, err := NewController(s.clientSet, s.opts.Namespace, certMgr, configMgr)
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CertificatesPrefix = "/certificates"
|
CertificatesPrefix = "certificates"
|
||||||
ConfigmapStoreCertficatesPrefix = "higress-cert-store-certificates-"
|
ConfigmapStoreCertficatesPrefix = "higress-cert-store-certificates-"
|
||||||
ConfigmapStoreDefaultName = "higress-cert-store-default"
|
ConfigmapStoreDefaultName = "higress-cert-store-default"
|
||||||
)
|
)
|
||||||
@@ -155,7 +155,7 @@ func (s *ConfigmapStorage) List(ctx context.Context, prefix string, recursive bo
|
|||||||
// Check if the prefix corresponds to a specific key
|
// Check if the prefix corresponds to a specific key
|
||||||
hashPrefix := fastHash([]byte(prefix))
|
hashPrefix := fastHash([]byte(prefix))
|
||||||
if strings.HasPrefix(prefix, CertificatesPrefix) {
|
if strings.HasPrefix(prefix, CertificatesPrefix) {
|
||||||
// If the prefix is "/certificates", get all ConfigMaps and traverse each one
|
// If the prefix is "certificates/", get all ConfigMaps and traverse each one
|
||||||
// List all ConfigMaps in the namespace with label higress.io/cert-https=true
|
// List all ConfigMaps in the namespace with label higress.io/cert-https=true
|
||||||
configmaps, err := s.client.CoreV1().ConfigMaps(s.namespace).List(ctx, metav1.ListOptions{FieldSelector: "metadata.annotations['higress.io/cert-https'] == 'true'"})
|
configmaps, err := s.client.CoreV1().ConfigMaps(s.namespace).List(ctx, metav1.ListOptions{FieldSelector: "metadata.annotations['higress.io/cert-https'] == 'true'"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -289,14 +289,29 @@ func (s *ConfigmapStorage) String() string {
|
|||||||
return "ConfigmapStorage"
|
return "ConfigmapStorage"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getConfigmapStoreNameByKey determines the storage name for a given key.
|
||||||
|
// It checks if the key starts with 'certificates/' and if so, the key pattern should match one of the following:
|
||||||
|
// 'certificates/<issuerKey>/<domain>/<domain>.json',
|
||||||
|
// 'certificates/<issuerKey>/<domain>/<domain>.crt',
|
||||||
|
// or 'certificates/<issuerKey>/<domain>/<domain>.key'.
|
||||||
|
// It then returns the corresponding ConfigMap name.
|
||||||
|
// If the key does not start with 'certificates/', it returns the default store name.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
//
|
||||||
|
// key - The configuration map key that needs to be mapped to a storage name.
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
//
|
||||||
|
// string - The calculated or default storage name based on the key.
|
||||||
func (s *ConfigmapStorage) getConfigmapStoreNameByKey(key string) string {
|
func (s *ConfigmapStorage) getConfigmapStoreNameByKey(key string) string {
|
||||||
parts := strings.SplitN(key, "/", 10)
|
if strings.HasPrefix(key, "certificates/") {
|
||||||
if len(parts) >= 4 && parts[1] == "certificates" {
|
parts := strings.Split(key, "/")
|
||||||
domain := strings.TrimSuffix(parts[3], ".crt")
|
if len(parts) >= 4 && parts[0] == "certificates" {
|
||||||
domain = strings.TrimSuffix(domain, ".key")
|
domain := parts[2]
|
||||||
domain = strings.TrimSuffix(domain, ".json")
|
issuerKey := parts[1]
|
||||||
issuerKey := parts[2]
|
return ConfigmapStoreCertficatesPrefix + fastHash([]byte(issuerKey+domain))
|
||||||
return ConfigmapStoreCertficatesPrefix + fastHash([]byte(issuerKey+domain))
|
}
|
||||||
}
|
}
|
||||||
return ConfigmapStoreDefaultName
|
return ConfigmapStoreDefaultName
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,22 +39,29 @@ func TestGetConfigmapStoreNameByKey(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "certificate crt",
|
name: "certificate crt",
|
||||||
key: "/certificates/issuerKey/domain.crt",
|
key: "certificates/issuerKey/domain/domain.crt",
|
||||||
expected: "higress-cert-store-certificates-" + fastHash([]byte("issuerKey"+"domain")),
|
expected: "higress-cert-store-certificates-" + fastHash([]byte("issuerKey"+"domain")),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "47.237.14.136.sslip.io crt",
|
||||||
|
key: "certificates/acme-v02.api.letsencrypt.org-directory/47.237.14.136.sslip.io/47.237.14.136.sslip.io.crt",
|
||||||
|
expected: "higress-cert-store-certificates-" + fastHash([]byte("acme-v02.api.letsencrypt.org-directory"+"47.237.14.136.sslip.io")),
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: "certificate meta",
|
name: "certificate meta",
|
||||||
key: "/certificates/issuerKey/domain.json",
|
key: "certificates/issuerKey/domain/domain.json",
|
||||||
expected: "higress-cert-store-certificates-" + fastHash([]byte("issuerKey"+"domain")),
|
expected: "higress-cert-store-certificates-" + fastHash([]byte("issuerKey"+"domain")),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "certificate key",
|
name: "certificate key",
|
||||||
key: "/certificates/issuerKey/domain.key",
|
key: "certificates/issuerKey/domain/domain.key",
|
||||||
expected: "higress-cert-store-certificates-" + fastHash([]byte("issuerKey"+"domain")),
|
expected: "higress-cert-store-certificates-" + fastHash([]byte("issuerKey"+"domain")),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "user key",
|
name: "user key",
|
||||||
key: "/users/hello/2",
|
key: "users/hello/2",
|
||||||
expected: "higress-cert-store-default",
|
expected: "higress-cert-store-default",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -82,7 +89,7 @@ func TestExists(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Store a test key
|
// Store a test key
|
||||||
testKey := "/certificates/issuer1/domain1.crt"
|
testKey := "certificates/issuer1/domain1/domain1.crt"
|
||||||
err = storage.Store(context.Background(), testKey, []byte("test-data"))
|
err = storage.Store(context.Background(), testKey, []byte("test-data"))
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
@@ -94,17 +101,17 @@ func TestExists(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Existing Key",
|
name: "Existing Key",
|
||||||
key: "/certificates/issuer1/domain1.crt",
|
key: "certificates/issuer1/domain1/domain1.crt",
|
||||||
shouldExist: true,
|
shouldExist: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Non-Existent Key1",
|
name: "Non-Existent Key1",
|
||||||
key: "/certificates/issuer2/domain2.crt",
|
key: "certificates/issuer2/domain2/domain2.crt",
|
||||||
shouldExist: false,
|
shouldExist: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Non-Existent Key2",
|
name: "Non-Existent Key2",
|
||||||
key: "/users/hello/a",
|
key: "users/hello/a",
|
||||||
shouldExist: false,
|
shouldExist: false,
|
||||||
},
|
},
|
||||||
// Add more test cases as needed
|
// Add more test cases as needed
|
||||||
@@ -129,7 +136,7 @@ func TestLoad(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Store a test key
|
// Store a test key
|
||||||
testKey := "/certificates/issuer1/domain1.crt"
|
testKey := "certificates/issuer1/domain1/domain1.crt"
|
||||||
testValue := []byte("test-data")
|
testValue := []byte("test-data")
|
||||||
err = storage.Store(context.Background(), testKey, testValue)
|
err = storage.Store(context.Background(), testKey, testValue)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@@ -143,13 +150,13 @@ func TestLoad(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Existing Key",
|
name: "Existing Key",
|
||||||
key: "/certificates/issuer1/domain1.crt",
|
key: "certificates/issuer1/domain1/domain1.crt",
|
||||||
expected: testValue,
|
expected: testValue,
|
||||||
shouldError: false,
|
shouldError: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Non-Existent Key",
|
name: "Non-Existent Key",
|
||||||
key: "/certificates/issuer2/domain2.crt",
|
key: "certificates/issuer2/domain2/domain2.crt",
|
||||||
expected: nil,
|
expected: nil,
|
||||||
shouldError: true,
|
shouldError: true,
|
||||||
},
|
},
|
||||||
@@ -192,28 +199,28 @@ func TestStore(t *testing.T) {
|
|||||||
shouldError bool
|
shouldError bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Store Key with /certificates prefix",
|
name: "Store Key with certificates prefix",
|
||||||
key: "/certificates/issuer1/domain1.crt",
|
key: "certificates/issuer1/domain1/domain1.crt",
|
||||||
value: []byte("test-data1"),
|
value: []byte("test-data1"),
|
||||||
expected: map[string]string{fastHash([]byte("/certificates/issuer1/domain1.crt")): `{"k":"/certificates/issuer1/domain1.crt","v":"dGVzdC1kYXRhMQ=="}`},
|
expected: map[string]string{fastHash([]byte("certificates/issuer1/domain1/domain1.crt")): `{"k":"certificates/issuer1/domain1/domain1.crt","v":"dGVzdC1kYXRhMQ=="}`},
|
||||||
expectedConfigmapName: "higress-cert-store-certificates-" + fastHash([]byte("issuer1"+"domain1")),
|
expectedConfigmapName: "higress-cert-store-certificates-" + fastHash([]byte("issuer1"+"domain1")),
|
||||||
shouldError: false,
|
shouldError: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Store Key with /certificates prefix (additional data)",
|
name: "Store Key with certificates prefix (additional data)",
|
||||||
key: "/certificates/issuer2/domain2.crt",
|
key: "certificates/issuer2/domain2/domain2.crt",
|
||||||
value: []byte("test-data2"),
|
value: []byte("test-data2"),
|
||||||
expected: map[string]string{
|
expected: map[string]string{
|
||||||
fastHash([]byte("/certificates/issuer2/domain2.crt")): `{"k":"/certificates/issuer2/domain2.crt","v":"dGVzdC1kYXRhMg=="}`,
|
fastHash([]byte("certificates/issuer2/domain2/domain2.crt")): `{"k":"certificates/issuer2/domain2/domain2.crt","v":"dGVzdC1kYXRhMg=="}`,
|
||||||
},
|
},
|
||||||
expectedConfigmapName: "higress-cert-store-certificates-" + fastHash([]byte("issuer2"+"domain2")),
|
expectedConfigmapName: "higress-cert-store-certificates-" + fastHash([]byte("issuer2"+"domain2")),
|
||||||
shouldError: false,
|
shouldError: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Store Key without /certificates prefix",
|
name: "Store Key without certificates prefix",
|
||||||
key: "/other/path/data.txt",
|
key: "other/path/data.txt",
|
||||||
value: []byte("test-data3"),
|
value: []byte("test-data3"),
|
||||||
expected: map[string]string{fastHash([]byte("/other/path/data.txt")): `{"k":"/other/path/data.txt","v":"dGVzdC1kYXRhMw=="}`},
|
expected: map[string]string{fastHash([]byte("other/path/data.txt")): `{"k":"other/path/data.txt","v":"dGVzdC1kYXRhMw=="}`},
|
||||||
expectedConfigmapName: "higress-cert-store-default",
|
expectedConfigmapName: "higress-cert-store-default",
|
||||||
shouldError: false,
|
shouldError: false,
|
||||||
},
|
},
|
||||||
@@ -256,17 +263,17 @@ func TestList(t *testing.T) {
|
|||||||
// Store some test data
|
// Store some test data
|
||||||
// Store some test data
|
// Store some test data
|
||||||
testKeys := []string{
|
testKeys := []string{
|
||||||
"/certificates/issuer1/domain1.crt",
|
"certificates/issuer1/domain1/domain1.crt",
|
||||||
"/certificates/issuer1/domain2.crt",
|
"certificates/issuer1/domain2/domain2.crt",
|
||||||
"/certificates/issuer1/domain3.crt", // Added another domain for issuer1
|
"certificates/issuer1/domain3/domain3.crt", // Added another domain for issuer1
|
||||||
"/certificates/issuer2/domain4.crt",
|
"certificates/issuer2/domain4/domain4.crt",
|
||||||
"/certificates/issuer2/domain5.crt",
|
"certificates/issuer2/domain5/domain5.crt",
|
||||||
"/certificates/issuer3/subdomain1/domain6.crt", // Two-level subdirectory under issuer3
|
"certificates/issuer3/domain6/domain6.crt", // Two-level subdirectory under issuer3
|
||||||
"/certificates/issuer3/subdomain1/subdomain2/domain7.crt", // Two more levels under issuer3
|
"certificates/issuer3/subdomain1/subdomain2/domain7.crt", // Two more levels under issuer3
|
||||||
"/other-prefix/key1/file1",
|
"other-prefix/key1/file1",
|
||||||
"/other-prefix/key1/file2",
|
"other-prefix/key1/file2",
|
||||||
"/other-prefix/key2/file3",
|
"other-prefix/key2/file3",
|
||||||
"/other-prefix/key2/file4",
|
"other-prefix/key2/file4",
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, key := range testKeys {
|
for _, key := range testKeys {
|
||||||
@@ -283,34 +290,34 @@ func TestList(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "List Certificates (Non-Recursive)",
|
name: "List Certificates (Non-Recursive)",
|
||||||
prefix: "/certificates",
|
prefix: "certificates",
|
||||||
recursive: false,
|
recursive: false,
|
||||||
expected: []string{"/certificates/issuer1", "/certificates/issuer2", "/certificates/issuer3"},
|
expected: []string{"certificates/issuer1", "certificates/issuer2", "certificates/issuer3"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "List Certificates (Recursive)",
|
name: "List Certificates (Recursive)",
|
||||||
prefix: "/certificates",
|
prefix: "certificates",
|
||||||
recursive: true,
|
recursive: true,
|
||||||
expected: []string{"/certificates/issuer1/domain1.crt", "/certificates/issuer1/domain2.crt", "/certificates/issuer1/domain3.crt", "/certificates/issuer2/domain4.crt", "/certificates/issuer2/domain5.crt", "/certificates/issuer3/subdomain1/domain6.crt", "/certificates/issuer3/subdomain1/subdomain2/domain7.crt"},
|
expected: []string{"certificates/issuer1/domain1/domain1.crt", "certificates/issuer1/domain2/domain2.crt", "certificates/issuer1/domain3/domain3.crt", "certificates/issuer2/domain4/domain4.crt", "certificates/issuer2/domain5/domain5.crt", "certificates/issuer3/domain6/domain6.crt", "certificates/issuer3/subdomain1/subdomain2/domain7.crt"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "List Other Prefix (Non-Recursive)",
|
name: "List Other Prefix (Non-Recursive)",
|
||||||
prefix: "/other-prefix",
|
prefix: "other-prefix",
|
||||||
recursive: false,
|
recursive: false,
|
||||||
expected: []string{"/other-prefix/key1", "/other-prefix/key2"},
|
expected: []string{"other-prefix/key1", "other-prefix/key2"},
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: "List Other Prefix (Non-Recursive)",
|
name: "List Other Prefix (Non-Recursive)",
|
||||||
prefix: "/other-prefix/key1",
|
prefix: "other-prefix/key1",
|
||||||
recursive: false,
|
recursive: false,
|
||||||
expected: []string{"/other-prefix/key1/file1", "/other-prefix/key1/file2"},
|
expected: []string{"other-prefix/key1/file1", "other-prefix/key1/file2"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "List Other Prefix (Recursive)",
|
name: "List Other Prefix (Recursive)",
|
||||||
prefix: "/other-prefix",
|
prefix: "other-prefix",
|
||||||
recursive: true,
|
recursive: true,
|
||||||
expected: []string{"/other-prefix/key1/file1", "/other-prefix/key1/file2", "/other-prefix/key2/file3", "/other-prefix/key2/file4"},
|
expected: []string{"other-prefix/key1/file1", "other-prefix/key1/file2", "other-prefix/key2/file3", "other-prefix/key2/file4"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -425,7 +425,7 @@ func openCommand(writer io.Writer, command string, args ...string) {
|
|||||||
_, err := exec.LookPath(command)
|
_, err := exec.LookPath(command)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, exec.ErrNotFound) {
|
if errors.Is(err, exec.ErrNotFound) {
|
||||||
fmt.Fprintf(writer, "Could not open your browser. Please open it maually.\n")
|
fmt.Fprintf(writer, "Could not open your browser. Please open it manually.\n")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Fprintf(writer, "Failed to open browser; open %s in your browser.\nError: %s\n", args[0], err.Error())
|
fmt.Fprintf(writer, "Failed to open browser; open %s in your browser.\nError: %s\n", args[0], err.Error())
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
setFlagHelpStr = `Override an higress profile value, e.g. to choose a profile
|
setFlagHelpStr = `Override an higress profile value, e.g. to choose a profile
|
||||||
(--set profile=local-k8s), or override profile values (--set gateway.replicas=2), or override helm values (--set values.global.proxy.resources.requsts.cpu=500m).`
|
(--set profile=local-k8s), or override profile values (--set gateway.replicas=2), or override helm values (--set values.global.proxy.resources.requests.cpu=500m).`
|
||||||
// manifestsFlagHelpStr is the command line description for --manifests
|
// manifestsFlagHelpStr is the command line description for --manifests
|
||||||
manifestsFlagHelpStr = `Specify a path to a directory of profiles
|
manifestsFlagHelpStr = `Specify a path to a directory of profiles
|
||||||
(e.g. ~/Downloads/higress/manifests).`
|
(e.g. ~/Downloads/higress/manifests).`
|
||||||
@@ -101,7 +101,7 @@ func newInstallCmd() *cobra.Command {
|
|||||||
hgctl install --set profile=local-k8s --set global.enableIstioAPI=true --set gateway.replicas=2"
|
hgctl install --set profile=local-k8s --set global.enableIstioAPI=true --set gateway.replicas=2"
|
||||||
|
|
||||||
# To override helm setting
|
# To override helm setting
|
||||||
hgctl install --set profile=local-k8s --set values.global.proxy.resources.requsts.cpu=500m"
|
hgctl install --set profile=local-k8s --set values.global.proxy.resources.requests.cpu=500m"
|
||||||
|
|
||||||
|
|
||||||
`,
|
`,
|
||||||
@@ -175,7 +175,7 @@ func promptInstall(writer io.Writer, profileName string) bool {
|
|||||||
|
|
||||||
func promptProfileName(writer io.Writer) string {
|
func promptProfileName(writer io.Writer) string {
|
||||||
answer := ""
|
answer := ""
|
||||||
fmt.Fprintf(writer, "\nPlease select higress install configration profile:\n")
|
fmt.Fprintf(writer, "\nPlease select higress install configuration profile:\n")
|
||||||
fmt.Fprintf(writer, "\n1.Install higress to local kubernetes cluster like kind etc.\n")
|
fmt.Fprintf(writer, "\n1.Install higress to local kubernetes cluster like kind etc.\n")
|
||||||
fmt.Fprintf(writer, "\n2.Install higress to kubernetes cluster\n")
|
fmt.Fprintf(writer, "\n2.Install higress to kubernetes cluster\n")
|
||||||
fmt.Fprintf(writer, "\n3.Install higress to local docker environment\n")
|
fmt.Fprintf(writer, "\n3.Install higress to local docker environment\n")
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ func (a *Agent) checkSudoPermission() error {
|
|||||||
case <-time.After(5 * time.Second):
|
case <-time.After(5 * time.Second):
|
||||||
cmd2.Process.Signal(os.Interrupt)
|
cmd2.Process.Signal(os.Interrupt)
|
||||||
if !a.quiet {
|
if !a.quiet {
|
||||||
fmt.Fprintf(a.writer, "checked result: timeout execeed and need sudo with password\n")
|
fmt.Fprintf(a.writer, "checked result: timeout exceed and need sudo with password\n")
|
||||||
}
|
}
|
||||||
a.runSudoState = SudoWithPassword
|
a.runSudoState = SudoWithPassword
|
||||||
|
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ func upgrade(writer io.Writer, iArgs *InstallArgs) error {
|
|||||||
func promptUpgrade(writer io.Writer) bool {
|
func promptUpgrade(writer io.Writer) bool {
|
||||||
answer := ""
|
answer := ""
|
||||||
for {
|
for {
|
||||||
fmt.Fprintf(writer, "All Higress resources will be upgraed from the cluster. \nProceed? (y/N)")
|
fmt.Fprintf(writer, "All Higress resources will be upgrade from the cluster. \nProceed? (y/N)")
|
||||||
fmt.Scanln(&answer)
|
fmt.Scanln(&answer)
|
||||||
if strings.TrimSpace(answer) == "y" {
|
if strings.TrimSpace(answer) == "y" {
|
||||||
fmt.Fprintf(writer, "\n")
|
fmt.Fprintf(writer, "\n")
|
||||||
@@ -170,7 +170,7 @@ func promptProfileContexts(writer io.Writer, profileContexts []*installer.Profil
|
|||||||
if len(profileContexts) == 1 {
|
if len(profileContexts) == 1 {
|
||||||
fmt.Fprintf(writer, "\nFound a profile:: ")
|
fmt.Fprintf(writer, "\nFound a profile:: ")
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(writer, "\nPlease select higress installed configration profiles:\n")
|
fmt.Fprintf(writer, "\nPlease select higress installed configuration profiles:\n")
|
||||||
}
|
}
|
||||||
index := 1
|
index := 1
|
||||||
for _, profileContext := range profileContexts {
|
for _, profileContext := range profileContexts {
|
||||||
|
|||||||
@@ -918,7 +918,7 @@ func (m *IngressConfig) AddOrUpdateWasmPlugin(clusterNamespacedName util.Cluster
|
|||||||
Labels: map[string]string{constants.AlwaysPushLabel: "true"},
|
Labels: map[string]string{constants.AlwaysPushLabel: "true"},
|
||||||
}
|
}
|
||||||
for _, f := range m.wasmPluginHandlers {
|
for _, f := range m.wasmPluginHandlers {
|
||||||
IngressLog.Debug("WasmPlugin triggerd update")
|
IngressLog.Debug("WasmPlugin triggered update")
|
||||||
f(config.Config{Meta: metadata}, config.Config{Meta: metadata}, model.EventUpdate)
|
f(config.Config{Meta: metadata}, config.Config{Meta: metadata}, model.EventUpdate)
|
||||||
}
|
}
|
||||||
istioWasmPlugin, err := m.convertIstioWasmPlugin(&wasmPlugin.Spec)
|
istioWasmPlugin, err := m.convertIstioWasmPlugin(&wasmPlugin.Spec)
|
||||||
@@ -960,7 +960,7 @@ func (m *IngressConfig) DeleteWasmPlugin(clusterNamespacedName util.ClusterNames
|
|||||||
Labels: map[string]string{constants.AlwaysPushLabel: "true"},
|
Labels: map[string]string{constants.AlwaysPushLabel: "true"},
|
||||||
}
|
}
|
||||||
for _, f := range m.wasmPluginHandlers {
|
for _, f := range m.wasmPluginHandlers {
|
||||||
IngressLog.Debug("WasmPlugin triggerd update")
|
IngressLog.Debug("WasmPlugin triggered update")
|
||||||
f(config.Config{Meta: metadata}, config.Config{Meta: metadata}, model.EventDelete)
|
f(config.Config{Meta: metadata}, config.Config{Meta: metadata}, model.EventDelete)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -987,7 +987,7 @@ func (m *IngressConfig) AddOrUpdateMcpBridge(clusterNamespacedName util.ClusterN
|
|||||||
Labels: map[string]string{constants.AlwaysPushLabel: "true"},
|
Labels: map[string]string{constants.AlwaysPushLabel: "true"},
|
||||||
}
|
}
|
||||||
for _, f := range m.serviceEntryHandlers {
|
for _, f := range m.serviceEntryHandlers {
|
||||||
IngressLog.Debug("McpBridge triggerd serviceEntry update")
|
IngressLog.Debug("McpBridge triggered serviceEntry update")
|
||||||
f(config.Config{Meta: metadata}, config.Config{Meta: metadata}, model.EventUpdate)
|
f(config.Config{Meta: metadata}, config.Config{Meta: metadata}, model.EventUpdate)
|
||||||
}
|
}
|
||||||
}, m.localKubeClient, m.namespace)
|
}, m.localKubeClient, m.namespace)
|
||||||
@@ -1042,7 +1042,7 @@ func (m *IngressConfig) AddOrUpdateHttp2Rpc(clusterNamespacedName util.ClusterNa
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *IngressConfig) DeleteHttp2Rpc(clusterNamespacedName util.ClusterNamespacedName) {
|
func (m *IngressConfig) DeleteHttp2Rpc(clusterNamespacedName util.ClusterNamespacedName) {
|
||||||
IngressLog.Infof("Http2Rpc triggerd deleted event %s", clusterNamespacedName.Name)
|
IngressLog.Infof("Http2Rpc triggered deleted event %s", clusterNamespacedName.Name)
|
||||||
if clusterNamespacedName.Namespace != m.namespace {
|
if clusterNamespacedName.Namespace != m.namespace {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -1054,7 +1054,7 @@ func (m *IngressConfig) DeleteHttp2Rpc(clusterNamespacedName util.ClusterNamespa
|
|||||||
}
|
}
|
||||||
m.mutex.Unlock()
|
m.mutex.Unlock()
|
||||||
if hit {
|
if hit {
|
||||||
IngressLog.Infof("Http2Rpc triggerd deleted event executed %s", clusterNamespacedName.Name)
|
IngressLog.Infof("Http2Rpc triggered deleted event executed %s", clusterNamespacedName.Name)
|
||||||
push := func(kind config.GroupVersionKind) {
|
push := func(kind config.GroupVersionKind) {
|
||||||
m.XDSUpdater.ConfigUpdate(&model.PushRequest{
|
m.XDSUpdater.ConfigUpdate(&model.PushRequest{
|
||||||
Full: true,
|
Full: true,
|
||||||
@@ -1160,13 +1160,13 @@ func (m *IngressConfig) constructHttp2RpcEnvoyFilter(http2rpcConfig *annotations
|
|||||||
IngressLog.Infof("Found http2rpc mappings %v", mappings)
|
IngressLog.Infof("Found http2rpc mappings %v", mappings)
|
||||||
if _, exist := mappings[http2rpcConfig.Name]; !exist {
|
if _, exist := mappings[http2rpcConfig.Name]; !exist {
|
||||||
IngressLog.Errorf("Http2RpcConfig name %s, not found Http2Rpc CRD", http2rpcConfig.Name)
|
IngressLog.Errorf("Http2RpcConfig name %s, not found Http2Rpc CRD", http2rpcConfig.Name)
|
||||||
return nil, errors.New("invalid http2rpcConfig has no useable http2rpc")
|
return nil, errors.New("invalid http2rpcConfig has no usable http2rpc")
|
||||||
}
|
}
|
||||||
http2rpcCRD := mappings[http2rpcConfig.Name]
|
http2rpcCRD := mappings[http2rpcConfig.Name]
|
||||||
|
|
||||||
if http2rpcCRD.GetDubbo() == nil {
|
if http2rpcCRD.GetDubbo() == nil {
|
||||||
IngressLog.Errorf("Http2RpcConfig name %s, only support Http2Rpc CRD Dubbo Service type", http2rpcConfig.Name)
|
IngressLog.Errorf("Http2RpcConfig name %s, only support Http2Rpc CRD Dubbo Service type", http2rpcConfig.Name)
|
||||||
return nil, errors.New("invalid http2rpcConfig has no useable http2rpc")
|
return nil, errors.New("invalid http2rpcConfig has no usable http2rpc")
|
||||||
}
|
}
|
||||||
|
|
||||||
httpRoute := route.HTTPRoute
|
httpRoute := route.HTTPRoute
|
||||||
@@ -1293,7 +1293,7 @@ func (m *IngressConfig) constructHttp2RpcMethods(dubbo *higressv1.DubboService)
|
|||||||
var method = make(map[string]interface{})
|
var method = make(map[string]interface{})
|
||||||
method["name"] = serviceMethod.GetServiceMethod()
|
method["name"] = serviceMethod.GetServiceMethod()
|
||||||
var params []interface{}
|
var params []interface{}
|
||||||
// paramFromEntireBody is for methods with single parameter. So when paramFromEntireBody exists, we just ignore parmas.
|
// paramFromEntireBody is for methods with single parameter. So when paramFromEntireBody exists, we just ignore params.
|
||||||
var paramFromEntireBody = serviceMethod.GetParamFromEntireBody()
|
var paramFromEntireBody = serviceMethod.GetParamFromEntireBody()
|
||||||
if paramFromEntireBody != nil {
|
if paramFromEntireBody != nil {
|
||||||
var param = make(map[string]interface{})
|
var param = make(map[string]interface{})
|
||||||
|
|||||||
@@ -433,6 +433,11 @@ func (c *controller) ConvertGateway(convertOptions *common.ConvertOptions, wrapp
|
|||||||
// If there is no matching secret, try to get it from configmap.
|
// If there is no matching secret, try to get it from configmap.
|
||||||
secretName = httpsCredentialConfig.MatchSecretNameByDomain(rule.Host)
|
secretName = httpsCredentialConfig.MatchSecretNameByDomain(rule.Host)
|
||||||
secretNamespace = c.options.SystemNamespace
|
secretNamespace = c.options.SystemNamespace
|
||||||
|
namespace, secret := cert.ParseTLSSecret(secretName)
|
||||||
|
if namespace != "" {
|
||||||
|
secretNamespace = namespace
|
||||||
|
secretName = secret
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -441,6 +446,11 @@ func (c *controller) ConvertGateway(convertOptions *common.ConvertOptions, wrapp
|
|||||||
if httpsCredentialConfig != nil {
|
if httpsCredentialConfig != nil {
|
||||||
secretName = httpsCredentialConfig.MatchSecretNameByDomain(rule.Host)
|
secretName = httpsCredentialConfig.MatchSecretNameByDomain(rule.Host)
|
||||||
secretNamespace = c.options.SystemNamespace
|
secretNamespace = c.options.SystemNamespace
|
||||||
|
namespace, secret := cert.ParseTLSSecret(secretName)
|
||||||
|
if namespace != "" {
|
||||||
|
secretNamespace = namespace
|
||||||
|
secretName = secret
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if secretName == "" {
|
if secretName == "" {
|
||||||
|
|||||||
@@ -419,6 +419,11 @@ func (c *controller) ConvertGateway(convertOptions *common.ConvertOptions, wrapp
|
|||||||
// If there is no matching secret, try to get it from configmap.
|
// If there is no matching secret, try to get it from configmap.
|
||||||
secretName = httpsCredentialConfig.MatchSecretNameByDomain(rule.Host)
|
secretName = httpsCredentialConfig.MatchSecretNameByDomain(rule.Host)
|
||||||
secretNamespace = c.options.SystemNamespace
|
secretNamespace = c.options.SystemNamespace
|
||||||
|
namespace, secret := cert.ParseTLSSecret(secretName)
|
||||||
|
if namespace != "" {
|
||||||
|
secretNamespace = namespace
|
||||||
|
secretName = secret
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -427,6 +432,11 @@ func (c *controller) ConvertGateway(convertOptions *common.ConvertOptions, wrapp
|
|||||||
if httpsCredentialConfig != nil {
|
if httpsCredentialConfig != nil {
|
||||||
secretName = httpsCredentialConfig.MatchSecretNameByDomain(rule.Host)
|
secretName = httpsCredentialConfig.MatchSecretNameByDomain(rule.Host)
|
||||||
secretNamespace = c.options.SystemNamespace
|
secretNamespace = c.options.SystemNamespace
|
||||||
|
namespace, secret := cert.ParseTLSSecret(secretName)
|
||||||
|
if namespace != "" {
|
||||||
|
secretNamespace = namespace
|
||||||
|
secretName = secret
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
1
plugins/wasm-go/extensions/ai-cache/.buildrc
Normal file
1
plugins/wasm-go/extensions/ai-cache/.buildrc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
EXTRA_TAGS=proxy_wasm_version_0_2_100
|
||||||
@@ -8,7 +8,7 @@ replace github.com/alibaba/higress/plugins/wasm-go => ../..
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/alibaba/higress/plugins/wasm-go v1.3.6-0.20240528060522-53bccf89f441
|
github.com/alibaba/higress/plugins/wasm-go v1.3.6-0.20240528060522-53bccf89f441
|
||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240711023527-ba358c48772f
|
||||||
github.com/tidwall/gjson v1.14.3
|
github.com/tidwall/gjson v1.14.3
|
||||||
github.com/tidwall/resp v0.1.1
|
github.com/tidwall/resp v0.1.1
|
||||||
github.com/tidwall/sjson v1.2.5
|
github.com/tidwall/sjson v1.2.5
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520 h1:IHDghbG
|
|||||||
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520/go.mod h1:Nz8ORLaFiLWotg6GeKlJMhv8cci8mM43uEnLA5t8iew=
|
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520/go.mod h1:Nz8ORLaFiLWotg6GeKlJMhv8cci8mM43uEnLA5t8iew=
|
||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc h1:t2AT8zb6N/59Y78lyRWedVoVWHNRSCBh0oWCC+bluTQ=
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc h1:t2AT8zb6N/59Y78lyRWedVoVWHNRSCBh0oWCC+bluTQ=
|
||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
||||||
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240711023527-ba358c48772f/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
||||||
github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo=
|
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/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
|||||||
@@ -222,9 +222,9 @@ func onHttpRequestBody(ctx wrapper.HttpContext, config PluginConfig, body []byte
|
|||||||
log.Debugf("cache hit, key:%s", key)
|
log.Debugf("cache hit, key:%s", key)
|
||||||
ctx.SetContext(CacheKeyContextKey, nil)
|
ctx.SetContext(CacheKeyContextKey, nil)
|
||||||
if !stream {
|
if !stream {
|
||||||
proxywasm.SendHttpResponse(200, [][2]string{{"content-type", "application/json; charset=utf-8"}}, []byte(fmt.Sprintf(config.ReturnResponseTemplate, response.String())), -1)
|
proxywasm.SendHttpResponseWithDetail(200, "ai-cache.hit", [][2]string{{"content-type", "application/json; charset=utf-8"}}, []byte(fmt.Sprintf(config.ReturnResponseTemplate, response.String())), -1)
|
||||||
} else {
|
} else {
|
||||||
proxywasm.SendHttpResponse(200, [][2]string{{"content-type", "text/event-stream; charset=utf-8"}}, []byte(fmt.Sprintf(config.ReturnStreamResponseTemplate, response.String())), -1)
|
proxywasm.SendHttpResponseWithDetail(200, "ai-cache.hit", [][2]string{{"content-type", "text/event-stream; charset=utf-8"}}, []byte(fmt.Sprintf(config.ReturnStreamResponseTemplate, response.String())), -1)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,18 +1,12 @@
|
|||||||
# 简介
|
# 简介
|
||||||
AI提示词修饰插件,通过在与大模型发起的请求前后插入指定信息来调整大模型的输出。
|
AI提示词装饰器插件,支持在LLM的请求前后插入prompt。
|
||||||
|
|
||||||
# 配置说明
|
# 配置说明
|
||||||
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
|
|
||||||
|----------------|-----------------|------|-----|----------------------------------|
|
|
||||||
| `decorators` | array of object | 必填 | - | 修饰设置 |
|
|
||||||
|
|
||||||
template object 配置说明:
|
|
||||||
|
|
||||||
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
|
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
|
||||||
|----------------|-----------------|------|-----|----------------------------------|
|
|----------------|-----------------|------|-----|----------------------------------|
|
||||||
| `name` | string | 必填 | - | 修饰名称 |
|
| `prepend` | array of message object | optional | - | 在初始输入之前插入的语句 |
|
||||||
| `decorator.prepend` | array of message object | 必填 | - | 在初始输入之前插入的语句 |
|
| `append` | array of message object | optional | - | 在初始输入之后插入的语句 |
|
||||||
| `decorator.append` | array of message object | 必填 | - | 在初始输入之后插入的语句 |
|
|
||||||
|
|
||||||
message object 配置说明:
|
message object 配置说明:
|
||||||
|
|
||||||
@@ -26,57 +20,50 @@ message object 配置说明:
|
|||||||
配置示例如下:
|
配置示例如下:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
decorators:
|
prepend:
|
||||||
- name: "hangzhou-guide"
|
- role: system
|
||||||
decorator:
|
content: "请使用英语回答问题"
|
||||||
prepend:
|
append:
|
||||||
- role: system
|
- role: user
|
||||||
content: "You will always respond in the Chinese language."
|
content: "每次回答完问题,尝试进行反问"
|
||||||
- role: user
|
|
||||||
content: "Assume you are from Hangzhou."
|
|
||||||
append:
|
|
||||||
- role: user
|
|
||||||
content: "Don't introduce Hangzhou's food."
|
|
||||||
```
|
```
|
||||||
|
|
||||||
使用以上配置发起请求:
|
使用以上配置发起请求:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
{
|
curl http://localhost/test \
|
||||||
|
-H "content-type: application/json" \
|
||||||
|
-d '{
|
||||||
"model": "gpt-3.5-turbo",
|
"model": "gpt-3.5-turbo",
|
||||||
"messages": [
|
"messages": [
|
||||||
{
|
{
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"content": "Please introduce your home."
|
"content": "你是谁?"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
响应如下:
|
经过插件处理后,实际请求为:
|
||||||
|
|
||||||
```
|
```bash
|
||||||
{
|
curl http://localhost/test \
|
||||||
"id": "chatcmpl-9UYwQlEg6GwAswEZBDYXl41RU4gab",
|
-H "content-type: application/json" \
|
||||||
"object": "chat.completion",
|
-d '{
|
||||||
"created": 1717071182,
|
"model": "gpt-3.5-turbo",
|
||||||
"model": "gpt-3.5-turbo-0125",
|
"messages": [
|
||||||
"choices": [
|
|
||||||
{
|
{
|
||||||
"index": 0,
|
"role": "system",
|
||||||
"message": {
|
"content": "请使用英语回答问题"
|
||||||
"role": "assistant",
|
},
|
||||||
"content": "杭州是一个美丽的城市,有着悠久的历史和富有特色的文化。这里风景优美,有西湖、雷峰塔等著名景点,吸引着许多游客前来观光。杭州人民热情好客,城市宁静安逸,是一个适合居住和旅游的地方。"
|
{
|
||||||
},
|
"role": "user",
|
||||||
"logprobs": null,
|
"content": "你是谁?"
|
||||||
"finish_reason": "stop"
|
},
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": "每次回答完问题,尝试进行反问"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"usage": {
|
|
||||||
"prompt_tokens": 49,
|
|
||||||
"completion_tokens": 117,
|
|
||||||
"total_tokens": 166
|
|
||||||
},
|
|
||||||
"system_fingerprint": null
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -2,9 +2,11 @@ module ai-prompt-decorator
|
|||||||
|
|
||||||
go 1.18
|
go 1.18
|
||||||
|
|
||||||
|
replace github.com/alibaba/higress/plugins/wasm-go => ../..
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/alibaba/higress/plugins/wasm-go v1.3.5
|
github.com/alibaba/higress/plugins/wasm-go v1.3.5
|
||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240226064518-b3dc4646a35a
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240711023527-ba358c48772f
|
||||||
github.com/tidwall/gjson v1.14.3
|
github.com/tidwall/gjson v1.14.3
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
github.com/alibaba/higress/plugins/wasm-go v1.3.5 h1:VOLL3m442IHCSu8mR5AZ4sc6LVT9X0w1hdqDI7oB9jY=
|
|
||||||
github.com/alibaba/higress/plugins/wasm-go v1.3.5/go.mod h1:kr3V9Ntbspj1eSrX8rgjBsdMXkGupYEf+LM72caGPQc=
|
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
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 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520 h1:IHDghbGQ2DTIXHBHxWfqCYQW1fKjyJ/I7W1pMyUDeEA=
|
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520 h1:IHDghbGQ2DTIXHBHxWfqCYQW1fKjyJ/I7W1pMyUDeEA=
|
||||||
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520/go.mod h1:Nz8ORLaFiLWotg6GeKlJMhv8cci8mM43uEnLA5t8iew=
|
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520/go.mod h1:Nz8ORLaFiLWotg6GeKlJMhv8cci8mM43uEnLA5t8iew=
|
||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240226064518-b3dc4646a35a h1:luYRvxLTE1xYxrXYj7nmjd1U0HHh8pUPiKfdZ0MhCGE=
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240711023527-ba358c48772f h1:ZIiIBRvIw62gA5MJhuwp1+2wWbqL9IGElQ499rUsYYg=
|
||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240226064518-b3dc4646a35a/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240711023527-ba358c48772f/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
||||||
github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo=
|
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/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"encoding/json"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/alibaba/higress/plugins/wasm-go/pkg/wrapper"
|
"github.com/alibaba/higress/plugins/wasm-go/pkg/wrapper"
|
||||||
"github.com/higress-group/proxy-wasm-go-sdk/proxywasm"
|
"github.com/higress-group/proxy-wasm-go-sdk/proxywasm"
|
||||||
@@ -20,66 +19,53 @@ func main() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Message struct {
|
||||||
|
Role string `json:"role"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
type AIPromptDecoratorConfig struct {
|
type AIPromptDecoratorConfig struct {
|
||||||
decorators map[string]string
|
Prepend []Message `json:"prepend"`
|
||||||
|
Append []Message `json:"append"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeBrackets(raw string) (string, error) {
|
func parseConfig(jsonConfig gjson.Result, config *AIPromptDecoratorConfig, log wrapper.Log) error {
|
||||||
startIndex := strings.Index(raw, "{")
|
return json.Unmarshal([]byte(jsonConfig.Raw), config)
|
||||||
endIndex := strings.LastIndex(raw, "}")
|
|
||||||
if startIndex == -1 || endIndex == -1 {
|
|
||||||
return raw, errors.New("message format is wrong!")
|
|
||||||
} else {
|
|
||||||
return raw[startIndex : endIndex+1], nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseConfig(json gjson.Result, config *AIPromptDecoratorConfig, log wrapper.Log) error {
|
|
||||||
config.decorators = make(map[string]string)
|
|
||||||
for _, v := range json.Get("decorators").Array() {
|
|
||||||
config.decorators[v.Get("name").String()] = v.Get("decorator").Raw
|
|
||||||
// log.Info(v.Get("decorator").Raw)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func onHttpRequestHeaders(ctx wrapper.HttpContext, config AIPromptDecoratorConfig, log wrapper.Log) types.Action {
|
func onHttpRequestHeaders(ctx wrapper.HttpContext, config AIPromptDecoratorConfig, log wrapper.Log) types.Action {
|
||||||
decorator, _ := proxywasm.GetHttpRequestHeader("decorator")
|
|
||||||
if decorator == "" {
|
|
||||||
ctx.DontReadRequestBody()
|
|
||||||
return types.ActionContinue
|
|
||||||
}
|
|
||||||
ctx.SetContext("decorator", decorator)
|
|
||||||
proxywasm.RemoveHttpRequestHeader("decorator")
|
|
||||||
proxywasm.RemoveHttpRequestHeader("content-length")
|
proxywasm.RemoveHttpRequestHeader("content-length")
|
||||||
return types.ActionContinue
|
return types.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
func onHttpRequestBody(ctx wrapper.HttpContext, config AIPromptDecoratorConfig, body []byte, log wrapper.Log) types.Action {
|
func onHttpRequestBody(ctx wrapper.HttpContext, config AIPromptDecoratorConfig, body []byte, log wrapper.Log) types.Action {
|
||||||
decoratorName := ctx.GetContext("decorator").(string)
|
|
||||||
decorator := config.decorators[decoratorName]
|
|
||||||
|
|
||||||
messageJson := `{"messages":[]}`
|
messageJson := `{"messages":[]}`
|
||||||
|
|
||||||
prependMessage := gjson.Get(decorator, "prepend")
|
for _, entry := range config.Prepend {
|
||||||
if prependMessage.Exists() {
|
msg, err := json.Marshal(entry)
|
||||||
for _, entry := range prependMessage.Array() {
|
if err != nil {
|
||||||
messageJson, _ = sjson.SetRaw(messageJson, "messages.-1", entry.Raw)
|
log.Errorf("Failed to add prepend message, error: %v", err)
|
||||||
|
return types.ActionContinue
|
||||||
}
|
}
|
||||||
|
messageJson, _ = sjson.SetRaw(messageJson, "messages.-1", string(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
rawMessage := gjson.GetBytes(body, "messages")
|
rawMessage := gjson.GetBytes(body, "messages")
|
||||||
if rawMessage.Exists() {
|
if !rawMessage.Exists() {
|
||||||
for _, entry := range rawMessage.Array() {
|
log.Errorf("Cannot find messages field in request body")
|
||||||
messageJson, _ = sjson.SetRaw(messageJson, "messages.-1", entry.Raw)
|
return types.ActionContinue
|
||||||
}
|
}
|
||||||
|
for _, entry := range rawMessage.Array() {
|
||||||
|
messageJson, _ = sjson.SetRaw(messageJson, "messages.-1", entry.Raw)
|
||||||
}
|
}
|
||||||
|
|
||||||
appendMessage := gjson.Get(decorator, "append")
|
for _, entry := range config.Append {
|
||||||
if appendMessage.Exists() {
|
msg, err := json.Marshal(entry)
|
||||||
for _, entry := range appendMessage.Array() {
|
if err != nil {
|
||||||
messageJson, _ = sjson.SetRaw(messageJson, "messages.-1", entry.Raw)
|
log.Errorf("Failed to add prepend message, error: %v", err)
|
||||||
|
return types.ActionContinue
|
||||||
}
|
}
|
||||||
|
messageJson, _ = sjson.SetRaw(messageJson, "messages.-1", string(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
newbody, err := sjson.SetRaw(string(body), "messages", gjson.Get(messageJson, "messages").Raw)
|
newbody, err := sjson.SetRaw(string(body), "messages", gjson.Get(messageJson, "messages").Raw)
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ module ai-prompt-template
|
|||||||
|
|
||||||
go 1.18
|
go 1.18
|
||||||
|
|
||||||
|
replace github.com/alibaba/higress/plugins/wasm-go => ../..
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/alibaba/higress/plugins/wasm-go v1.3.5
|
github.com/alibaba/higress/plugins/wasm-go v1.3.5
|
||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240226064518-b3dc4646a35a
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240711023527-ba358c48772f
|
||||||
github.com/tidwall/gjson v1.14.3
|
github.com/tidwall/gjson v1.14.3
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520 h1:IHDghbG
|
|||||||
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520/go.mod h1:Nz8ORLaFiLWotg6GeKlJMhv8cci8mM43uEnLA5t8iew=
|
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520/go.mod h1:Nz8ORLaFiLWotg6GeKlJMhv8cci8mM43uEnLA5t8iew=
|
||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240226064518-b3dc4646a35a h1:luYRvxLTE1xYxrXYj7nmjd1U0HHh8pUPiKfdZ0MhCGE=
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240226064518-b3dc4646a35a h1:luYRvxLTE1xYxrXYj7nmjd1U0HHh8pUPiKfdZ0MhCGE=
|
||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240226064518-b3dc4646a35a/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240226064518-b3dc4646a35a/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
||||||
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240711023527-ba358c48772f/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
||||||
github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo=
|
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/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
|||||||
1
plugins/wasm-go/extensions/ai-proxy/.buildrc
Normal file
1
plugins/wasm-go/extensions/ai-proxy/.buildrc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
EXTRA_TAGS=proxy_wasm_version_0_2_100
|
||||||
@@ -19,14 +19,14 @@ description: AI 代理插件配置参考
|
|||||||
|
|
||||||
`provider`的配置字段说明如下:
|
`provider`的配置字段说明如下:
|
||||||
|
|
||||||
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
|
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
|
||||||
| -------------- | --------------- | -------- | ------ | ------------------------------------------------------------ |
|
| -------------- | --------------- | -------- | ------ |-------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `type` | string | 必填 | - | AI 服务提供商名称 |
|
| `type` | string | 必填 | - | AI 服务提供商名称 |
|
||||||
| `apiTokens` | array of string | 必填 | - | 用于在访问 AI 服务时进行认证的令牌。如果配置了多个 token,插件会在请求时随机进行选择。部分服务提供商只支持配置一个 token。 |
|
| `apiTokens` | array of string | 必填 | - | 用于在访问 AI 服务时进行认证的令牌。如果配置了多个 token,插件会在请求时随机进行选择。部分服务提供商只支持配置一个 token。 |
|
||||||
| `timeout` | number | 非必填 | - | 访问 AI 服务的超时时间。单位为毫秒。默认值为 120000,即 2 分钟 |
|
| `timeout` | number | 非必填 | - | 访问 AI 服务的超时时间。单位为毫秒。默认值为 120000,即 2 分钟 |
|
||||||
| `modelMapping` | map of string | 非必填 | - | AI 模型映射表,用于将请求中的模型名称映射为服务提供商支持模型名称。<br/>可以使用 "*" 为键来配置通用兜底映射关系 |
|
| `modelMapping` | map of string | 非必填 | - | AI 模型映射表,用于将请求中的模型名称映射为服务提供商支持模型名称。<br/>1. 支持前缀匹配。例如用 "gpt-3-*" 匹配所有名称以“gpt-3-”开头的模型;<br/>2. 支持使用 "*" 为键来配置通用兜底映射关系;<br/>3. 如果映射的目标名称为空字符串 "",则表示保留原模型名称。 |
|
||||||
| `protocol` | string | 非必填 | - | 插件对外提供的 API 接口契约。目前支持以下取值:openai(默认值,使用 OpenAI 的接口契约)、original(使用目标服务提供商的原始接口契约) |
|
| `protocol` | string | 非必填 | - | 插件对外提供的 API 接口契约。目前支持以下取值:openai(默认值,使用 OpenAI 的接口契约)、original(使用目标服务提供商的原始接口契约) |
|
||||||
| `context` | object | 非必填 | - | 配置 AI 对话上下文信息 |
|
| `context` | object | 非必填 | - | 配置 AI 对话上下文信息 |
|
||||||
|
|
||||||
`context`的配置字段说明如下:
|
`context`的配置字段说明如下:
|
||||||
|
|
||||||
@@ -131,6 +131,15 @@ Ollama 所对应的 `type` 为 `ollama`。它特有的配置字段如下:
|
|||||||
|
|
||||||
阶跃星辰所对应的 `type` 为 `stepfun`。它并无特有的配置字段。
|
阶跃星辰所对应的 `type` 为 `stepfun`。它并无特有的配置字段。
|
||||||
|
|
||||||
|
#### Cloudflare Workers AI
|
||||||
|
|
||||||
|
Cloudflare Workers AI 所对应的 `type` 为 `cloudflare`。它特有的配置字段如下:
|
||||||
|
|
||||||
|
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
|
||||||
|
|-------------------|--------|------|-----|----------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| `cloudflareAccountId` | string | 必填 | - | [Cloudflare Account ID](https://developers.cloudflare.com/workers-ai/get-started/rest-api/#1-get-api-token-and-account-id) |
|
||||||
|
|
||||||
|
|
||||||
## 用法示例
|
## 用法示例
|
||||||
|
|
||||||
### 使用 OpenAI 协议代理 Azure OpenAI 服务
|
### 使用 OpenAI 协议代理 Azure OpenAI 服务
|
||||||
@@ -246,25 +255,72 @@ provider:
|
|||||||
'gpt-3': "qwen-turbo"
|
'gpt-3': "qwen-turbo"
|
||||||
'gpt-35-turbo': "qwen-plus"
|
'gpt-35-turbo': "qwen-plus"
|
||||||
'gpt-4-turbo': "qwen-max"
|
'gpt-4-turbo': "qwen-max"
|
||||||
|
'gpt-4-*': "qwen-max"
|
||||||
|
'text-embedding-v1': 'text-embedding-v1'
|
||||||
'*': "qwen-turbo"
|
'*': "qwen-turbo"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**AI 对话请求示例**
|
||||||
|
|
||||||
|
URL: http://your-domain/v1/chat/completions
|
||||||
|
|
||||||
|
请求体:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"model": "text-embedding-v1",
|
||||||
|
"input": "Hello"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
响应体示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"object": "list",
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"object": "embedding",
|
||||||
|
"index": 0,
|
||||||
|
"embedding": [
|
||||||
|
-1.0437825918197632,
|
||||||
|
5.208984375,
|
||||||
|
3.0483806133270264,
|
||||||
|
-1.7897135019302368,
|
||||||
|
-2.0107421875,
|
||||||
|
...,
|
||||||
|
0.8125,
|
||||||
|
-1.1759847402572632,
|
||||||
|
0.8174641728401184,
|
||||||
|
1.0432943105697632,
|
||||||
|
-0.5885213017463684
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"model": "text-embedding-v1",
|
||||||
|
"usage": {
|
||||||
|
"prompt_tokens": 1,
|
||||||
|
"total_tokens": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
**请求示例**
|
**请求示例**
|
||||||
|
|
||||||
|
URL: http://your-domain/v1/embeddings
|
||||||
|
|
||||||
|
示例请求内容:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"model": "gpt-3",
|
"model": "text-embedding-v1",
|
||||||
"messages": [
|
"input": [
|
||||||
{
|
"Hello world!"
|
||||||
"role": "user",
|
]
|
||||||
"content": "你好,你是谁?"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"temperature": 0.3
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**响应示例**
|
示例响应内容:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -758,6 +814,57 @@ provider:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 使用 OpenAI 协议代理 Cloudflare Workers AI 服务
|
||||||
|
|
||||||
|
**配置信息**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
provider:
|
||||||
|
type: cloudflare
|
||||||
|
apiTokens:
|
||||||
|
- "YOUR_WORKERS_AI_API_TOKEN"
|
||||||
|
cloudflareAccountId: "YOUR_CLOUDFLARE_ACCOUNT_ID"
|
||||||
|
modelMapping:
|
||||||
|
"*": "@cf/meta/llama-3-8b-instruct"
|
||||||
|
```
|
||||||
|
|
||||||
|
**请求示例**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"model": "gpt-3.5",
|
||||||
|
"max_tokens": 1024,
|
||||||
|
"messages": [
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": "Who are you?"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**响应示例**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "id-1720367803430",
|
||||||
|
"object": "chat.completion",
|
||||||
|
"created": 1720367803,
|
||||||
|
"model": "@cf/meta/llama-3-8b-instruct",
|
||||||
|
"choices": [
|
||||||
|
{
|
||||||
|
"index": 0,
|
||||||
|
"message": {
|
||||||
|
"role": "assistant",
|
||||||
|
"content": "I am LLaMA, an AI assistant developed by Meta AI that can understand and respond to human input in a conversational manner. I'm not a human, but a computer program designed to simulate conversation and answer questions to the best of my knowledge. I can be used to generate text on a wide range of topics, from science and history to entertainment and culture.\n\nI'm a large language model, which means I've been trained on a massive dataset of text from the internet and can generate human-like responses. I can understand natural language and respond accordingly, making me suitable for tasks such as:\n\n* Answering questions on various topics\n* Generating text based on a given prompt\n* Translating text from one language to another\n* Summarizing long pieces of text\n* Creating chatbot dialogues\n\nI'm constantly learning and improving, so the more conversations I have with users like you, the better I'll become."
|
||||||
|
},
|
||||||
|
"logprobs": null,
|
||||||
|
"finish_reason": "stop"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## 完整配置示例
|
## 完整配置示例
|
||||||
|
|
||||||
### Kubernetes 示例
|
### Kubernetes 示例
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ replace github.com/alibaba/higress/plugins/wasm-go => ../..
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/alibaba/higress/plugins/wasm-go v0.0.0
|
github.com/alibaba/higress/plugins/wasm-go v0.0.0
|
||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240711023527-ba358c48772f
|
||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.8.4
|
||||||
github.com/tidwall/gjson v1.14.3
|
github.com/tidwall/gjson v1.14.3
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ 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/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520 h1:IHDghbGQ2DTIXHBHxWfqCYQW1fKjyJ/I7W1pMyUDeEA=
|
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520 h1:IHDghbGQ2DTIXHBHxWfqCYQW1fKjyJ/I7W1pMyUDeEA=
|
||||||
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520/go.mod h1:Nz8ORLaFiLWotg6GeKlJMhv8cci8mM43uEnLA5t8iew=
|
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520/go.mod h1:Nz8ORLaFiLWotg6GeKlJMhv8cci8mM43uEnLA5t8iew=
|
||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc h1:t2AT8zb6N/59Y78lyRWedVoVWHNRSCBh0oWCC+bluTQ=
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240711023527-ba358c48772f h1:ZIiIBRvIw62gA5MJhuwp1+2wWbqL9IGElQ499rUsYYg=
|
||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240711023527-ba358c48772f/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
||||||
github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo=
|
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/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ const (
|
|||||||
pluginName = "ai-proxy"
|
pluginName = "ai-proxy"
|
||||||
|
|
||||||
ctxKeyApiName = "apiKey"
|
ctxKeyApiName = "apiKey"
|
||||||
|
|
||||||
|
defaultMaxBodyBytes uint32 = 10 * 1024 * 1024
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -61,10 +63,10 @@ func onHttpRequestHeader(ctx wrapper.HttpContext, pluginConfig config.PluginConf
|
|||||||
|
|
||||||
rawPath := ctx.Path()
|
rawPath := ctx.Path()
|
||||||
path, _ := url.Parse(rawPath)
|
path, _ := url.Parse(rawPath)
|
||||||
apiName := getApiName(path.Path)
|
apiName := getOpenAiApiName(path.Path)
|
||||||
if apiName == "" {
|
if apiName == "" {
|
||||||
log.Debugf("[onHttpRequestHeader] unsupported path: %s", path.Path)
|
log.Debugf("[onHttpRequestHeader] unsupported path: %s", path.Path)
|
||||||
_ = util.SendResponse(404, util.MimeTypeTextPlain, "API not found: "+path.Path)
|
_ = util.SendResponse(404, "ai-proxy.unknown_api", util.MimeTypeTextPlain, "API not found: "+path.Path)
|
||||||
return types.ActionContinue
|
return types.ActionContinue
|
||||||
}
|
}
|
||||||
ctx.SetContext(ctxKeyApiName, apiName)
|
ctx.SetContext(ctxKeyApiName, apiName)
|
||||||
@@ -75,16 +77,18 @@ func onHttpRequestHeader(ctx wrapper.HttpContext, pluginConfig config.PluginConf
|
|||||||
|
|
||||||
action, err := handler.OnRequestHeaders(ctx, apiName, log)
|
action, err := handler.OnRequestHeaders(ctx, apiName, log)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
if contentType, err := proxywasm.GetHttpRequestHeader("Content-Type"); err == nil && contentType != "" {
|
||||||
|
ctx.SetRequestBodyBufferLimit(defaultMaxBodyBytes)
|
||||||
|
// Always return types.HeaderStopIteration to support fallback routing,
|
||||||
|
// as long as onHttpRequestBody can be called.
|
||||||
|
return types.HeaderStopIteration
|
||||||
|
}
|
||||||
return action
|
return action
|
||||||
}
|
}
|
||||||
_ = util.SendResponse(404, util.MimeTypeTextPlain, fmt.Sprintf("failed to process request headers: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.proc_req_headers_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to process request headers: %v", err))
|
||||||
return types.ActionContinue
|
return types.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, needHandleBody := activeProvider.(provider.RequestBodyHandler); needHandleBody {
|
|
||||||
ctx.DontReadRequestBody()
|
|
||||||
}
|
|
||||||
|
|
||||||
return types.ActionContinue
|
return types.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,18 +103,24 @@ func onHttpRequestBody(ctx wrapper.HttpContext, pluginConfig config.PluginConfig
|
|||||||
log.Debugf("[onHttpRequestBody] provider=%s", activeProvider.GetProviderType())
|
log.Debugf("[onHttpRequestBody] provider=%s", activeProvider.GetProviderType())
|
||||||
|
|
||||||
if handler, ok := activeProvider.(provider.RequestBodyHandler); ok {
|
if handler, ok := activeProvider.(provider.RequestBodyHandler); ok {
|
||||||
apiName := ctx.GetContext(ctxKeyApiName).(provider.ApiName)
|
apiName, _ := ctx.GetContext(ctxKeyApiName).(provider.ApiName)
|
||||||
action, err := handler.OnRequestBody(ctx, apiName, body, log)
|
action, err := handler.OnRequestBody(ctx, apiName, body, log)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return action
|
return action
|
||||||
}
|
}
|
||||||
_ = util.SendResponse(404, util.MimeTypeTextPlain, fmt.Sprintf("failed to process request body: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.proc_req_body_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to process request body: %v", err))
|
||||||
return types.ActionContinue
|
return types.ActionContinue
|
||||||
}
|
}
|
||||||
return types.ActionContinue
|
return types.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
func onHttpResponseHeaders(ctx wrapper.HttpContext, pluginConfig config.PluginConfig, log wrapper.Log) types.Action {
|
func onHttpResponseHeaders(ctx wrapper.HttpContext, pluginConfig config.PluginConfig, log wrapper.Log) types.Action {
|
||||||
|
if !wrapper.IsResponseFromUpstream() {
|
||||||
|
// Response is not coming from the upstream. Let it pass through.
|
||||||
|
ctx.DontReadResponseBody()
|
||||||
|
return types.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
activeProvider := pluginConfig.GetProvider()
|
activeProvider := pluginConfig.GetProvider()
|
||||||
|
|
||||||
if activeProvider == nil {
|
if activeProvider == nil {
|
||||||
@@ -139,12 +149,12 @@ func onHttpResponseHeaders(ctx wrapper.HttpContext, pluginConfig config.PluginCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
if handler, ok := activeProvider.(provider.ResponseHeadersHandler); ok {
|
if handler, ok := activeProvider.(provider.ResponseHeadersHandler); ok {
|
||||||
apiName := ctx.GetContext(ctxKeyApiName).(provider.ApiName)
|
apiName, _ := ctx.GetContext(ctxKeyApiName).(provider.ApiName)
|
||||||
action, err := handler.OnResponseHeaders(ctx, apiName, log)
|
action, err := handler.OnResponseHeaders(ctx, apiName, log)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return action
|
return action
|
||||||
}
|
}
|
||||||
_ = util.SendResponse(404, util.MimeTypeTextPlain, fmt.Sprintf("failed to process response headers: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.proc_resp_headers_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to process response headers: %v", err))
|
||||||
return types.ActionContinue
|
return types.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,7 +181,7 @@ func onStreamingResponseBody(ctx wrapper.HttpContext, pluginConfig config.Plugin
|
|||||||
log.Debugf("isLastChunk=%v chunk: %s", isLastChunk, string(chunk))
|
log.Debugf("isLastChunk=%v chunk: %s", isLastChunk, string(chunk))
|
||||||
|
|
||||||
if handler, ok := activeProvider.(provider.StreamingResponseBodyHandler); ok {
|
if handler, ok := activeProvider.(provider.StreamingResponseBodyHandler); ok {
|
||||||
apiName := ctx.GetContext(ctxKeyApiName).(provider.ApiName)
|
apiName, _ := ctx.GetContext(ctxKeyApiName).(provider.ApiName)
|
||||||
modifiedChunk, err := handler.OnStreamingResponseBody(ctx, apiName, chunk, isLastChunk, log)
|
modifiedChunk, err := handler.OnStreamingResponseBody(ctx, apiName, chunk, isLastChunk, log)
|
||||||
if err == nil && modifiedChunk != nil {
|
if err == nil && modifiedChunk != nil {
|
||||||
return modifiedChunk
|
return modifiedChunk
|
||||||
@@ -193,20 +203,23 @@ func onHttpResponseBody(ctx wrapper.HttpContext, pluginConfig config.PluginConfi
|
|||||||
//log.Debugf("response body: %s", string(body))
|
//log.Debugf("response body: %s", string(body))
|
||||||
|
|
||||||
if handler, ok := activeProvider.(provider.ResponseBodyHandler); ok {
|
if handler, ok := activeProvider.(provider.ResponseBodyHandler); ok {
|
||||||
apiName := ctx.GetContext(ctxKeyApiName).(provider.ApiName)
|
apiName, _ := ctx.GetContext(ctxKeyApiName).(provider.ApiName)
|
||||||
action, err := handler.OnResponseBody(ctx, apiName, body, log)
|
action, err := handler.OnResponseBody(ctx, apiName, body, log)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return action
|
return action
|
||||||
}
|
}
|
||||||
_ = util.SendResponse(404, util.MimeTypeTextPlain, fmt.Sprintf("failed to process response body: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.proc_resp_body_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to process response body: %v", err))
|
||||||
return types.ActionContinue
|
return types.ActionContinue
|
||||||
}
|
}
|
||||||
return types.ActionContinue
|
return types.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
func getApiName(path string) provider.ApiName {
|
func getOpenAiApiName(path string) provider.ApiName {
|
||||||
if strings.HasSuffix(path, "/v1/chat/completions") {
|
if strings.HasSuffix(path, "/v1/chat/completions") {
|
||||||
return provider.ApiNameChatCompletion
|
return provider.ApiNameChatCompletion
|
||||||
}
|
}
|
||||||
|
if strings.HasSuffix(path, "/v1/embeddings") {
|
||||||
|
return provider.ApiNameEmbeddings
|
||||||
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,13 +58,7 @@ func (m *azureProvider) OnRequestHeaders(ctx wrapper.HttpContext, apiName ApiNam
|
|||||||
_ = util.OverwriteRequestPath(m.serviceUrl.RequestURI())
|
_ = util.OverwriteRequestPath(m.serviceUrl.RequestURI())
|
||||||
_ = util.OverwriteRequestHost(m.serviceUrl.Host)
|
_ = util.OverwriteRequestHost(m.serviceUrl.Host)
|
||||||
_ = proxywasm.ReplaceHttpRequestHeader("api-key", m.config.apiTokens[0])
|
_ = proxywasm.ReplaceHttpRequestHeader("api-key", m.config.apiTokens[0])
|
||||||
|
_ = proxywasm.RemoveHttpRequestHeader("Content-Length")
|
||||||
if m.contextCache == nil {
|
|
||||||
ctx.DontReadRequestBody()
|
|
||||||
} else {
|
|
||||||
_ = proxywasm.RemoveHttpRequestHeader("Content-Length")
|
|
||||||
}
|
|
||||||
|
|
||||||
return types.ActionContinue, nil
|
return types.ActionContinue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,11 +79,11 @@ func (m *azureProvider) OnRequestBody(ctx wrapper.HttpContext, apiName ApiName,
|
|||||||
}()
|
}()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to load context file: %v", err)
|
log.Errorf("failed to load context file: %v", err)
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.azure.load_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
||||||
}
|
}
|
||||||
insertContextMessage(request, content)
|
insertContextMessage(request, content)
|
||||||
if err := replaceJsonRequestBody(request, log); err != nil {
|
if err := replaceJsonRequestBody(request, log); err != nil {
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.azure.insert_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
||||||
}
|
}
|
||||||
}, log)
|
}, log)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|||||||
@@ -45,14 +45,8 @@ func (m *baichuanProvider) OnRequestHeaders(ctx wrapper.HttpContext, apiName Api
|
|||||||
}
|
}
|
||||||
_ = util.OverwriteRequestPath(baichuanChatCompletionPath)
|
_ = util.OverwriteRequestPath(baichuanChatCompletionPath)
|
||||||
_ = util.OverwriteRequestHost(baichuanDomain)
|
_ = util.OverwriteRequestHost(baichuanDomain)
|
||||||
_ = proxywasm.ReplaceHttpRequestHeader("Authorization", "Bearer "+m.config.GetRandomToken())
|
_ = util.OverwriteRequestAuthorization("Bearer " + m.config.GetRandomToken())
|
||||||
|
_ = proxywasm.RemoveHttpRequestHeader("Content-Length")
|
||||||
if m.contextCache == nil {
|
|
||||||
ctx.DontReadRequestBody()
|
|
||||||
} else {
|
|
||||||
_ = proxywasm.RemoveHttpRequestHeader("Content-Length")
|
|
||||||
}
|
|
||||||
|
|
||||||
return types.ActionContinue, nil
|
return types.ActionContinue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,11 +67,11 @@ func (m *baichuanProvider) OnRequestBody(ctx wrapper.HttpContext, apiName ApiNam
|
|||||||
}()
|
}()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to load context file: %v", err)
|
log.Errorf("failed to load context file: %v", err)
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.baichuan.load_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
||||||
}
|
}
|
||||||
insertContextMessage(request, content)
|
insertContextMessage(request, content)
|
||||||
if err := replaceJsonRequestBody(request, log); err != nil {
|
if err := replaceJsonRequestBody(request, log); err != nil {
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.baichuan.insert_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
||||||
}
|
}
|
||||||
}, log)
|
}, log)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|||||||
@@ -94,11 +94,11 @@ func (b *baiduProvider) OnRequestBody(ctx wrapper.HttpContext, apiName ApiName,
|
|||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to load context file: %v", err)
|
log.Errorf("failed to load context file: %v", err)
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.baidu.load_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
||||||
}
|
}
|
||||||
b.setSystemContent(request, content)
|
b.setSystemContent(request, content)
|
||||||
if err := replaceJsonRequestBody(request, log); err != nil {
|
if err := replaceJsonRequestBody(request, log); err != nil {
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.baidu.insert_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
||||||
}
|
}
|
||||||
}, log)
|
}, log)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -137,12 +137,12 @@ func (b *baiduProvider) OnRequestBody(ctx wrapper.HttpContext, apiName ApiName,
|
|||||||
}()
|
}()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to load context file: %v", err)
|
log.Errorf("failed to load context file: %v", err)
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.baidu.load_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
||||||
}
|
}
|
||||||
insertContextMessage(request, content)
|
insertContextMessage(request, content)
|
||||||
baiduRequest := b.baiduTextGenRequest(request)
|
baiduRequest := b.baiduTextGenRequest(request)
|
||||||
if err := replaceJsonRequestBody(baiduRequest, log); err != nil {
|
if err := replaceJsonRequestBody(baiduRequest, log); err != nil {
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to replace Request body: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.baidu.insert_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to replace Request body: %v", err))
|
||||||
}
|
}
|
||||||
}, log)
|
}, log)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -298,11 +298,11 @@ func (b *baiduProvider) responseBaidu2OpenAI(ctx wrapper.HttpContext, response *
|
|||||||
return &chatCompletionResponse{
|
return &chatCompletionResponse{
|
||||||
Id: response.Id,
|
Id: response.Id,
|
||||||
Created: time.Now().UnixMilli() / 1000,
|
Created: time.Now().UnixMilli() / 1000,
|
||||||
Model: ctx.GetContext(ctxKeyFinalRequestModel).(string),
|
Model: ctx.GetStringContext(ctxKeyFinalRequestModel, ""),
|
||||||
SystemFingerprint: "",
|
SystemFingerprint: "",
|
||||||
Object: objectChatCompletion,
|
Object: objectChatCompletion,
|
||||||
Choices: []chatCompletionChoice{choice},
|
Choices: []chatCompletionChoice{choice},
|
||||||
Usage: chatCompletionUsage{
|
Usage: usage{
|
||||||
PromptTokens: response.Usage.PromptTokens,
|
PromptTokens: response.Usage.PromptTokens,
|
||||||
CompletionTokens: response.Usage.CompletionTokens,
|
CompletionTokens: response.Usage.CompletionTokens,
|
||||||
TotalTokens: response.Usage.TotalTokens,
|
TotalTokens: response.Usage.TotalTokens,
|
||||||
@@ -321,11 +321,11 @@ func (b *baiduProvider) streamResponseBaidu2OpenAI(ctx wrapper.HttpContext, resp
|
|||||||
return &chatCompletionResponse{
|
return &chatCompletionResponse{
|
||||||
Id: response.Id,
|
Id: response.Id,
|
||||||
Created: time.Now().UnixMilli() / 1000,
|
Created: time.Now().UnixMilli() / 1000,
|
||||||
Model: ctx.GetContext(ctxKeyFinalRequestModel).(string),
|
Model: ctx.GetStringContext(ctxKeyFinalRequestModel, ""),
|
||||||
SystemFingerprint: "",
|
SystemFingerprint: "",
|
||||||
Object: objectChatCompletion,
|
Object: objectChatCompletion,
|
||||||
Choices: []chatCompletionChoice{choice},
|
Choices: []chatCompletionChoice{choice},
|
||||||
Usage: chatCompletionUsage{
|
Usage: usage{
|
||||||
PromptTokens: response.Usage.PromptTokens,
|
PromptTokens: response.Usage.PromptTokens,
|
||||||
CompletionTokens: response.Usage.CompletionTokens,
|
CompletionTokens: response.Usage.CompletionTokens,
|
||||||
TotalTokens: response.Usage.TotalTokens,
|
TotalTokens: response.Usage.TotalTokens,
|
||||||
|
|||||||
@@ -139,10 +139,10 @@ func (c *claudeProvider) OnRequestBody(ctx wrapper.HttpContext, apiName ApiName,
|
|||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to load context file: %v", err)
|
log.Errorf("failed to load context file: %v", err)
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.claude.load_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
||||||
}
|
}
|
||||||
if err := replaceJsonRequestBody(request, log); err != nil {
|
if err := replaceJsonRequestBody(request, log); err != nil {
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.claude.insert_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
||||||
}
|
}
|
||||||
}, log)
|
}, log)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -185,12 +185,12 @@ func (c *claudeProvider) OnRequestBody(ctx wrapper.HttpContext, apiName ApiName,
|
|||||||
}()
|
}()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to load context file: %v", err)
|
log.Errorf("failed to load context file: %v", err)
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.claude.load_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
||||||
}
|
}
|
||||||
insertContextMessage(request, content)
|
insertContextMessage(request, content)
|
||||||
claudeRequest := c.buildClaudeTextGenRequest(request)
|
claudeRequest := c.buildClaudeTextGenRequest(request)
|
||||||
if err := replaceJsonRequestBody(claudeRequest, log); err != nil {
|
if err := replaceJsonRequestBody(claudeRequest, log); err != nil {
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.claude.insert_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
||||||
}
|
}
|
||||||
}, log)
|
}, log)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -292,11 +292,11 @@ func (c *claudeProvider) responseClaude2OpenAI(ctx wrapper.HttpContext, origResp
|
|||||||
return &chatCompletionResponse{
|
return &chatCompletionResponse{
|
||||||
Id: origResponse.Id,
|
Id: origResponse.Id,
|
||||||
Created: time.Now().UnixMilli() / 1000,
|
Created: time.Now().UnixMilli() / 1000,
|
||||||
Model: ctx.GetContext(ctxKeyFinalRequestModel).(string),
|
Model: ctx.GetStringContext(ctxKeyFinalRequestModel, ""),
|
||||||
SystemFingerprint: "",
|
SystemFingerprint: "",
|
||||||
Object: objectChatCompletion,
|
Object: objectChatCompletion,
|
||||||
Choices: []chatCompletionChoice{choice},
|
Choices: []chatCompletionChoice{choice},
|
||||||
Usage: chatCompletionUsage{
|
Usage: usage{
|
||||||
PromptTokens: origResponse.Usage.InputTokens,
|
PromptTokens: origResponse.Usage.InputTokens,
|
||||||
CompletionTokens: origResponse.Usage.OutputTokens,
|
CompletionTokens: origResponse.Usage.OutputTokens,
|
||||||
TotalTokens: origResponse.Usage.InputTokens + origResponse.Usage.OutputTokens,
|
TotalTokens: origResponse.Usage.InputTokens + origResponse.Usage.OutputTokens,
|
||||||
@@ -356,7 +356,7 @@ func createChatCompletionResponse(ctx wrapper.HttpContext, response *claudeTextG
|
|||||||
return &chatCompletionResponse{
|
return &chatCompletionResponse{
|
||||||
Id: response.Message.Id,
|
Id: response.Message.Id,
|
||||||
Created: time.Now().UnixMilli() / 1000,
|
Created: time.Now().UnixMilli() / 1000,
|
||||||
Model: ctx.GetContext(ctxKeyFinalRequestModel).(string),
|
Model: ctx.GetStringContext(ctxKeyFinalRequestModel, ""),
|
||||||
Object: objectChatCompletionChunk,
|
Object: objectChatCompletionChunk,
|
||||||
Choices: []chatCompletionChoice{choice},
|
Choices: []chatCompletionChoice{choice},
|
||||||
}
|
}
|
||||||
|
|||||||
106
plugins/wasm-go/extensions/ai-proxy/provider/cloudflare.go
Normal file
106
plugins/wasm-go/extensions/ai-proxy/provider/cloudflare.go
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
package provider
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/alibaba/higress/plugins/wasm-go/extensions/ai-proxy/util"
|
||||||
|
"github.com/alibaba/higress/plugins/wasm-go/pkg/wrapper"
|
||||||
|
"github.com/higress-group/proxy-wasm-go-sdk/proxywasm"
|
||||||
|
"github.com/higress-group/proxy-wasm-go-sdk/proxywasm/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
cloudflareDomain = "api.cloudflare.com"
|
||||||
|
// https://developers.cloudflare.com/workers-ai/configuration/open-ai-compatibility/
|
||||||
|
cloudflareChatCompletionPath = "/client/v4/accounts/{account_id}/ai/v1/chat/completions"
|
||||||
|
)
|
||||||
|
|
||||||
|
type cloudflareProviderInitializer struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cloudflareProviderInitializer) ValidateConfig(config ProviderConfig) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cloudflareProviderInitializer) CreateProvider(config ProviderConfig) (Provider, error) {
|
||||||
|
return &cloudflareProvider{
|
||||||
|
config: config,
|
||||||
|
contextCache: createContextCache(&config),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type cloudflareProvider struct {
|
||||||
|
config ProviderConfig
|
||||||
|
contextCache *contextCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cloudflareProvider) GetProviderType() string {
|
||||||
|
return providerTypeCloudflare
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cloudflareProvider) OnRequestHeaders(ctx wrapper.HttpContext, apiName ApiName, log wrapper.Log) (types.Action, error) {
|
||||||
|
if apiName != ApiNameChatCompletion {
|
||||||
|
return types.ActionContinue, errUnsupportedApiName
|
||||||
|
}
|
||||||
|
_ = util.OverwriteRequestPath(strings.Replace(cloudflareChatCompletionPath, "{account_id}", c.config.cloudflareAccountId, 1))
|
||||||
|
_ = util.OverwriteRequestHost(cloudflareDomain)
|
||||||
|
_ = util.OverwriteRequestAuthorization("Bearer " + c.config.GetRandomToken())
|
||||||
|
|
||||||
|
_ = proxywasm.RemoveHttpRequestHeader("Accept-Encoding")
|
||||||
|
_ = proxywasm.RemoveHttpRequestHeader("Content-Length")
|
||||||
|
|
||||||
|
return types.ActionContinue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cloudflareProvider) OnRequestBody(ctx wrapper.HttpContext, apiName ApiName, body []byte, log wrapper.Log) (types.Action, error) {
|
||||||
|
if apiName != ApiNameChatCompletion {
|
||||||
|
return types.ActionContinue, errUnsupportedApiName
|
||||||
|
}
|
||||||
|
|
||||||
|
request := &chatCompletionRequest{}
|
||||||
|
if err := decodeChatCompletionRequest(body, request); err != nil {
|
||||||
|
return types.ActionContinue, err
|
||||||
|
}
|
||||||
|
model := request.Model
|
||||||
|
if model == "" {
|
||||||
|
return types.ActionContinue, errors.New("missing model in chat completion request")
|
||||||
|
}
|
||||||
|
ctx.SetContext(ctxKeyOriginalRequestModel, model)
|
||||||
|
mappedModel := getMappedModel(model, c.config.modelMapping, log)
|
||||||
|
if mappedModel == "" {
|
||||||
|
return types.ActionContinue, errors.New("model becomes empty after applying the configured mapping")
|
||||||
|
}
|
||||||
|
request.Model = mappedModel
|
||||||
|
ctx.SetContext(ctxKeyFinalRequestModel, request.Model)
|
||||||
|
|
||||||
|
streaming := request.Stream
|
||||||
|
if streaming {
|
||||||
|
_ = proxywasm.ReplaceHttpRequestHeader("Accept", "text/event-stream")
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.contextCache == nil {
|
||||||
|
if err := replaceJsonRequestBody(request, log); err != nil {
|
||||||
|
_ = util.SendResponse(500, "ai-proxy.cloudflare.transform_body_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
||||||
|
}
|
||||||
|
return types.ActionContinue, nil
|
||||||
|
}
|
||||||
|
err := c.contextCache.GetContent(func(content string, err error) {
|
||||||
|
defer func() {
|
||||||
|
_ = proxywasm.ResumeHttpRequest()
|
||||||
|
}()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to load context file: %v", err)
|
||||||
|
_ = util.SendResponse(500, "ai-proxy.cloudflare.load_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
||||||
|
}
|
||||||
|
insertContextMessage(request, content)
|
||||||
|
if err := replaceJsonRequestBody(request, log); err != nil {
|
||||||
|
_ = util.SendResponse(500, "ai-proxy.cloudflare.insert_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
||||||
|
}
|
||||||
|
}, log)
|
||||||
|
if err == nil {
|
||||||
|
return types.ActionPause, nil
|
||||||
|
}
|
||||||
|
return types.ActionContinue, err
|
||||||
|
}
|
||||||
@@ -45,14 +45,8 @@ func (m *deepseekProvider) OnRequestHeaders(ctx wrapper.HttpContext, apiName Api
|
|||||||
}
|
}
|
||||||
_ = util.OverwriteRequestPath(deepseekChatCompletionPath)
|
_ = util.OverwriteRequestPath(deepseekChatCompletionPath)
|
||||||
_ = util.OverwriteRequestHost(deepseekDomain)
|
_ = util.OverwriteRequestHost(deepseekDomain)
|
||||||
_ = proxywasm.ReplaceHttpRequestHeader("Authorization", "Bearer "+m.config.GetRandomToken())
|
_ = util.OverwriteRequestAuthorization("Bearer " + m.config.GetRandomToken())
|
||||||
|
_ = proxywasm.RemoveHttpRequestHeader("Content-Length")
|
||||||
if m.contextCache == nil {
|
|
||||||
ctx.DontReadRequestBody()
|
|
||||||
} else {
|
|
||||||
_ = proxywasm.RemoveHttpRequestHeader("Content-Length")
|
|
||||||
}
|
|
||||||
|
|
||||||
return types.ActionContinue, nil
|
return types.ActionContinue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,11 +67,11 @@ func (m *deepseekProvider) OnRequestBody(ctx wrapper.HttpContext, apiName ApiNam
|
|||||||
}()
|
}()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to load context file: %v", err)
|
log.Errorf("failed to load context file: %v", err)
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.deepseek.load_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
||||||
}
|
}
|
||||||
insertContextMessage(request, content)
|
insertContextMessage(request, content)
|
||||||
if err := replaceJsonRequestBody(request, log); err != nil {
|
if err := replaceJsonRequestBody(request, log); err != nil {
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.deepseek.insert_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
||||||
}
|
}
|
||||||
}, log)
|
}, log)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|||||||
@@ -43,14 +43,8 @@ func (m *groqProvider) OnRequestHeaders(ctx wrapper.HttpContext, apiName ApiName
|
|||||||
}
|
}
|
||||||
_ = util.OverwriteRequestPath(groqChatCompletionPath)
|
_ = util.OverwriteRequestPath(groqChatCompletionPath)
|
||||||
_ = util.OverwriteRequestHost(groqDomain)
|
_ = util.OverwriteRequestHost(groqDomain)
|
||||||
_ = proxywasm.ReplaceHttpRequestHeader("Authorization", "Bearer "+m.config.GetRandomToken())
|
_ = util.OverwriteRequestAuthorization("Bearer " + m.config.GetRandomToken())
|
||||||
|
_ = proxywasm.RemoveHttpRequestHeader("Content-Length")
|
||||||
if m.contextCache == nil {
|
|
||||||
ctx.DontReadRequestBody()
|
|
||||||
} else {
|
|
||||||
_ = proxywasm.RemoveHttpRequestHeader("Content-Length")
|
|
||||||
}
|
|
||||||
|
|
||||||
return types.ActionContinue, nil
|
return types.ActionContinue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,11 +65,11 @@ func (m *groqProvider) OnRequestBody(ctx wrapper.HttpContext, apiName ApiName, b
|
|||||||
}()
|
}()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to load context file: %v", err)
|
log.Errorf("failed to load context file: %v", err)
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.groq.load_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
||||||
}
|
}
|
||||||
insertContextMessage(request, content)
|
insertContextMessage(request, content)
|
||||||
if err := replaceJsonRequestBody(request, log); err != nil {
|
if err := replaceJsonRequestBody(request, log); err != nil {
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.groq.insert_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
||||||
}
|
}
|
||||||
}, log)
|
}, log)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ func (m *hunyuanProvider) OnRequestBody(ctx wrapper.HttpContext, apiName ApiName
|
|||||||
// 根据确定好的payload进行签名
|
// 根据确定好的payload进行签名
|
||||||
hunyuanBody, _ := json.Marshal(request)
|
hunyuanBody, _ := json.Marshal(request)
|
||||||
authorizedValueNew := GetTC3Authorizationcode(m.config.hunyuanAuthId, m.config.hunyuanAuthKey, timestamp, hunyuanDomain, hunyuanChatCompletionTCAction, string(hunyuanBody))
|
authorizedValueNew := GetTC3Authorizationcode(m.config.hunyuanAuthId, m.config.hunyuanAuthKey, timestamp, hunyuanDomain, hunyuanChatCompletionTCAction, string(hunyuanBody))
|
||||||
_ = proxywasm.ReplaceHttpRequestHeader(authorizationKey, authorizedValueNew)
|
_ = util.OverwriteRequestAuthorization(authorizedValueNew)
|
||||||
_ = proxywasm.ReplaceHttpRequestHeader("Accept", "*/*")
|
_ = proxywasm.ReplaceHttpRequestHeader("Accept", "*/*")
|
||||||
// log.Debugf("#debug nash5# OnRequestBody call hunyuan api using original api! signature computation done!")
|
// log.Debugf("#debug nash5# OnRequestBody call hunyuan api using original api! signature computation done!")
|
||||||
|
|
||||||
@@ -171,17 +171,17 @@ func (m *hunyuanProvider) OnRequestBody(ctx wrapper.HttpContext, apiName ApiName
|
|||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to load context file: %v", err)
|
log.Errorf("failed to load context file: %v", err)
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.hunyuan.load_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
||||||
}
|
}
|
||||||
m.insertContextMessageIntoHunyuanRequest(request, content)
|
m.insertContextMessageIntoHunyuanRequest(request, content)
|
||||||
|
|
||||||
// 因为手动插入了context内容,这里需要重新计算签名
|
// 因为手动插入了context内容,这里需要重新计算签名
|
||||||
hunyuanBody, _ := json.Marshal(request)
|
hunyuanBody, _ := json.Marshal(request)
|
||||||
authorizedValueNew := GetTC3Authorizationcode(m.config.hunyuanAuthId, m.config.hunyuanAuthKey, timestamp, hunyuanDomain, hunyuanChatCompletionTCAction, string(hunyuanBody))
|
authorizedValueNew := GetTC3Authorizationcode(m.config.hunyuanAuthId, m.config.hunyuanAuthKey, timestamp, hunyuanDomain, hunyuanChatCompletionTCAction, string(hunyuanBody))
|
||||||
_ = proxywasm.ReplaceHttpRequestHeader(authorizationKey, authorizedValueNew)
|
_ = util.OverwriteRequestAuthorization(authorizedValueNew)
|
||||||
|
|
||||||
if err := replaceJsonRequestBody(request, log); err != nil {
|
if err := replaceJsonRequestBody(request, log); err != nil {
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.hunyuan.insert_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
||||||
}
|
}
|
||||||
}, log)
|
}, log)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -234,7 +234,7 @@ func (m *hunyuanProvider) OnRequestBody(ctx wrapper.HttpContext, apiName ApiName
|
|||||||
hunyuanChatCompletionTCAction,
|
hunyuanChatCompletionTCAction,
|
||||||
string(body),
|
string(body),
|
||||||
)
|
)
|
||||||
_ = proxywasm.ReplaceHttpRequestHeader(authorizationKey, authorizedValueNew)
|
_ = util.OverwriteRequestAuthorization(authorizedValueNew)
|
||||||
// log.Debugf("#debug nash5# OnRequestBody done, body is: ", string(body))
|
// log.Debugf("#debug nash5# OnRequestBody done, body is: ", string(body))
|
||||||
|
|
||||||
// // 打印所有的headers
|
// // 打印所有的headers
|
||||||
@@ -256,7 +256,7 @@ func (m *hunyuanProvider) OnRequestBody(ctx wrapper.HttpContext, apiName ApiName
|
|||||||
}()
|
}()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to load context file: %v", err)
|
log.Errorf("failed to load context file: %v", err)
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.hunyuan.load_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
insertContextMessage(request, content)
|
insertContextMessage(request, content)
|
||||||
@@ -265,10 +265,10 @@ func (m *hunyuanProvider) OnRequestBody(ctx wrapper.HttpContext, apiName ApiName
|
|||||||
// 因为手动插入了context内容,这里需要重新计算签名
|
// 因为手动插入了context内容,这里需要重新计算签名
|
||||||
hunyuanBody, _ := json.Marshal(hunyuanRequest)
|
hunyuanBody, _ := json.Marshal(hunyuanRequest)
|
||||||
authorizedValueNew := GetTC3Authorizationcode(m.config.hunyuanAuthId, m.config.hunyuanAuthKey, timestamp, hunyuanDomain, hunyuanChatCompletionTCAction, string(hunyuanBody))
|
authorizedValueNew := GetTC3Authorizationcode(m.config.hunyuanAuthId, m.config.hunyuanAuthKey, timestamp, hunyuanDomain, hunyuanChatCompletionTCAction, string(hunyuanBody))
|
||||||
_ = proxywasm.ReplaceHttpRequestHeader(authorizationKey, authorizedValueNew)
|
_ = util.OverwriteRequestAuthorization(authorizedValueNew)
|
||||||
|
|
||||||
if err := replaceJsonRequestBody(hunyuanRequest, log); err != nil {
|
if err := replaceJsonRequestBody(hunyuanRequest, log); err != nil {
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.hunyuan.insert_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
||||||
}
|
}
|
||||||
}, log)
|
}, log)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -351,10 +351,10 @@ func (m *hunyuanProvider) convertChunkFromHunyuanToOpenAI(ctx wrapper.HttpContex
|
|||||||
openAIFormattedChunk := &chatCompletionResponse{
|
openAIFormattedChunk := &chatCompletionResponse{
|
||||||
Id: hunyuanFormattedChunk.Id,
|
Id: hunyuanFormattedChunk.Id,
|
||||||
Created: time.Now().UnixMilli() / 1000,
|
Created: time.Now().UnixMilli() / 1000,
|
||||||
Model: ctx.GetContext(ctxKeyFinalRequestModel).(string),
|
Model: ctx.GetStringContext(ctxKeyFinalRequestModel, ""),
|
||||||
SystemFingerprint: "",
|
SystemFingerprint: "",
|
||||||
Object: objectChatCompletionChunk,
|
Object: objectChatCompletionChunk,
|
||||||
Usage: chatCompletionUsage{
|
Usage: usage{
|
||||||
PromptTokens: hunyuanFormattedChunk.Usage.PromptTokens,
|
PromptTokens: hunyuanFormattedChunk.Usage.PromptTokens,
|
||||||
CompletionTokens: hunyuanFormattedChunk.Usage.CompletionTokens,
|
CompletionTokens: hunyuanFormattedChunk.Usage.CompletionTokens,
|
||||||
TotalTokens: hunyuanFormattedChunk.Usage.TotalTokens,
|
TotalTokens: hunyuanFormattedChunk.Usage.TotalTokens,
|
||||||
@@ -470,11 +470,11 @@ func (m *hunyuanProvider) buildChatCompletionResponse(ctx wrapper.HttpContext, h
|
|||||||
return &chatCompletionResponse{
|
return &chatCompletionResponse{
|
||||||
Id: hunyuanResponse.Response.Id,
|
Id: hunyuanResponse.Response.Id,
|
||||||
Created: time.Now().UnixMilli() / 1000,
|
Created: time.Now().UnixMilli() / 1000,
|
||||||
Model: ctx.GetContext(ctxKeyFinalRequestModel).(string),
|
Model: ctx.GetStringContext(ctxKeyFinalRequestModel, ""),
|
||||||
SystemFingerprint: "",
|
SystemFingerprint: "",
|
||||||
Object: objectChatCompletion,
|
Object: objectChatCompletion,
|
||||||
Choices: choices,
|
Choices: choices,
|
||||||
Usage: chatCompletionUsage{
|
Usage: usage{
|
||||||
PromptTokens: hunyuanResponse.Response.Usage.PromptTokens,
|
PromptTokens: hunyuanResponse.Response.Usage.PromptTokens,
|
||||||
CompletionTokens: hunyuanResponse.Response.Usage.CompletionTokens,
|
CompletionTokens: hunyuanResponse.Response.Usage.CompletionTokens,
|
||||||
TotalTokens: hunyuanResponse.Response.Usage.TotalTokens,
|
TotalTokens: hunyuanResponse.Response.Usage.TotalTokens,
|
||||||
|
|||||||
@@ -4,11 +4,12 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/alibaba/higress/plugins/wasm-go/extensions/ai-proxy/util"
|
"github.com/alibaba/higress/plugins/wasm-go/extensions/ai-proxy/util"
|
||||||
"github.com/alibaba/higress/plugins/wasm-go/pkg/wrapper"
|
"github.com/alibaba/higress/plugins/wasm-go/pkg/wrapper"
|
||||||
"github.com/higress-group/proxy-wasm-go-sdk/proxywasm"
|
"github.com/higress-group/proxy-wasm-go-sdk/proxywasm"
|
||||||
"github.com/higress-group/proxy-wasm-go-sdk/proxywasm/types"
|
"github.com/higress-group/proxy-wasm-go-sdk/proxywasm/types"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// minimaxProvider is the provider for minimax service.
|
// minimaxProvider is the provider for minimax service.
|
||||||
@@ -75,7 +76,7 @@ func (m *minimaxProvider) OnRequestHeaders(ctx wrapper.HttpContext, apiName ApiN
|
|||||||
return types.ActionContinue, errUnsupportedApiName
|
return types.ActionContinue, errUnsupportedApiName
|
||||||
}
|
}
|
||||||
_ = util.OverwriteRequestHost(minimaxDomain)
|
_ = util.OverwriteRequestHost(minimaxDomain)
|
||||||
_ = proxywasm.ReplaceHttpRequestHeader("Authorization", "Bearer "+m.config.GetRandomToken())
|
_ = util.OverwriteRequestAuthorization("Bearer " + m.config.GetRandomToken())
|
||||||
_ = proxywasm.RemoveHttpRequestHeader("Content-Length")
|
_ = proxywasm.RemoveHttpRequestHeader("Content-Length")
|
||||||
|
|
||||||
// Delay the header processing to allow changing streaming mode in OnRequestBody
|
// Delay the header processing to allow changing streaming mode in OnRequestBody
|
||||||
@@ -135,11 +136,11 @@ func (m *minimaxProvider) handleRequestBodyByChatCompletionPro(body []byte, log
|
|||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to load context file: %v", err)
|
log.Errorf("failed to load context file: %v", err)
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.minimax.load_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
||||||
}
|
}
|
||||||
m.setBotSettings(request, content)
|
m.setBotSettings(request, content)
|
||||||
if err := replaceJsonRequestBody(request, log); err != nil {
|
if err := replaceJsonRequestBody(request, log); err != nil {
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.minimax.insert_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
||||||
}
|
}
|
||||||
}, log)
|
}, log)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -168,11 +169,11 @@ func (m *minimaxProvider) handleRequestBodyByChatCompletionPro(body []byte, log
|
|||||||
}()
|
}()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to load context file: %v", err)
|
log.Errorf("failed to load context file: %v", err)
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.minimax.load_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
||||||
}
|
}
|
||||||
minimaxRequest := m.buildMinimaxChatCompletionV2Request(request, content)
|
minimaxRequest := m.buildMinimaxChatCompletionV2Request(request, content)
|
||||||
if err := replaceJsonRequestBody(minimaxRequest, log); err != nil {
|
if err := replaceJsonRequestBody(minimaxRequest, log); err != nil {
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to replace Request body: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.minimax.insert_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to replace Request body: %v", err))
|
||||||
}
|
}
|
||||||
}, log)
|
}, log)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -202,11 +203,11 @@ func (m *minimaxProvider) handleRequestBodyByChatCompletionV2(body []byte, log w
|
|||||||
}()
|
}()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to load context file: %v", err)
|
log.Errorf("failed to load context file: %v", err)
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.minimax.load_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
||||||
}
|
}
|
||||||
insertContextMessage(request, content)
|
insertContextMessage(request, content)
|
||||||
if err := replaceJsonRequestBody(request, log); err != nil {
|
if err := replaceJsonRequestBody(request, log); err != nil {
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.minimax.insert_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
||||||
}
|
}
|
||||||
}, log)
|
}, log)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -222,9 +223,9 @@ func (m *minimaxProvider) OnResponseHeaders(ctx wrapper.HttpContext, apiName Api
|
|||||||
return types.ActionContinue, nil
|
return types.ActionContinue, nil
|
||||||
}
|
}
|
||||||
// 模型对应接口为ChatCompletion v2,跳过OnStreamingResponseBody()和OnResponseBody()
|
// 模型对应接口为ChatCompletion v2,跳过OnStreamingResponseBody()和OnResponseBody()
|
||||||
model := ctx.GetContext(ctxKeyFinalRequestModel)
|
model := ctx.GetStringContext(ctxKeyFinalRequestModel, "")
|
||||||
if model != nil {
|
if model != "" {
|
||||||
_, ok := chatCompletionProModels[model.(string)]
|
_, ok := chatCompletionProModels[model]
|
||||||
if !ok {
|
if !ok {
|
||||||
ctx.DontReadResponseBody()
|
ctx.DontReadResponseBody()
|
||||||
return types.ActionContinue, nil
|
return types.ActionContinue, nil
|
||||||
@@ -461,7 +462,7 @@ func (m *minimaxProvider) responseV2ToOpenAI(response *minimaxChatCompletionV2Re
|
|||||||
Created: response.Created,
|
Created: response.Created,
|
||||||
Model: response.Model,
|
Model: response.Model,
|
||||||
Choices: choices,
|
Choices: choices,
|
||||||
Usage: chatCompletionUsage{
|
Usage: usage{
|
||||||
TotalTokens: int(response.Usage.TotalTokens),
|
TotalTokens: int(response.Usage.TotalTokens),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ type chatCompletionResponse struct {
|
|||||||
Model string `json:"model,omitempty"`
|
Model string `json:"model,omitempty"`
|
||||||
SystemFingerprint string `json:"system_fingerprint,omitempty"`
|
SystemFingerprint string `json:"system_fingerprint,omitempty"`
|
||||||
Object string `json:"object,omitempty"`
|
Object string `json:"object,omitempty"`
|
||||||
Usage chatCompletionUsage `json:"usage,omitempty"`
|
Usage usage `json:"usage,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type chatCompletionChoice struct {
|
type chatCompletionChoice struct {
|
||||||
@@ -70,7 +70,7 @@ type chatCompletionChoice struct {
|
|||||||
FinishReason string `json:"finish_reason,omitempty"`
|
FinishReason string `json:"finish_reason,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type chatCompletionUsage struct {
|
type usage struct {
|
||||||
PromptTokens int `json:"prompt_tokens,omitempty"`
|
PromptTokens int `json:"prompt_tokens,omitempty"`
|
||||||
CompletionTokens int `json:"completion_tokens,omitempty"`
|
CompletionTokens int `json:"completion_tokens,omitempty"`
|
||||||
TotalTokens int `json:"total_tokens,omitempty"`
|
TotalTokens int `json:"total_tokens,omitempty"`
|
||||||
@@ -140,3 +140,24 @@ func (e *streamEvent) setValue(key, value string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type embeddingsRequest struct {
|
||||||
|
Input interface{} `json:"input"`
|
||||||
|
Model string `json:"model"`
|
||||||
|
EncodingFormat string `json:"encoding_format,omitempty"`
|
||||||
|
Dimensions int `json:"dimensions,omitempty"`
|
||||||
|
User string `json:"user,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type embeddingsResponse struct {
|
||||||
|
Object string `json:"object"`
|
||||||
|
Data []embedding `json:"data"`
|
||||||
|
Model string `json:"model"`
|
||||||
|
Usage usage `json:"usage"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type embedding struct {
|
||||||
|
Object string `json:"object"`
|
||||||
|
Index int `json:"index"`
|
||||||
|
Embedding []float64 `json:"embedding"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ func (m *moonshotProvider) OnRequestHeaders(ctx wrapper.HttpContext, apiName Api
|
|||||||
}
|
}
|
||||||
_ = util.OverwriteRequestPath(moonshotChatCompletionPath)
|
_ = util.OverwriteRequestPath(moonshotChatCompletionPath)
|
||||||
_ = util.OverwriteRequestHost(moonshotDomain)
|
_ = util.OverwriteRequestHost(moonshotDomain)
|
||||||
_ = proxywasm.ReplaceHttpRequestHeader("Authorization", "Bearer "+m.config.GetRandomToken())
|
_ = util.OverwriteRequestAuthorization("Bearer " + m.config.GetRandomToken())
|
||||||
_ = proxywasm.RemoveHttpRequestHeader("Content-Length")
|
_ = proxywasm.RemoveHttpRequestHeader("Content-Length")
|
||||||
return types.ActionContinue, nil
|
return types.ActionContinue, nil
|
||||||
}
|
}
|
||||||
@@ -92,12 +92,12 @@ func (m *moonshotProvider) OnRequestBody(ctx wrapper.HttpContext, apiName ApiNam
|
|||||||
}()
|
}()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to load context file: %v", err)
|
log.Errorf("failed to load context file: %v", err)
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.moonshot.load_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = m.performChatCompletion(ctx, content, request, log)
|
err = m.performChatCompletion(ctx, content, request, log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to perform chat completion: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.moonshot.insert_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to perform chat completion: %v", err))
|
||||||
}
|
}
|
||||||
}, log)
|
}, log)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ func (m *ollamaProvider) OnRequestBody(ctx wrapper.HttpContext, apiName ApiName,
|
|||||||
if m.config.modelMapping == nil && m.contextCache == nil {
|
if m.config.modelMapping == nil && m.contextCache == nil {
|
||||||
return types.ActionContinue, nil
|
return types.ActionContinue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
request := &chatCompletionRequest{}
|
request := &chatCompletionRequest{}
|
||||||
if err := decodeChatCompletionRequest(body, request); err != nil {
|
if err := decodeChatCompletionRequest(body, request); err != nil {
|
||||||
return types.ActionContinue, err
|
return types.ActionContinue, err
|
||||||
@@ -83,7 +83,7 @@ func (m *ollamaProvider) OnRequestBody(ctx wrapper.HttpContext, apiName ApiName,
|
|||||||
return types.ActionContinue, errors.New("model becomes empty after applying the configured mapping")
|
return types.ActionContinue, errors.New("model becomes empty after applying the configured mapping")
|
||||||
}
|
}
|
||||||
request.Model = mappedModel
|
request.Model = mappedModel
|
||||||
|
|
||||||
if m.contextCache != nil {
|
if m.contextCache != nil {
|
||||||
err := m.contextCache.GetContent(func(content string, err error) {
|
err := m.contextCache.GetContent(func(content string, err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
@@ -91,11 +91,11 @@ func (m *ollamaProvider) OnRequestBody(ctx wrapper.HttpContext, apiName ApiName,
|
|||||||
}()
|
}()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to load context file: %v", err)
|
log.Errorf("failed to load context file: %v", err)
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.ollama.load_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
||||||
}
|
}
|
||||||
insertContextMessage(request, content)
|
insertContextMessage(request, content)
|
||||||
if err := replaceJsonRequestBody(request, log); err != nil {
|
if err := replaceJsonRequestBody(request, log); err != nil {
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.ollama.insert_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
||||||
}
|
}
|
||||||
}, log)
|
}, log)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -105,7 +105,7 @@ func (m *ollamaProvider) OnRequestBody(ctx wrapper.HttpContext, apiName ApiName,
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err := replaceJsonRequestBody(request, log); err != nil {
|
if err := replaceJsonRequestBody(request, log); err != nil {
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.ollama.transform_body_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
||||||
return types.ActionContinue, err
|
return types.ActionContinue, err
|
||||||
}
|
}
|
||||||
_ = proxywasm.ResumeHttpRequest()
|
_ = proxywasm.ResumeHttpRequest()
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
const (
|
const (
|
||||||
openaiDomain = "api.openai.com"
|
openaiDomain = "api.openai.com"
|
||||||
openaiChatCompletionPath = "/v1/chat/completions"
|
openaiChatCompletionPath = "/v1/chat/completions"
|
||||||
|
openaiEmbeddingsPath = "/v1/chat/embeddings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type openaiProviderInitializer struct {
|
type openaiProviderInitializer struct {
|
||||||
@@ -40,44 +41,60 @@ func (m *openaiProvider) GetProviderType() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *openaiProvider) OnRequestHeaders(ctx wrapper.HttpContext, apiName ApiName, log wrapper.Log) (types.Action, error) {
|
func (m *openaiProvider) OnRequestHeaders(ctx wrapper.HttpContext, apiName ApiName, log wrapper.Log) (types.Action, error) {
|
||||||
if apiName != ApiNameChatCompletion {
|
switch apiName {
|
||||||
return types.ActionContinue, errUnsupportedApiName
|
case ApiNameChatCompletion:
|
||||||
|
_ = util.OverwriteRequestPath(openaiChatCompletionPath)
|
||||||
|
break
|
||||||
|
case ApiNameEmbeddings:
|
||||||
|
_ = util.OverwriteRequestPath(openaiEmbeddingsPath)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
_ = util.OverwriteRequestPath(openaiChatCompletionPath)
|
_ = util.OverwriteRequestAuthorization("Bearer " + m.config.GetRandomToken())
|
||||||
_ = util.OverwriteRequestHost(openaiDomain)
|
_ = proxywasm.RemoveHttpRequestHeader("Content-Length")
|
||||||
_ = proxywasm.ReplaceHttpRequestHeader("Authorization", "Bearer "+m.config.GetRandomToken())
|
|
||||||
|
|
||||||
if m.contextCache == nil {
|
|
||||||
ctx.DontReadRequestBody()
|
|
||||||
} else {
|
|
||||||
_ = proxywasm.RemoveHttpRequestHeader("Content-Length")
|
|
||||||
}
|
|
||||||
|
|
||||||
return types.ActionContinue, nil
|
return types.ActionContinue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *openaiProvider) OnRequestBody(ctx wrapper.HttpContext, apiName ApiName, body []byte, log wrapper.Log) (types.Action, error) {
|
func (m *openaiProvider) OnRequestBody(ctx wrapper.HttpContext, apiName ApiName, body []byte, log wrapper.Log) (types.Action, error) {
|
||||||
if apiName != ApiNameChatCompletion {
|
if apiName != ApiNameChatCompletion {
|
||||||
return types.ActionContinue, errUnsupportedApiName
|
// We don't need to process the request body for other APIs.
|
||||||
}
|
|
||||||
if m.contextCache == nil {
|
|
||||||
return types.ActionContinue, nil
|
return types.ActionContinue, nil
|
||||||
}
|
}
|
||||||
request := &chatCompletionRequest{}
|
request := &chatCompletionRequest{}
|
||||||
if err := decodeChatCompletionRequest(body, request); err != nil {
|
if err := decodeChatCompletionRequest(body, request); err != nil {
|
||||||
return types.ActionContinue, err
|
return types.ActionContinue, err
|
||||||
}
|
}
|
||||||
|
bodyAltered := false
|
||||||
|
if request.Stream {
|
||||||
|
// For stream requests, we need to include usage in the response.
|
||||||
|
if request.StreamOptions == nil {
|
||||||
|
request.StreamOptions = &streamOptions{IncludeUsage: true}
|
||||||
|
bodyAltered = true
|
||||||
|
} else if !request.StreamOptions.IncludeUsage {
|
||||||
|
request.StreamOptions.IncludeUsage = true
|
||||||
|
bodyAltered = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if m.contextCache == nil {
|
||||||
|
if bodyAltered {
|
||||||
|
if err := replaceJsonRequestBody(request, log); err != nil {
|
||||||
|
_ = util.SendResponse(500, "ai-proxy.openai.set_include_usage_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return types.ActionContinue, nil
|
||||||
|
} else {
|
||||||
|
// If context cache is configured and body has been altered, the new body will be replaced when inserting the context data.
|
||||||
|
}
|
||||||
err := m.contextCache.GetContent(func(content string, err error) {
|
err := m.contextCache.GetContent(func(content string, err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
_ = proxywasm.ResumeHttpRequest()
|
_ = proxywasm.ResumeHttpRequest()
|
||||||
}()
|
}()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to load context file: %v", err)
|
log.Errorf("failed to load context file: %v", err)
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.openai.load_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
||||||
}
|
}
|
||||||
insertContextMessage(request, content)
|
insertContextMessage(request, content)
|
||||||
if err := replaceJsonRequestBody(request, log); err != nil {
|
if err := replaceJsonRequestBody(request, log); err != nil {
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.openai.insert_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
||||||
}
|
}
|
||||||
}, log)
|
}, log)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package provider
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/alibaba/higress/plugins/wasm-go/pkg/wrapper"
|
"github.com/alibaba/higress/plugins/wasm-go/pkg/wrapper"
|
||||||
"github.com/higress-group/proxy-wasm-go-sdk/proxywasm/types"
|
"github.com/higress-group/proxy-wasm-go-sdk/proxywasm/types"
|
||||||
@@ -14,22 +15,24 @@ type Pointcut string
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
ApiNameChatCompletion ApiName = "chatCompletion"
|
ApiNameChatCompletion ApiName = "chatCompletion"
|
||||||
|
ApiNameEmbeddings ApiName = "embeddings"
|
||||||
|
|
||||||
providerTypeMoonshot = "moonshot"
|
providerTypeMoonshot = "moonshot"
|
||||||
providerTypeAzure = "azure"
|
providerTypeAzure = "azure"
|
||||||
providerTypeQwen = "qwen"
|
providerTypeQwen = "qwen"
|
||||||
providerTypeOpenAI = "openai"
|
providerTypeOpenAI = "openai"
|
||||||
providerTypeGroq = "groq"
|
providerTypeGroq = "groq"
|
||||||
providerTypeBaichuan = "baichuan"
|
providerTypeBaichuan = "baichuan"
|
||||||
providerTypeYi = "yi"
|
providerTypeYi = "yi"
|
||||||
providerTypeDeepSeek = "deepseek"
|
providerTypeDeepSeek = "deepseek"
|
||||||
providerTypeZhipuAi = "zhipuai"
|
providerTypeZhipuAi = "zhipuai"
|
||||||
providerTypeOllama = "ollama"
|
providerTypeOllama = "ollama"
|
||||||
providerTypeClaude = "claude"
|
providerTypeClaude = "claude"
|
||||||
providerTypeBaidu = "baidu"
|
providerTypeBaidu = "baidu"
|
||||||
providerTypeHunyuan = "hunyuan"
|
providerTypeHunyuan = "hunyuan"
|
||||||
providerTypeStepfun = "stepfun"
|
providerTypeStepfun = "stepfun"
|
||||||
providerTypeMinimax = "minimax"
|
providerTypeMinimax = "minimax"
|
||||||
|
providerTypeCloudflare = "cloudflare"
|
||||||
|
|
||||||
protocolOpenAI = "openai"
|
protocolOpenAI = "openai"
|
||||||
protocolOriginal = "original"
|
protocolOriginal = "original"
|
||||||
@@ -65,21 +68,22 @@ var (
|
|||||||
errUnsupportedApiName = errors.New("unsupported API name")
|
errUnsupportedApiName = errors.New("unsupported API name")
|
||||||
|
|
||||||
providerInitializers = map[string]providerInitializer{
|
providerInitializers = map[string]providerInitializer{
|
||||||
providerTypeMoonshot: &moonshotProviderInitializer{},
|
providerTypeMoonshot: &moonshotProviderInitializer{},
|
||||||
providerTypeAzure: &azureProviderInitializer{},
|
providerTypeAzure: &azureProviderInitializer{},
|
||||||
providerTypeQwen: &qwenProviderInitializer{},
|
providerTypeQwen: &qwenProviderInitializer{},
|
||||||
providerTypeOpenAI: &openaiProviderInitializer{},
|
providerTypeOpenAI: &openaiProviderInitializer{},
|
||||||
providerTypeGroq: &groqProviderInitializer{},
|
providerTypeGroq: &groqProviderInitializer{},
|
||||||
providerTypeBaichuan: &baichuanProviderInitializer{},
|
providerTypeBaichuan: &baichuanProviderInitializer{},
|
||||||
providerTypeYi: &yiProviderInitializer{},
|
providerTypeYi: &yiProviderInitializer{},
|
||||||
providerTypeDeepSeek: &deepseekProviderInitializer{},
|
providerTypeDeepSeek: &deepseekProviderInitializer{},
|
||||||
providerTypeZhipuAi: &zhipuAiProviderInitializer{},
|
providerTypeZhipuAi: &zhipuAiProviderInitializer{},
|
||||||
providerTypeOllama: &ollamaProviderInitializer{},
|
providerTypeOllama: &ollamaProviderInitializer{},
|
||||||
providerTypeClaude: &claudeProviderInitializer{},
|
providerTypeClaude: &claudeProviderInitializer{},
|
||||||
providerTypeBaidu: &baiduProviderInitializer{},
|
providerTypeBaidu: &baiduProviderInitializer{},
|
||||||
providerTypeHunyuan: &hunyuanProviderInitializer{},
|
providerTypeHunyuan: &hunyuanProviderInitializer{},
|
||||||
providerTypeStepfun: &stepfunProviderInitializer{},
|
providerTypeStepfun: &stepfunProviderInitializer{},
|
||||||
providerTypeMinimax: &minimaxProviderInitializer{},
|
providerTypeMinimax: &minimaxProviderInitializer{},
|
||||||
|
providerTypeCloudflare: &cloudflareProviderInitializer{},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -156,6 +160,9 @@ type ProviderConfig struct {
|
|||||||
// @Title zh-CN 版本
|
// @Title zh-CN 版本
|
||||||
// @Description zh-CN 请求AI服务的版本,目前仅适用于Claude AI服务
|
// @Description zh-CN 请求AI服务的版本,目前仅适用于Claude AI服务
|
||||||
claudeVersion string `required:"false" yaml:"version" json:"version"`
|
claudeVersion string `required:"false" yaml:"version" json:"version"`
|
||||||
|
// @Title zh-CN Cloudflare Account ID
|
||||||
|
// @Description zh-CN 仅适用于 Cloudflare Workers AI 服务。参考:https://developers.cloudflare.com/workers-ai/get-started/rest-api/#2-run-a-model-via-api
|
||||||
|
cloudflareAccountId string `required:"false" yaml:"cloudflareAccountId" json:"cloudflareAccountId"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ProviderConfig) FromJson(json gjson.Result) {
|
func (c *ProviderConfig) FromJson(json gjson.Result) {
|
||||||
@@ -194,6 +201,7 @@ func (c *ProviderConfig) FromJson(json gjson.Result) {
|
|||||||
c.hunyuanAuthId = json.Get("hunyuanAuthId").String()
|
c.hunyuanAuthId = json.Get("hunyuanAuthId").String()
|
||||||
c.hunyuanAuthKey = json.Get("hunyuanAuthKey").String()
|
c.hunyuanAuthKey = json.Get("hunyuanAuthKey").String()
|
||||||
c.minimaxGroupId = json.Get("minimaxGroupId").String()
|
c.minimaxGroupId = json.Get("minimaxGroupId").String()
|
||||||
|
c.cloudflareAccountId = json.Get("cloudflareAccountId").String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ProviderConfig) Validate() error {
|
func (c *ProviderConfig) Validate() error {
|
||||||
@@ -247,16 +255,38 @@ func CreateProvider(pc ProviderConfig) (Provider, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getMappedModel(model string, modelMapping map[string]string, log wrapper.Log) string {
|
func getMappedModel(model string, modelMapping map[string]string, log wrapper.Log) string {
|
||||||
if modelMapping == nil || len(modelMapping) == 0 {
|
mappedModel := doGetMappedModel(model, modelMapping, log)
|
||||||
return model
|
if len(mappedModel) != 0 {
|
||||||
}
|
return mappedModel
|
||||||
if v, ok := modelMapping[model]; ok && len(v) != 0 {
|
|
||||||
log.Debugf("model %s is mapped to %s explictly", model, v)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
if v, ok := modelMapping[wildcard]; ok {
|
|
||||||
log.Debugf("model %s is mapped to %s via wildcard", model, v)
|
|
||||||
return v
|
|
||||||
}
|
}
|
||||||
return model
|
return model
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func doGetMappedModel(model string, modelMapping map[string]string, log wrapper.Log) string {
|
||||||
|
if modelMapping == nil || len(modelMapping) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := modelMapping[model]; ok {
|
||||||
|
log.Debugf("model [%s] is mapped to [%s] explictly", model, v)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range modelMapping {
|
||||||
|
if k == wildcard || !strings.HasSuffix(k, wildcard) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
k = strings.TrimSuffix(k, wildcard)
|
||||||
|
if strings.HasPrefix(model, k) {
|
||||||
|
log.Debugf("model [%s] is mapped to [%s] via prefix [%s]", model, v, k)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := modelMapping[wildcard]; ok {
|
||||||
|
log.Debugf("model [%s] is mapped to [%s] via wildcard", model, v)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -21,6 +22,7 @@ const (
|
|||||||
|
|
||||||
qwenDomain = "dashscope.aliyuncs.com"
|
qwenDomain = "dashscope.aliyuncs.com"
|
||||||
qwenChatCompletionPath = "/api/v1/services/aigc/text-generation/generation"
|
qwenChatCompletionPath = "/api/v1/services/aigc/text-generation/generation"
|
||||||
|
qwenTextEmbeddingPath = "/api/v1/services/embeddings/text-embedding/text-embedding"
|
||||||
|
|
||||||
qwenTopPMin = 0.000001
|
qwenTopPMin = 0.000001
|
||||||
qwenTopPMax = 0.999999
|
qwenTopPMax = 0.999999
|
||||||
@@ -58,15 +60,17 @@ func (m *qwenProvider) GetProviderType() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *qwenProvider) OnRequestHeaders(ctx wrapper.HttpContext, apiName ApiName, log wrapper.Log) (types.Action, error) {
|
func (m *qwenProvider) OnRequestHeaders(ctx wrapper.HttpContext, apiName ApiName, log wrapper.Log) (types.Action, error) {
|
||||||
if apiName != ApiNameChatCompletion {
|
if apiName == ApiNameChatCompletion {
|
||||||
|
_ = util.OverwriteRequestPath(qwenChatCompletionPath)
|
||||||
|
} else if apiName == ApiNameEmbeddings {
|
||||||
|
_ = util.OverwriteRequestPath(qwenTextEmbeddingPath)
|
||||||
|
} else {
|
||||||
return types.ActionContinue, errUnsupportedApiName
|
return types.ActionContinue, errUnsupportedApiName
|
||||||
}
|
}
|
||||||
_ = util.OverwriteRequestPath(qwenChatCompletionPath)
|
|
||||||
_ = util.OverwriteRequestHost(qwenDomain)
|
_ = util.OverwriteRequestHost(qwenDomain)
|
||||||
_ = proxywasm.ReplaceHttpRequestHeader("Authorization", "Bearer "+m.config.GetRandomToken())
|
_ = util.OverwriteRequestAuthorization("Bearer " + m.config.GetRandomToken())
|
||||||
|
|
||||||
if m.config.protocol == protocolOriginal && m.config.context == nil {
|
if m.config.protocol == protocolOriginal {
|
||||||
ctx.DontReadRequestBody()
|
|
||||||
return types.ActionContinue, nil
|
return types.ActionContinue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,10 +82,16 @@ func (m *qwenProvider) OnRequestHeaders(ctx wrapper.HttpContext, apiName ApiName
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *qwenProvider) OnRequestBody(ctx wrapper.HttpContext, apiName ApiName, body []byte, log wrapper.Log) (types.Action, error) {
|
func (m *qwenProvider) OnRequestBody(ctx wrapper.HttpContext, apiName ApiName, body []byte, log wrapper.Log) (types.Action, error) {
|
||||||
if apiName != ApiNameChatCompletion {
|
if apiName == ApiNameChatCompletion {
|
||||||
return types.ActionContinue, errUnsupportedApiName
|
return m.onChatCompletionRequestBody(ctx, body, log)
|
||||||
}
|
}
|
||||||
|
if apiName == ApiNameEmbeddings {
|
||||||
|
return m.onEmbeddingsRequestBody(ctx, body, log)
|
||||||
|
}
|
||||||
|
return types.ActionContinue, errUnsupportedApiName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *qwenProvider) onChatCompletionRequestBody(ctx wrapper.HttpContext, body []byte, log wrapper.Log) (types.Action, error) {
|
||||||
if m.config.protocol == protocolOriginal {
|
if m.config.protocol == protocolOriginal {
|
||||||
if m.config.context == nil {
|
if m.config.context == nil {
|
||||||
return types.ActionContinue, nil
|
return types.ActionContinue, nil
|
||||||
@@ -99,11 +109,11 @@ func (m *qwenProvider) OnRequestBody(ctx wrapper.HttpContext, apiName ApiName, b
|
|||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to load context file: %v", err)
|
log.Errorf("failed to load context file: %v", err)
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.qwen.load_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
||||||
}
|
}
|
||||||
m.insertContextMessage(request, content, false)
|
m.insertContextMessage(request, content, false)
|
||||||
if err := replaceJsonRequestBody(request, log); err != nil {
|
if err := replaceJsonRequestBody(request, log); err != nil {
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.qwen.insert_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
||||||
}
|
}
|
||||||
}, log)
|
}, log)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -152,7 +162,7 @@ func (m *qwenProvider) OnRequestBody(ctx wrapper.HttpContext, apiName ApiName, b
|
|||||||
}()
|
}()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to load context file: %v", err)
|
log.Errorf("failed to load context file: %v", err)
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.qwen.load_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
||||||
}
|
}
|
||||||
insertContextMessage(request, content)
|
insertContextMessage(request, content)
|
||||||
qwenRequest := m.buildQwenTextGenerationRequest(request, streaming)
|
qwenRequest := m.buildQwenTextGenerationRequest(request, streaming)
|
||||||
@@ -160,7 +170,7 @@ func (m *qwenProvider) OnRequestBody(ctx wrapper.HttpContext, apiName ApiName, b
|
|||||||
ctx.SetContext(ctxKeyIncrementalStreaming, qwenRequest.Parameters.IncrementalOutput)
|
ctx.SetContext(ctxKeyIncrementalStreaming, qwenRequest.Parameters.IncrementalOutput)
|
||||||
}
|
}
|
||||||
if err := replaceJsonRequestBody(qwenRequest, log); err != nil {
|
if err := replaceJsonRequestBody(qwenRequest, log); err != nil {
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.qwen.insert_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
||||||
}
|
}
|
||||||
}, log)
|
}, log)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -169,6 +179,33 @@ func (m *qwenProvider) OnRequestBody(ctx wrapper.HttpContext, apiName ApiName, b
|
|||||||
return types.ActionContinue, err
|
return types.ActionContinue, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *qwenProvider) onEmbeddingsRequestBody(ctx wrapper.HttpContext, body []byte, log wrapper.Log) (types.Action, error) {
|
||||||
|
request := &embeddingsRequest{}
|
||||||
|
if err := json.Unmarshal(body, request); err != nil {
|
||||||
|
return types.ActionContinue, fmt.Errorf("unable to unmarshal request: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("=== embeddings request: %v", request)
|
||||||
|
|
||||||
|
model := request.Model
|
||||||
|
if model == "" {
|
||||||
|
return types.ActionContinue, errors.New("missing model in the request")
|
||||||
|
}
|
||||||
|
ctx.SetContext(ctxKeyOriginalRequestModel, model)
|
||||||
|
mappedModel := getMappedModel(model, m.config.modelMapping, log)
|
||||||
|
if mappedModel == "" {
|
||||||
|
return types.ActionContinue, errors.New("model becomes empty after applying the configured mapping")
|
||||||
|
}
|
||||||
|
request.Model = mappedModel
|
||||||
|
ctx.SetContext(ctxKeyFinalRequestModel, request.Model)
|
||||||
|
|
||||||
|
if qwenRequest, err := m.buildQwenTextEmbeddingRequest(request); err == nil {
|
||||||
|
return types.ActionContinue, replaceJsonRequestBody(qwenRequest, log)
|
||||||
|
} else {
|
||||||
|
return types.ActionContinue, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (m *qwenProvider) OnResponseHeaders(ctx wrapper.HttpContext, apiName ApiName, log wrapper.Log) (types.Action, error) {
|
func (m *qwenProvider) OnResponseHeaders(ctx wrapper.HttpContext, apiName ApiName, log wrapper.Log) (types.Action, error) {
|
||||||
if m.config.protocol == protocolOriginal {
|
if m.config.protocol == protocolOriginal {
|
||||||
ctx.DontReadResponseBody()
|
ctx.DontReadResponseBody()
|
||||||
@@ -180,15 +217,16 @@ func (m *qwenProvider) OnResponseHeaders(ctx wrapper.HttpContext, apiName ApiNam
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *qwenProvider) OnStreamingResponseBody(ctx wrapper.HttpContext, name ApiName, chunk []byte, isLastChunk bool, log wrapper.Log) ([]byte, error) {
|
func (m *qwenProvider) OnStreamingResponseBody(ctx wrapper.HttpContext, name ApiName, chunk []byte, isLastChunk bool, log wrapper.Log) ([]byte, error) {
|
||||||
|
if name != ApiNameChatCompletion {
|
||||||
|
return chunk, nil
|
||||||
|
}
|
||||||
|
|
||||||
receivedBody := chunk
|
receivedBody := chunk
|
||||||
if bufferedStreamingBody, has := ctx.GetContext(ctxKeyStreamingBody).([]byte); has {
|
if bufferedStreamingBody, has := ctx.GetContext(ctxKeyStreamingBody).([]byte); has {
|
||||||
receivedBody = append(bufferedStreamingBody, chunk...)
|
receivedBody = append(bufferedStreamingBody, chunk...)
|
||||||
}
|
}
|
||||||
|
|
||||||
incrementalStreaming, err := ctx.GetContext(ctxKeyIncrementalStreaming).(bool)
|
incrementalStreaming := ctx.GetBoolContext(ctxKeyIncrementalStreaming, false)
|
||||||
if !err {
|
|
||||||
incrementalStreaming = false
|
|
||||||
}
|
|
||||||
|
|
||||||
eventStartIndex, lineStartIndex, valueStartIndex := -1, -1, -1
|
eventStartIndex, lineStartIndex, valueStartIndex := -1, -1, -1
|
||||||
|
|
||||||
@@ -264,6 +302,16 @@ func (m *qwenProvider) OnStreamingResponseBody(ctx wrapper.HttpContext, name Api
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *qwenProvider) OnResponseBody(ctx wrapper.HttpContext, apiName ApiName, body []byte, log wrapper.Log) (types.Action, error) {
|
func (m *qwenProvider) OnResponseBody(ctx wrapper.HttpContext, apiName ApiName, body []byte, log wrapper.Log) (types.Action, error) {
|
||||||
|
if apiName == ApiNameChatCompletion {
|
||||||
|
return m.onChatCompletionResponseBody(ctx, body, log)
|
||||||
|
}
|
||||||
|
if apiName == ApiNameEmbeddings {
|
||||||
|
return m.onEmbeddingsResponseBody(ctx, body, log)
|
||||||
|
}
|
||||||
|
return types.ActionContinue, errUnsupportedApiName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *qwenProvider) onChatCompletionResponseBody(ctx wrapper.HttpContext, body []byte, log wrapper.Log) (types.Action, error) {
|
||||||
qwenResponse := &qwenTextGenResponse{}
|
qwenResponse := &qwenTextGenResponse{}
|
||||||
if err := json.Unmarshal(body, qwenResponse); err != nil {
|
if err := json.Unmarshal(body, qwenResponse); err != nil {
|
||||||
return types.ActionContinue, fmt.Errorf("unable to unmarshal Qwen response: %v", err)
|
return types.ActionContinue, fmt.Errorf("unable to unmarshal Qwen response: %v", err)
|
||||||
@@ -272,6 +320,15 @@ func (m *qwenProvider) OnResponseBody(ctx wrapper.HttpContext, apiName ApiName,
|
|||||||
return types.ActionContinue, replaceJsonResponseBody(response, log)
|
return types.ActionContinue, replaceJsonResponseBody(response, log)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *qwenProvider) onEmbeddingsResponseBody(ctx wrapper.HttpContext, body []byte, log wrapper.Log) (types.Action, error) {
|
||||||
|
qwenResponse := &qwenTextEmbeddingResponse{}
|
||||||
|
if err := json.Unmarshal(body, qwenResponse); err != nil {
|
||||||
|
return types.ActionContinue, fmt.Errorf("unable to unmarshal Qwen response: %v", err)
|
||||||
|
}
|
||||||
|
response := m.buildEmbeddingsResponse(ctx, qwenResponse)
|
||||||
|
return types.ActionContinue, replaceJsonResponseBody(response, log)
|
||||||
|
}
|
||||||
|
|
||||||
func (m *qwenProvider) buildQwenTextGenerationRequest(origRequest *chatCompletionRequest, streaming bool) *qwenTextGenRequest {
|
func (m *qwenProvider) buildQwenTextGenerationRequest(origRequest *chatCompletionRequest, streaming bool) *qwenTextGenRequest {
|
||||||
messages := make([]qwenMessage, 0, len(origRequest.Messages))
|
messages := make([]qwenMessage, 0, len(origRequest.Messages))
|
||||||
for i := range origRequest.Messages {
|
for i := range origRequest.Messages {
|
||||||
@@ -324,11 +381,11 @@ func (m *qwenProvider) buildChatCompletionResponse(ctx wrapper.HttpContext, qwen
|
|||||||
return &chatCompletionResponse{
|
return &chatCompletionResponse{
|
||||||
Id: qwenResponse.RequestId,
|
Id: qwenResponse.RequestId,
|
||||||
Created: time.Now().UnixMilli() / 1000,
|
Created: time.Now().UnixMilli() / 1000,
|
||||||
Model: ctx.GetContext(ctxKeyFinalRequestModel).(string),
|
Model: ctx.GetStringContext(ctxKeyFinalRequestModel, ""),
|
||||||
SystemFingerprint: "",
|
SystemFingerprint: "",
|
||||||
Object: objectChatCompletion,
|
Object: objectChatCompletion,
|
||||||
Choices: choices,
|
Choices: choices,
|
||||||
Usage: chatCompletionUsage{
|
Usage: usage{
|
||||||
PromptTokens: qwenResponse.Usage.InputTokens,
|
PromptTokens: qwenResponse.Usage.InputTokens,
|
||||||
CompletionTokens: qwenResponse.Usage.OutputTokens,
|
CompletionTokens: qwenResponse.Usage.OutputTokens,
|
||||||
TotalTokens: qwenResponse.Usage.TotalTokens,
|
TotalTokens: qwenResponse.Usage.TotalTokens,
|
||||||
@@ -340,7 +397,7 @@ func (m *qwenProvider) buildChatCompletionStreamingResponse(ctx wrapper.HttpCont
|
|||||||
baseMessage := chatCompletionResponse{
|
baseMessage := chatCompletionResponse{
|
||||||
Id: qwenResponse.RequestId,
|
Id: qwenResponse.RequestId,
|
||||||
Created: time.Now().UnixMilli() / 1000,
|
Created: time.Now().UnixMilli() / 1000,
|
||||||
Model: ctx.GetContext(ctxKeyFinalRequestModel).(string),
|
Model: ctx.GetStringContext(ctxKeyFinalRequestModel, ""),
|
||||||
Choices: make([]chatCompletionChoice, 0),
|
Choices: make([]chatCompletionChoice, 0),
|
||||||
SystemFingerprint: "",
|
SystemFingerprint: "",
|
||||||
Object: objectChatCompletionChunk,
|
Object: objectChatCompletionChunk,
|
||||||
@@ -396,10 +453,11 @@ func (m *qwenProvider) buildChatCompletionStreamingResponse(ctx wrapper.HttpCont
|
|||||||
|
|
||||||
if finished {
|
if finished {
|
||||||
finishResponse := *&baseMessage
|
finishResponse := *&baseMessage
|
||||||
finishResponse.Choices = append(finishResponse.Choices, chatCompletionChoice{FinishReason: qwenChoice.FinishReason})
|
finishResponse.Choices = append(finishResponse.Choices, chatCompletionChoice{Delta: &chatMessage{}, FinishReason: qwenChoice.FinishReason})
|
||||||
|
|
||||||
usageResponse := *&baseMessage
|
usageResponse := *&baseMessage
|
||||||
usageResponse.Usage = chatCompletionUsage{
|
usageResponse.Choices = []chatCompletionChoice{{Delta: &chatMessage{}}}
|
||||||
|
usageResponse.Usage = usage{
|
||||||
PromptTokens: qwenResponse.Usage.InputTokens,
|
PromptTokens: qwenResponse.Usage.InputTokens,
|
||||||
CompletionTokens: qwenResponse.Usage.OutputTokens,
|
CompletionTokens: qwenResponse.Usage.OutputTokens,
|
||||||
TotalTokens: qwenResponse.Usage.TotalTokens,
|
TotalTokens: qwenResponse.Usage.TotalTokens,
|
||||||
@@ -484,6 +542,50 @@ func (m *qwenProvider) appendStreamEvent(responseBuilder *strings.Builder, event
|
|||||||
responseBuilder.WriteString("\n\n")
|
responseBuilder.WriteString("\n\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *qwenProvider) buildQwenTextEmbeddingRequest(request *embeddingsRequest) (*qwenTextEmbeddingRequest, error) {
|
||||||
|
var texts []string
|
||||||
|
if str, isString := request.Input.(string); isString {
|
||||||
|
texts = []string{str}
|
||||||
|
} else if strs, isArray := request.Input.([]interface{}); isArray {
|
||||||
|
texts = make([]string, 0, len(strs))
|
||||||
|
for _, item := range strs {
|
||||||
|
if str, isString := item.(string); isString {
|
||||||
|
texts = append(texts, str)
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("unsupported input type in array: " + reflect.TypeOf(item).String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("unsupported input type: " + reflect.TypeOf(request.Input).String())
|
||||||
|
}
|
||||||
|
return &qwenTextEmbeddingRequest{
|
||||||
|
Model: request.Model,
|
||||||
|
Input: qwenTextEmbeddingInput{
|
||||||
|
Texts: texts,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *qwenProvider) buildEmbeddingsResponse(ctx wrapper.HttpContext, qwenResponse *qwenTextEmbeddingResponse) *embeddingsResponse {
|
||||||
|
data := make([]embedding, 0, len(qwenResponse.Output.Embeddings))
|
||||||
|
for _, qwenEmbedding := range qwenResponse.Output.Embeddings {
|
||||||
|
data = append(data, embedding{
|
||||||
|
Object: "embedding",
|
||||||
|
Index: qwenEmbedding.TextIndex,
|
||||||
|
Embedding: qwenEmbedding.Embedding,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return &embeddingsResponse{
|
||||||
|
Object: "list",
|
||||||
|
Data: data,
|
||||||
|
Model: ctx.GetContext(ctxKeyFinalRequestModel).(string),
|
||||||
|
Usage: usage{
|
||||||
|
PromptTokens: qwenResponse.Usage.TotalTokens,
|
||||||
|
TotalTokens: qwenResponse.Usage.TotalTokens,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type qwenTextGenRequest struct {
|
type qwenTextGenRequest struct {
|
||||||
Model string `json:"model"`
|
Model string `json:"model"`
|
||||||
Input qwenTextGenInput `json:"input"`
|
Input qwenTextGenInput `json:"input"`
|
||||||
@@ -510,7 +612,7 @@ type qwenTextGenParameters struct {
|
|||||||
type qwenTextGenResponse struct {
|
type qwenTextGenResponse struct {
|
||||||
RequestId string `json:"request_id"`
|
RequestId string `json:"request_id"`
|
||||||
Output qwenTextGenOutput `json:"output"`
|
Output qwenTextGenOutput `json:"output"`
|
||||||
Usage qwenTextGenUsage `json:"usage"`
|
Usage qwenUsage `json:"usage"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type qwenTextGenOutput struct {
|
type qwenTextGenOutput struct {
|
||||||
@@ -523,7 +625,7 @@ type qwenTextGenChoice struct {
|
|||||||
Message qwenMessage `json:"message"`
|
Message qwenMessage `json:"message"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type qwenTextGenUsage struct {
|
type qwenUsage struct {
|
||||||
InputTokens int `json:"input_tokens"`
|
InputTokens int `json:"input_tokens"`
|
||||||
OutputTokens int `json:"output_tokens"`
|
OutputTokens int `json:"output_tokens"`
|
||||||
TotalTokens int `json:"total_tokens"`
|
TotalTokens int `json:"total_tokens"`
|
||||||
@@ -536,6 +638,36 @@ type qwenMessage struct {
|
|||||||
ToolCalls []toolCall `json:"tool_calls,omitempty"`
|
ToolCalls []toolCall `json:"tool_calls,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type qwenTextEmbeddingRequest struct {
|
||||||
|
Model string `json:"model"`
|
||||||
|
Input qwenTextEmbeddingInput `json:"input"`
|
||||||
|
Parameters qwenTextEmbeddingParameters `json:"parameters,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type qwenTextEmbeddingInput struct {
|
||||||
|
Texts []string `json:"texts"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type qwenTextEmbeddingParameters struct {
|
||||||
|
TextType string `json:"text_type,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type qwenTextEmbeddingResponse struct {
|
||||||
|
RequestId string `json:"request_id"`
|
||||||
|
Output qwenTextEmbeddingOutput `json:"output"`
|
||||||
|
Usage qwenUsage `json:"usage"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type qwenTextEmbeddingOutput struct {
|
||||||
|
RequestId string `json:"request_id"`
|
||||||
|
Embeddings []qwenTextEmbeddings `json:"embeddings"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type qwenTextEmbeddings struct {
|
||||||
|
TextIndex int `json:"text_index"`
|
||||||
|
Embedding []float64 `json:"embedding"`
|
||||||
|
}
|
||||||
|
|
||||||
func qwenMessageToChatMessage(qwenMessage qwenMessage) chatMessage {
|
func qwenMessageToChatMessage(qwenMessage qwenMessage) chatMessage {
|
||||||
return chatMessage{
|
return chatMessage{
|
||||||
Name: qwenMessage.Name,
|
Name: qwenMessage.Name,
|
||||||
|
|||||||
@@ -43,14 +43,8 @@ func (m *stepfunProvider) OnRequestHeaders(ctx wrapper.HttpContext, apiName ApiN
|
|||||||
}
|
}
|
||||||
_ = util.OverwriteRequestPath(stepfunChatCompletionPath)
|
_ = util.OverwriteRequestPath(stepfunChatCompletionPath)
|
||||||
_ = util.OverwriteRequestHost(stepfunDomain)
|
_ = util.OverwriteRequestHost(stepfunDomain)
|
||||||
_ = proxywasm.ReplaceHttpRequestHeader("Authorization", "Bearer "+m.config.GetRandomToken())
|
_ = util.OverwriteRequestAuthorization("Bearer " + m.config.GetRandomToken())
|
||||||
|
_ = proxywasm.RemoveHttpRequestHeader("Content-Length")
|
||||||
if m.contextCache == nil {
|
|
||||||
ctx.DontReadRequestBody()
|
|
||||||
} else {
|
|
||||||
_ = proxywasm.RemoveHttpRequestHeader("Content-Length")
|
|
||||||
}
|
|
||||||
|
|
||||||
return types.ActionContinue, nil
|
return types.ActionContinue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,11 +65,11 @@ func (m *stepfunProvider) OnRequestBody(ctx wrapper.HttpContext, apiName ApiName
|
|||||||
}()
|
}()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to load context file: %v", err)
|
log.Errorf("failed to load context file: %v", err)
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.stepfun.load_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
||||||
}
|
}
|
||||||
insertContextMessage(request, content)
|
insertContextMessage(request, content)
|
||||||
if err := replaceJsonRequestBody(request, log); err != nil {
|
if err := replaceJsonRequestBody(request, log); err != nil {
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.stepfun.insert_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
||||||
}
|
}
|
||||||
}, log)
|
}, log)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|||||||
@@ -43,14 +43,8 @@ func (m *yiProvider) OnRequestHeaders(ctx wrapper.HttpContext, apiName ApiName,
|
|||||||
}
|
}
|
||||||
_ = util.OverwriteRequestPath(yiChatCompletionPath)
|
_ = util.OverwriteRequestPath(yiChatCompletionPath)
|
||||||
_ = util.OverwriteRequestHost(yiDomain)
|
_ = util.OverwriteRequestHost(yiDomain)
|
||||||
_ = proxywasm.ReplaceHttpRequestHeader("Authorization", "Bearer "+m.config.GetRandomToken())
|
_ = util.OverwriteRequestAuthorization("Bearer " + m.config.GetRandomToken())
|
||||||
|
_ = proxywasm.RemoveHttpRequestHeader("Content-Length")
|
||||||
if m.contextCache == nil {
|
|
||||||
ctx.DontReadRequestBody()
|
|
||||||
} else {
|
|
||||||
_ = proxywasm.RemoveHttpRequestHeader("Content-Length")
|
|
||||||
}
|
|
||||||
|
|
||||||
return types.ActionContinue, nil
|
return types.ActionContinue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,11 +65,11 @@ func (m *yiProvider) OnRequestBody(ctx wrapper.HttpContext, apiName ApiName, bod
|
|||||||
}()
|
}()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to load context file: %v", err)
|
log.Errorf("failed to load context file: %v", err)
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.yi.load_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
||||||
}
|
}
|
||||||
insertContextMessage(request, content)
|
insertContextMessage(request, content)
|
||||||
if err := replaceJsonRequestBody(request, log); err != nil {
|
if err := replaceJsonRequestBody(request, log); err != nil {
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.yi.insert_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
||||||
}
|
}
|
||||||
}, log)
|
}, log)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|||||||
@@ -42,14 +42,8 @@ func (m *zhipuAiProvider) OnRequestHeaders(ctx wrapper.HttpContext, apiName ApiN
|
|||||||
}
|
}
|
||||||
_ = util.OverwriteRequestPath(zhipuAiChatCompletionPath)
|
_ = util.OverwriteRequestPath(zhipuAiChatCompletionPath)
|
||||||
_ = util.OverwriteRequestHost(zhipuAiDomain)
|
_ = util.OverwriteRequestHost(zhipuAiDomain)
|
||||||
_ = proxywasm.ReplaceHttpRequestHeader("Authorization", "Bearer "+m.config.GetRandomToken())
|
_ = util.OverwriteRequestAuthorization("Bearer " + m.config.GetRandomToken())
|
||||||
|
_ = proxywasm.RemoveHttpRequestHeader("Content-Length")
|
||||||
if m.contextCache == nil {
|
|
||||||
ctx.DontReadRequestBody()
|
|
||||||
} else {
|
|
||||||
_ = proxywasm.RemoveHttpRequestHeader("Content-Length")
|
|
||||||
}
|
|
||||||
|
|
||||||
return types.ActionContinue, nil
|
return types.ActionContinue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,11 +64,11 @@ func (m *zhipuAiProvider) OnRequestBody(ctx wrapper.HttpContext, apiName ApiName
|
|||||||
}()
|
}()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to load context file: %v", err)
|
log.Errorf("failed to load context file: %v", err)
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.zhihupai.load_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to load context file: %v", err))
|
||||||
}
|
}
|
||||||
insertContextMessage(request, content)
|
insertContextMessage(request, content)
|
||||||
if err := replaceJsonRequestBody(request, log); err != nil {
|
if err := replaceJsonRequestBody(request, log); err != nil {
|
||||||
_ = util.SendResponse(500, util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
_ = util.SendResponse(500, "ai-proxy.zhihupai.insert_ctx_failed", util.MimeTypeTextPlain, fmt.Sprintf("failed to replace request body: %v", err))
|
||||||
}
|
}
|
||||||
}, log)
|
}, log)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ const (
|
|||||||
MimeTypeApplicationJson = "application/json"
|
MimeTypeApplicationJson = "application/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
func SendResponse(statusCode uint32, contentType, body string) error {
|
func SendResponse(statusCode uint32, statusCodeDetails string, contentType, body string) error {
|
||||||
return proxywasm.SendHttpResponse(statusCode, CreateHeaders(HeaderContentType, contentType), []byte(body), -1)
|
return proxywasm.SendHttpResponseWithDetail(statusCode, statusCodeDetails, CreateHeaders(HeaderContentType, contentType), []byte(body), -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateHeaders(kvs ...string) [][2]string {
|
func CreateHeaders(kvs ...string) [][2]string {
|
||||||
@@ -34,3 +34,12 @@ func OverwriteRequestPath(path string) error {
|
|||||||
}
|
}
|
||||||
return proxywasm.ReplaceHttpRequestHeader(":path", path)
|
return proxywasm.ReplaceHttpRequestHeader(":path", path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func OverwriteRequestAuthorization(credential string) error {
|
||||||
|
if exist, _ := proxywasm.GetHttpRequestHeader("X-HI-ORIGINAL-AUTH"); exist == "" {
|
||||||
|
if originAuth, err := proxywasm.GetHttpRequestHeader("Authorization"); err == nil {
|
||||||
|
_ = proxywasm.AddHttpRequestHeader("X-HI-ORIGINAL-AUTH", originAuth)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return proxywasm.ReplaceHttpRequestHeader("Authorization", credential)
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ module ai-rag
|
|||||||
|
|
||||||
go 1.18
|
go 1.18
|
||||||
|
|
||||||
|
replace github.com/alibaba/higress/plugins/wasm-go => ../..
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/alibaba/higress/plugins/wasm-go v1.3.5
|
github.com/alibaba/higress/plugins/wasm-go v1.3.5
|
||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240226064518-b3dc4646a35a
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240711023527-ba358c48772f
|
||||||
github.com/tidwall/gjson v1.14.3
|
github.com/tidwall/gjson v1.14.3
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520 h1:IHDghbG
|
|||||||
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520/go.mod h1:Nz8ORLaFiLWotg6GeKlJMhv8cci8mM43uEnLA5t8iew=
|
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520/go.mod h1:Nz8ORLaFiLWotg6GeKlJMhv8cci8mM43uEnLA5t8iew=
|
||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240226064518-b3dc4646a35a h1:luYRvxLTE1xYxrXYj7nmjd1U0HHh8pUPiKfdZ0MhCGE=
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240226064518-b3dc4646a35a h1:luYRvxLTE1xYxrXYj7nmjd1U0HHh8pUPiKfdZ0MhCGE=
|
||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240226064518-b3dc4646a35a/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240226064518-b3dc4646a35a/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
||||||
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240711023527-ba358c48772f/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
||||||
github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo=
|
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/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
|||||||
1
plugins/wasm-go/extensions/ai-security-guard/.buildrc
Normal file
1
plugins/wasm-go/extensions/ai-security-guard/.buildrc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
EXTRA_TAGS=proxy_wasm_version_0_2_100
|
||||||
@@ -2,9 +2,11 @@ module myplugin
|
|||||||
|
|
||||||
go 1.18
|
go 1.18
|
||||||
|
|
||||||
|
replace github.com/alibaba/higress/plugins/wasm-go => ../..
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/alibaba/higress/plugins/wasm-go v1.3.6-0.20240522012622-fc6a6aad8906
|
github.com/alibaba/higress/plugins/wasm-go v1.3.6-0.20240522012622-fc6a6aad8906
|
||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240711023527-ba358c48772f
|
||||||
github.com/tidwall/gjson v1.14.3
|
github.com/tidwall/gjson v1.14.3
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520 h1:IHDghbG
|
|||||||
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520/go.mod h1:Nz8ORLaFiLWotg6GeKlJMhv8cci8mM43uEnLA5t8iew=
|
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520/go.mod h1:Nz8ORLaFiLWotg6GeKlJMhv8cci8mM43uEnLA5t8iew=
|
||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc h1:t2AT8zb6N/59Y78lyRWedVoVWHNRSCBh0oWCC+bluTQ=
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc h1:t2AT8zb6N/59Y78lyRWedVoVWHNRSCBh0oWCC+bluTQ=
|
||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
||||||
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240711023527-ba358c48772f/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
||||||
github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo=
|
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/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
|||||||
@@ -145,8 +145,9 @@ func onHttpRequestBody(ctx wrapper.HttpContext, config AISecurityConfig, body []
|
|||||||
Message: respAdvice.Array()[0].Get("Answer").String(),
|
Message: respAdvice.Array()[0].Get("Answer").String(),
|
||||||
}
|
}
|
||||||
jsonData, _ := json.MarshalIndent(sr, "", " ")
|
jsonData, _ := json.MarshalIndent(sr, "", " ")
|
||||||
proxywasm.SetProperty([]string{"risklabel"}, []byte(respResult.Array()[0].Get("Label").String()))
|
label := respResult.Array()[0].Get("Label").String()
|
||||||
proxywasm.SendHttpResponse(403, [][2]string{{"content-type", "application/json"}}, jsonData, -1)
|
proxywasm.SetProperty([]string{"risklabel"}, []byte(label))
|
||||||
|
proxywasm.SendHttpResponseWithDetail(403, "ai-security-guard.label."+label, [][2]string{{"content-type", "application/json"}}, jsonData, -1)
|
||||||
} else if respResult.Array()[0].Get("Label").String() != "nonLabel" {
|
} else if respResult.Array()[0].Get("Label").String() != "nonLabel" {
|
||||||
sr := StandardResponse{
|
sr := StandardResponse{
|
||||||
Code: 403,
|
Code: 403,
|
||||||
@@ -155,7 +156,7 @@ func onHttpRequestBody(ctx wrapper.HttpContext, config AISecurityConfig, body []
|
|||||||
}
|
}
|
||||||
jsonData, _ := json.MarshalIndent(sr, "", " ")
|
jsonData, _ := json.MarshalIndent(sr, "", " ")
|
||||||
proxywasm.SetProperty([]string{"risklabel"}, []byte(respResult.Array()[0].Get("Label").String()))
|
proxywasm.SetProperty([]string{"risklabel"}, []byte(respResult.Array()[0].Get("Label").String()))
|
||||||
proxywasm.SendHttpResponse(403, [][2]string{{"content-type", "application/json"}}, jsonData, -1)
|
proxywasm.SendHttpResponseWithDetail(403, "ai-security-guard.risk_detected", [][2]string{{"content-type", "application/json"}}, jsonData, -1)
|
||||||
} else {
|
} else {
|
||||||
proxywasm.ResumeHttpRequest()
|
proxywasm.ResumeHttpRequest()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ module ai-statistics
|
|||||||
|
|
||||||
go 1.18
|
go 1.18
|
||||||
|
|
||||||
|
replace github.com/alibaba/higress/plugins/wasm-go => ../..
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/alibaba/higress/plugins/wasm-go v1.3.6-0.20240522012622-fc6a6aad8906
|
github.com/alibaba/higress/plugins/wasm-go v1.3.6-0.20240522012622-fc6a6aad8906
|
||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240711023527-ba358c48772f
|
||||||
github.com/tidwall/gjson v1.14.3
|
github.com/tidwall/gjson v1.14.3
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520 h1:IHDghbG
|
|||||||
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520/go.mod h1:Nz8ORLaFiLWotg6GeKlJMhv8cci8mM43uEnLA5t8iew=
|
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520/go.mod h1:Nz8ORLaFiLWotg6GeKlJMhv8cci8mM43uEnLA5t8iew=
|
||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc h1:t2AT8zb6N/59Y78lyRWedVoVWHNRSCBh0oWCC+bluTQ=
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc h1:t2AT8zb6N/59Y78lyRWedVoVWHNRSCBh0oWCC+bluTQ=
|
||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
||||||
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240711023527-ba358c48772f/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
||||||
github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo=
|
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/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -52,79 +53,66 @@ func onHttpResponseHeaders(ctx wrapper.HttpContext, config AIStatisticsConfig, l
|
|||||||
return types.ActionContinue
|
return types.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
func getLastChunk(data []byte) []byte {
|
func onHttpStreamingBody(ctx wrapper.HttpContext, config AIStatisticsConfig, data []byte, endOfStream bool, log wrapper.Log) []byte {
|
||||||
chunks := strings.Split(strings.TrimSpace(string(data)), "\n\n")
|
model, inputToken, outputToken, ok := getUsage(data)
|
||||||
length := len(chunks)
|
if !ok {
|
||||||
if length < 2 {
|
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
// ai-proxy append extra usage chunk
|
setFilterStateData(model, inputToken, outputToken, log)
|
||||||
return []byte(chunks[length-1])
|
incrementCounter(config, model, inputToken, outputToken, log)
|
||||||
}
|
|
||||||
|
|
||||||
func onHttpStreamingBody(ctx wrapper.HttpContext, config AIStatisticsConfig, data []byte, endOfStream bool, log wrapper.Log) []byte {
|
|
||||||
lastChunk := getLastChunk(data)
|
|
||||||
modelObj := gjson.GetBytes(lastChunk, "model")
|
|
||||||
inputTokenObj := gjson.GetBytes(lastChunk, "usage.prompt_tokens")
|
|
||||||
outputTokenObj := gjson.GetBytes(lastChunk, "usage.completion_tokens")
|
|
||||||
if modelObj.Exists() && inputTokenObj.Exists() && outputTokenObj.Exists() {
|
|
||||||
ctx.SetContext("model", modelObj.String())
|
|
||||||
ctx.SetContext("input_token", inputTokenObj.Int())
|
|
||||||
ctx.SetContext("output_token", outputTokenObj.Int())
|
|
||||||
}
|
|
||||||
|
|
||||||
if endOfStream {
|
|
||||||
var route, cluster string
|
|
||||||
if raw, err := proxywasm.GetProperty([]string{"route_name"}); err == nil {
|
|
||||||
route = string(raw)
|
|
||||||
}
|
|
||||||
if raw, err := proxywasm.GetProperty([]string{"cluster_name"}); err == nil {
|
|
||||||
cluster = string(raw)
|
|
||||||
}
|
|
||||||
model, ok := ctx.GetContext("model").(string)
|
|
||||||
if !ok {
|
|
||||||
log.Error("Get model failed!")
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
inputToken, ok := ctx.GetContext("input_token").(int64)
|
|
||||||
if !ok {
|
|
||||||
log.Error("Get input_token failed!")
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
outputToken, ok := ctx.GetContext("output_token").(int64)
|
|
||||||
if !ok {
|
|
||||||
log.Error("Get output_token failed!")
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
config.incrementCounter("route."+route+".upstream."+cluster+".model."+model+".input_token", uint64(inputToken), log)
|
|
||||||
config.incrementCounter("route."+route+".upstream."+cluster+".model."+model+".output_token", uint64(outputToken), log)
|
|
||||||
proxywasm.SetProperty([]string{"model"}, []byte(model))
|
|
||||||
proxywasm.SetProperty([]string{"input_token"}, []byte(fmt.Sprint(inputToken)))
|
|
||||||
proxywasm.SetProperty([]string{"output_token"}, []byte(fmt.Sprint(outputToken)))
|
|
||||||
}
|
|
||||||
|
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
func onHttpResponseBody(ctx wrapper.HttpContext, config AIStatisticsConfig, body []byte, log wrapper.Log) types.Action {
|
func onHttpResponseBody(ctx wrapper.HttpContext, config AIStatisticsConfig, body []byte, log wrapper.Log) types.Action {
|
||||||
modeObj := gjson.GetBytes(body, "model")
|
model, inputToken, outputToken, ok := getUsage(body)
|
||||||
inputTokenObj := gjson.GetBytes(body, "usage.prompt_tokens")
|
if !ok {
|
||||||
outputTokenObj := gjson.GetBytes(body, "usage.completion_tokens")
|
|
||||||
if !modeObj.Exists() {
|
|
||||||
log.Error("Get model failed")
|
|
||||||
return types.ActionContinue
|
return types.ActionContinue
|
||||||
}
|
}
|
||||||
if !inputTokenObj.Exists() {
|
setFilterStateData(model, inputToken, outputToken, log)
|
||||||
log.Error("Get input_token failed")
|
incrementCounter(config, model, inputToken, outputToken, log)
|
||||||
return types.ActionContinue
|
return types.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUsage(data []byte) (model string, inputTokenUsage int64, outputTokenUsage int64, ok bool) {
|
||||||
|
chunks := bytes.Split(bytes.TrimSpace(data), []byte("\n\n"))
|
||||||
|
for _, chunk := range chunks {
|
||||||
|
// the feature strings are used to identify the usage data, like:
|
||||||
|
// {"model":"gpt2","usage":{"prompt_tokens":1,"completion_tokens":1}}
|
||||||
|
if !bytes.Contains(chunk, []byte("prompt_tokens")) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !bytes.Contains(chunk, []byte("completion_tokens")) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
modelObj := gjson.GetBytes(chunk, "model")
|
||||||
|
inputTokenObj := gjson.GetBytes(chunk, "usage.prompt_tokens")
|
||||||
|
outputTokenObj := gjson.GetBytes(chunk, "usage.completion_tokens")
|
||||||
|
if modelObj.Exists() && inputTokenObj.Exists() && outputTokenObj.Exists() {
|
||||||
|
model = modelObj.String()
|
||||||
|
inputTokenUsage = inputTokenObj.Int()
|
||||||
|
outputTokenUsage = outputTokenObj.Int()
|
||||||
|
ok = true
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if !outputTokenObj.Exists() {
|
return
|
||||||
log.Error("Get output_token failed")
|
}
|
||||||
return types.ActionContinue
|
|
||||||
|
// setFilterData sets the input_token and output_token in the filter state.
|
||||||
|
// ai-token-ratelimit will use these values to calculate the total token usage.
|
||||||
|
func setFilterStateData(model string, inputToken int64, outputToken int64, log wrapper.Log) {
|
||||||
|
if e := proxywasm.SetProperty([]string{"model"}, []byte(model)); e != nil {
|
||||||
|
log.Errorf("failed to set model in filter state: %v", e)
|
||||||
}
|
}
|
||||||
model := modeObj.String()
|
if e := proxywasm.SetProperty([]string{"input_token"}, []byte(fmt.Sprintf("%d", inputToken))); e != nil {
|
||||||
inputToken := inputTokenObj.Int()
|
log.Errorf("failed to set input_token in filter state: %v", e)
|
||||||
outputToken := outputTokenObj.Int()
|
}
|
||||||
|
if e := proxywasm.SetProperty([]string{"output_token"}, []byte(fmt.Sprintf("%d", outputToken))); e != nil {
|
||||||
|
log.Errorf("failed to set output_token in filter state: %v", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func incrementCounter(config AIStatisticsConfig, model string, inputToken int64, outputToken int64, log wrapper.Log) {
|
||||||
var route, cluster string
|
var route, cluster string
|
||||||
if raw, err := proxywasm.GetProperty([]string{"route_name"}); err == nil {
|
if raw, err := proxywasm.GetProperty([]string{"route_name"}); err == nil {
|
||||||
route = string(raw)
|
route = string(raw)
|
||||||
@@ -134,10 +122,4 @@ func onHttpResponseBody(ctx wrapper.HttpContext, config AIStatisticsConfig, body
|
|||||||
}
|
}
|
||||||
config.incrementCounter("route."+route+".upstream."+cluster+".model."+model+".input_token", uint64(inputToken), log)
|
config.incrementCounter("route."+route+".upstream."+cluster+".model."+model+".input_token", uint64(inputToken), log)
|
||||||
config.incrementCounter("route."+route+".upstream."+cluster+".model."+model+".output_token", uint64(outputToken), log)
|
config.incrementCounter("route."+route+".upstream."+cluster+".model."+model+".output_token", uint64(outputToken), log)
|
||||||
|
|
||||||
proxywasm.SetProperty([]string{"model"}, []byte(model))
|
|
||||||
proxywasm.SetProperty([]string{"input_token"}, []byte(fmt.Sprint(inputToken)))
|
|
||||||
proxywasm.SetProperty([]string{"output_token"}, []byte(fmt.Sprint(outputToken)))
|
|
||||||
|
|
||||||
return types.ActionContinue
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ module ai-token-ratelimit
|
|||||||
|
|
||||||
go 1.18
|
go 1.18
|
||||||
|
|
||||||
|
replace github.com/alibaba/higress/plugins/wasm-go => ../..
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/alibaba/higress/plugins/wasm-go v1.4.1-0.20240617024146-5f150179637c
|
github.com/alibaba/higress/plugins/wasm-go v1.4.1-0.20240617024146-5f150179637c
|
||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240711023527-ba358c48772f
|
||||||
github.com/tidwall/gjson v1.14.3
|
github.com/tidwall/gjson v1.14.3
|
||||||
github.com/wasilibs/go-re2 v1.5.3
|
github.com/wasilibs/go-re2 v1.5.3
|
||||||
github.com/zmap/go-iptree v0.0.0-20210731043055-d4e632617837
|
github.com/zmap/go-iptree v0.0.0-20210731043055-d4e632617837
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
github.com/alibaba/higress/plugins/wasm-go v1.4.1-0.20240617024146-5f150179637c h1:wKCSg4rYfwkZrMk7tYY7navjgcHCMZjcgFrCsjLQBmg=
|
|
||||||
github.com/alibaba/higress/plugins/wasm-go v1.4.1-0.20240617024146-5f150179637c/go.mod h1:10jQXKsYFUF7djs+Oy7t82f4dbie9pISfP9FJwpPLuk=
|
|
||||||
github.com/asergeyev/nradix v0.0.0-20170505151046-3872ab85bb56 h1:Wi5Tgn8K+jDcBYL+dIMS1+qXYH2r7tpRAyBgqrWfQtw=
|
github.com/asergeyev/nradix v0.0.0-20170505151046-3872ab85bb56 h1:Wi5Tgn8K+jDcBYL+dIMS1+qXYH2r7tpRAyBgqrWfQtw=
|
||||||
github.com/asergeyev/nradix v0.0.0-20170505151046-3872ab85bb56/go.mod h1:8BhOLuqtSuT5NZtZMwfvEibi09RO3u79uqfHZzfDTR4=
|
github.com/asergeyev/nradix v0.0.0-20170505151046-3872ab85bb56/go.mod h1:8BhOLuqtSuT5NZtZMwfvEibi09RO3u79uqfHZzfDTR4=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
@@ -7,8 +5,8 @@ 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/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520 h1:IHDghbGQ2DTIXHBHxWfqCYQW1fKjyJ/I7W1pMyUDeEA=
|
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520 h1:IHDghbGQ2DTIXHBHxWfqCYQW1fKjyJ/I7W1pMyUDeEA=
|
||||||
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520/go.mod h1:Nz8ORLaFiLWotg6GeKlJMhv8cci8mM43uEnLA5t8iew=
|
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520/go.mod h1:Nz8ORLaFiLWotg6GeKlJMhv8cci8mM43uEnLA5t8iew=
|
||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc h1:t2AT8zb6N/59Y78lyRWedVoVWHNRSCBh0oWCC+bluTQ=
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240711023527-ba358c48772f h1:ZIiIBRvIw62gA5MJhuwp1+2wWbqL9IGElQ499rUsYYg=
|
||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240711023527-ba358c48772f/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
||||||
github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo=
|
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/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
|||||||
@@ -114,6 +114,7 @@ func onHttpRequestHeaders(ctx wrapper.HttpContext, config ClusterKeyRateLimitCon
|
|||||||
resultArray := response.Array()
|
resultArray := response.Array()
|
||||||
if len(resultArray) != 3 {
|
if len(resultArray) != 3 {
|
||||||
log.Errorf("redis response parse error, response: %v", response)
|
log.Errorf("redis response parse error, response: %v", response)
|
||||||
|
proxywasm.ResumeHttpRequest()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
context := LimitContext{
|
context := LimitContext{
|
||||||
@@ -162,12 +163,7 @@ func onHttpStreamingBody(ctx wrapper.HttpContext, config ClusterKeyRateLimitConf
|
|||||||
keys := []interface{}{limitRedisContext.key}
|
keys := []interface{}{limitRedisContext.key}
|
||||||
args := []interface{}{limitRedisContext.count, limitRedisContext.window, inputToken + outputToken}
|
args := []interface{}{limitRedisContext.count, limitRedisContext.window, inputToken + outputToken}
|
||||||
|
|
||||||
err = config.redisClient.Eval(ResponsePhaseFixedWindowScript, 1, keys, args, func(response resp.Value) {
|
err = config.redisClient.Eval(ResponsePhaseFixedWindowScript, 1, keys, args, nil)
|
||||||
if response.Error() != nil {
|
|
||||||
log.Errorf("call Eval error: %v", response.Error())
|
|
||||||
}
|
|
||||||
proxywasm.ResumeHttpResponse()
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("redis call failed: %v", err)
|
log.Errorf("redis call failed: %v", err)
|
||||||
return data
|
return data
|
||||||
@@ -298,6 +294,6 @@ func getDownStreamIp(rule LimitRuleItem) (net.IP, error) {
|
|||||||
func rejected(config ClusterKeyRateLimitConfig, context LimitContext) {
|
func rejected(config ClusterKeyRateLimitConfig, context LimitContext) {
|
||||||
headers := make(map[string][]string)
|
headers := make(map[string][]string)
|
||||||
headers[RateLimitResetHeader] = []string{strconv.Itoa(context.reset)}
|
headers[RateLimitResetHeader] = []string{strconv.Itoa(context.reset)}
|
||||||
_ = proxywasm.SendHttpResponse(
|
_ = proxywasm.SendHttpResponseWithDetail(
|
||||||
config.rejectedCode, reconvertHeaders(headers), []byte(config.rejectedMsg), -1)
|
config.rejectedCode, "ai-token-ratelimit.rejected", reconvertHeaders(headers), []byte(config.rejectedMsg), -1)
|
||||||
}
|
}
|
||||||
|
|||||||
1
plugins/wasm-go/extensions/ai-transformer/.buildrc
Normal file
1
plugins/wasm-go/extensions/ai-transformer/.buildrc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
EXTRA_TAGS=proxy_wasm_version_0_2_100
|
||||||
@@ -2,9 +2,11 @@ module ai-transformer
|
|||||||
|
|
||||||
go 1.18
|
go 1.18
|
||||||
|
|
||||||
|
replace github.com/alibaba/higress/plugins/wasm-go => ../..
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/alibaba/higress/plugins/wasm-go v1.4.0
|
github.com/alibaba/higress/plugins/wasm-go v1.4.0
|
||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240711023527-ba358c48772f
|
||||||
github.com/tidwall/gjson v1.14.3
|
github.com/tidwall/gjson v1.14.3
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240226064518-b3dc4646a35a h1
|
|||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240226064518-b3dc4646a35a/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240226064518-b3dc4646a35a/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc h1:t2AT8zb6N/59Y78lyRWedVoVWHNRSCBh0oWCC+bluTQ=
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc h1:t2AT8zb6N/59Y78lyRWedVoVWHNRSCBh0oWCC+bluTQ=
|
||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
||||||
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240711023527-ba358c48772f/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
||||||
github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo=
|
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/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ replace github.com/alibaba/higress/plugins/wasm-go => ../..
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/alibaba/higress/plugins/wasm-go v0.0.0
|
github.com/alibaba/higress/plugins/wasm-go v0.0.0
|
||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240711023527-ba358c48772f
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/tidwall/gjson v1.14.3
|
github.com/tidwall/gjson v1.14.3
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240226064518-b3dc4646a35a h1
|
|||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240226064518-b3dc4646a35a/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240226064518-b3dc4646a35a/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240318034951-d5306e367c43/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240318034951-d5306e367c43/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
||||||
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240711023527-ba358c48772f/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
||||||
github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo=
|
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/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
|||||||
@@ -293,19 +293,19 @@ func onHttpRequestHeaders(ctx wrapper.HttpContext, config BasicAuthConfig, log w
|
|||||||
}
|
}
|
||||||
|
|
||||||
func deniedNoBasicAuthData() types.Action {
|
func deniedNoBasicAuthData() types.Action {
|
||||||
_ = proxywasm.SendHttpResponse(http.StatusUnauthorized, WWWAuthenticateHeader(protectionSpace),
|
_ = proxywasm.SendHttpResponseWithDetail(http.StatusUnauthorized, "basic-auth.no_auth_data", WWWAuthenticateHeader(protectionSpace),
|
||||||
[]byte("Request denied by Basic Auth check. No Basic Authentication information found."), -1)
|
[]byte("Request denied by Basic Auth check. No Basic Authentication information found."), -1)
|
||||||
return types.ActionContinue
|
return types.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
func deniedInvalidCredentials() types.Action {
|
func deniedInvalidCredentials() types.Action {
|
||||||
_ = proxywasm.SendHttpResponse(http.StatusUnauthorized, WWWAuthenticateHeader(protectionSpace),
|
_ = proxywasm.SendHttpResponseWithDetail(http.StatusUnauthorized, "basic-auth.bad_credential", WWWAuthenticateHeader(protectionSpace),
|
||||||
[]byte("Request denied by Basic Auth check. Invalid username and/or password."), -1)
|
[]byte("Request denied by Basic Auth check. Invalid username and/or password."), -1)
|
||||||
return types.ActionContinue
|
return types.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
func deniedUnauthorizedConsumer() types.Action {
|
func deniedUnauthorizedConsumer() types.Action {
|
||||||
_ = proxywasm.SendHttpResponse(http.StatusForbidden, WWWAuthenticateHeader(protectionSpace),
|
_ = proxywasm.SendHttpResponseWithDetail(http.StatusForbidden, "basic-auth.unauthorized", WWWAuthenticateHeader(protectionSpace),
|
||||||
[]byte("Request denied by Basic Auth check. Unauthorized consumer."), -1)
|
[]byte("Request denied by Basic Auth check. Unauthorized consumer."), -1)
|
||||||
return types.ActionContinue
|
return types.ActionContinue
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ replace github.com/alibaba/higress/plugins/wasm-go => ../..
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/alibaba/higress/plugins/wasm-go v1.3.2
|
github.com/alibaba/higress/plugins/wasm-go v1.3.2
|
||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240711023527-ba358c48772f
|
||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.8.4
|
||||||
github.com/tidwall/gjson v1.14.3
|
github.com/tidwall/gjson v1.14.3
|
||||||
github.com/wasilibs/go-re2 v1.4.1
|
github.com/wasilibs/go-re2 v1.4.1
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240226064518-b3dc4646a35a h1
|
|||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240226064518-b3dc4646a35a/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240226064518-b3dc4646a35a/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240318034951-d5306e367c43/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240318034951-d5306e367c43/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
||||||
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240711023527-ba358c48772f/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
|
||||||
github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg=
|
github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg=
|
||||||
github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ func onHttpRequestHeaders(ctx wrapper.HttpContext, botDetectConfig config.BotDet
|
|||||||
method := ctx.Method()
|
method := ctx.Method()
|
||||||
|
|
||||||
if ok, rule := botDetectConfig.Process(ua); !ok {
|
if ok, rule := botDetectConfig.Process(ua); !ok {
|
||||||
proxywasm.SendHttpResponse(botDetectConfig.BlockedCode, nil, []byte(botDetectConfig.BlockedMessage), -1)
|
proxywasm.SendHttpResponseWithDetail(botDetectConfig.BlockedCode, "bot-detect.blocked", nil, []byte(botDetectConfig.BlockedMessage), -1)
|
||||||
log.Debugf("scheme:%s, host:%s, method:%s, path:%s user-agent:%s has been blocked by rule:%s", scheme, host, method, path, ua, rule)
|
log.Debugf("scheme:%s, host:%s, method:%s, path:%s user-agent:%s has been blocked by rule:%s", scheme, host, method, path, ua, rule)
|
||||||
return types.ActionPause
|
return types.ActionPause
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ replace github.com/alibaba/higress/plugins/wasm-go => ../..
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/alibaba/higress/plugins/wasm-go v0.0.0
|
github.com/alibaba/higress/plugins/wasm-go v0.0.0
|
||||||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc
|
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240711023527-ba358c48772f
|
||||||
github.com/tidwall/gjson v1.14.3
|
github.com/tidwall/gjson v1.14.3
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user