mirror of
https://github.com/alibaba/higress.git
synced 2026-02-25 21:21:01 +08:00
Compare commits
113 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c9c7df78a9 | ||
|
|
1e018770e5 | ||
|
|
53ff28c58b | ||
|
|
66f2d8980d | ||
|
|
b1e4cf9492 | ||
|
|
c250e850d5 | ||
|
|
1f7e98cef5 | ||
|
|
4273bf573a | ||
|
|
13f36856cc | ||
|
|
e84f9980a1 | ||
|
|
a2fecd59a1 | ||
|
|
89c72777e1 | ||
|
|
85df257f4e | ||
|
|
659a982496 | ||
|
|
0ae376b320 | ||
|
|
c647ab3a08 | ||
|
|
688247f4f9 | ||
|
|
10f5267b3f | ||
|
|
cec99686a0 | ||
|
|
2d5d9c095b | ||
|
|
4bd4433248 | ||
|
|
4ea85e9a35 | ||
|
|
a140f780d2 | ||
|
|
2548815667 | ||
|
|
e760b4d0ab | ||
|
|
3cc1c7877f | ||
|
|
8039b82699 | ||
|
|
f9a015e45a | ||
|
|
5fbfbe0e4a | ||
|
|
a3339a9b1c | ||
|
|
aa94412af2 | ||
|
|
817925ef39 | ||
|
|
c55a5b9bd9 | ||
|
|
518d8dfa3d | ||
|
|
d2ee6065a0 | ||
|
|
4426f18a84 | ||
|
|
17794cef2a | ||
|
|
a554ee1ceb | ||
|
|
1dbb130539 | ||
|
|
9c1684c941 | ||
|
|
bd4109e1a4 | ||
|
|
967fa3f3d1 | ||
|
|
d57ffce1dc | ||
|
|
a2d97ae98f | ||
|
|
324e0bcf91 | ||
|
|
14742705b1 | ||
|
|
b204ad4c8d | ||
|
|
34054f8c76 | ||
|
|
6803aa44ab | ||
|
|
e5cd334d5d | ||
|
|
88c0386ca3 | ||
|
|
5174397e7c | ||
|
|
cb0479510f | ||
|
|
57b8cb1d69 | ||
|
|
9f5b795a4d | ||
|
|
26654aefc0 | ||
|
|
70176cde3e | ||
|
|
7b1f538d38 | ||
|
|
344035698a | ||
|
|
9136908354 | ||
|
|
de1dd3bfbc | ||
|
|
0c1db17de6 | ||
|
|
8cbe16f77c | ||
|
|
8ed4c5609a | ||
|
|
9ea1903ce6 | ||
|
|
6835486725 | ||
|
|
265df42456 | ||
|
|
3b1b621627 | ||
|
|
901ad9619d | ||
|
|
4a5127fedc | ||
|
|
4e44e7a1bb | ||
|
|
b54a2e7387 | ||
|
|
124caf8785 | ||
|
|
754ec71d6e | ||
|
|
d8e91851d9 | ||
|
|
86b223bc75 | ||
|
|
a18879bf86 | ||
|
|
970cfd44ee | ||
|
|
f685b0353a | ||
|
|
e135789c3e | ||
|
|
3e72d4b1f0 | ||
|
|
1ded5322a5 | ||
|
|
be8563765e | ||
|
|
45c4c80a66 | ||
|
|
d8c34bb863 | ||
|
|
4e392d1cf6 | ||
|
|
5b663ae412 | ||
|
|
fcf19535f9 | ||
|
|
14e43aa921 | ||
|
|
64ccbab29c | ||
|
|
945787f7dc | ||
|
|
792b9b0ee5 | ||
|
|
26ed9a6d93 | ||
|
|
ed36a4989f | ||
|
|
f23e26374f | ||
|
|
eb2934c084 | ||
|
|
2da1c62c69 | ||
|
|
fab734d39a | ||
|
|
2393af5c85 | ||
|
|
b142f51776 | ||
|
|
587267a733 | ||
|
|
a2078711f5 | ||
|
|
dc54c581f3 | ||
|
|
b47d74bce5 | ||
|
|
8d8ad6d624 | ||
|
|
8062625d75 | ||
|
|
54a8a906ae | ||
|
|
8659895a91 | ||
|
|
dc3e496aa0 | ||
|
|
8747e1ddad | ||
|
|
2b9e3a14c2 | ||
|
|
1051201e97 | ||
|
|
8b24a20651 |
70
.github/workflows/build-and-test-plugin.yaml
vendored
Normal file
70
.github/workflows/build-and-test-plugin.yaml
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
name: "Build and Test Plugins"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
paths:
|
||||
- 'plugins/**'
|
||||
- 'test/**'
|
||||
pull_request:
|
||||
branches: ["*"]
|
||||
paths:
|
||||
- 'plugins/**'
|
||||
- 'test/**'
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.19
|
||||
# There are too many lint errors in current code bases
|
||||
# uncomment when we decide what lint should be addressed or ignored.
|
||||
# - run: make lint
|
||||
|
||||
higress-wasmplugin-test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
# TODO(Xunzhuo): Enable C WASM Filters in CI
|
||||
wasmPluginType: [ GO ]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: "Setup Go"
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.19
|
||||
|
||||
- name: Setup Golang Caches
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |-
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ github.run_id }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go
|
||||
|
||||
- name: Setup Submodule Caches
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |-
|
||||
envoy
|
||||
istio
|
||||
.git/modules
|
||||
key: ${{ runner.os }}-submodules-new-${{ github.run_id }}
|
||||
restore-keys: ${{ runner.os }}-submodules-new
|
||||
|
||||
- run: git stash # restore patch
|
||||
|
||||
- name: "Run Ingress WasmPlugins Tests"
|
||||
run: GOPROXY="https://proxy.golang.org,direct" PLUGIN_TYPE=${{ matrix.wasmPluginType }} make higress-wasmplugin-test
|
||||
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [higress-wasmplugin-test]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
54
.github/workflows/build-and-test.yaml
vendored
54
.github/workflows/build-and-test.yaml
vendored
@@ -38,10 +38,9 @@ jobs:
|
||||
path: |-
|
||||
envoy
|
||||
istio
|
||||
external
|
||||
.git/modules
|
||||
key: ${{ runner.os }}-submodules-${{ github.run_id }}
|
||||
restore-keys: ${{ runner.os }}-submodules
|
||||
key: ${{ runner.os }}-submodules-new-${{ github.run_id }}
|
||||
restore-keys: ${{ runner.os }}-submodules-new
|
||||
|
||||
- run: git stash # restore patch
|
||||
|
||||
@@ -85,10 +84,9 @@ jobs:
|
||||
path: |-
|
||||
envoy
|
||||
istio
|
||||
external
|
||||
.git/modules
|
||||
key: ${{ runner.os }}-submodules-${{ github.run_id }}
|
||||
restore-keys: ${{ runner.os }}-submodules
|
||||
key: ${{ runner.os }}-submodules-new-${{ github.run_id }}
|
||||
restore-keys: ${{ runner.os }}-submodules-new
|
||||
|
||||
- run: git stash # restore patch
|
||||
|
||||
@@ -134,55 +132,17 @@ jobs:
|
||||
path: |-
|
||||
envoy
|
||||
istio
|
||||
external
|
||||
.git/modules
|
||||
key: ${{ runner.os }}-submodules-${{ github.run_id }}
|
||||
restore-keys: ${{ runner.os }}-submodules
|
||||
key: ${{ runner.os }}-submodules-new-${{ github.run_id }}
|
||||
restore-keys: ${{ runner.os }}-submodules-new
|
||||
|
||||
- run: git stash # restore patch
|
||||
|
||||
- name: "Run Higress E2E Conformance Tests"
|
||||
run: GOPROXY="https://proxy.golang.org,direct" make higress-conformance-test
|
||||
|
||||
higress-wasmplugin-test:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: "Setup Go"
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.19
|
||||
|
||||
- name: Setup Golang Caches
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |-
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ github.run_id }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go
|
||||
|
||||
- name: Setup Submodule Caches
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |-
|
||||
envoy
|
||||
istio
|
||||
external
|
||||
.git/modules
|
||||
key: ${{ runner.os }}-submodules-${{ github.run_id }}
|
||||
restore-keys: ${{ runner.os }}-submodules
|
||||
|
||||
- run: git stash # restore patch
|
||||
|
||||
- name: "Run Ingress WasmPlugins Tests"
|
||||
run: GOPROXY="https://proxy.golang.org,direct" make higress-wasmplugin-test
|
||||
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [higress-conformance-test,gateway-conformance-test,higress-wasmplugin-test]
|
||||
needs: [higress-conformance-test,gateway-conformance-test]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
2
.github/workflows/build-image-and-push.yaml
vendored
2
.github/workflows/build-image-and-push.yaml
vendored
@@ -41,7 +41,7 @@ jobs:
|
||||
envoy
|
||||
istio
|
||||
.git/modules
|
||||
key: ${{ runner.os }}-submodules-${{ github.run_id }}
|
||||
key: ${{ runner.os }}-submodules-new-${{ github.run_id }}
|
||||
restore-keys: ${{ runner.os }}-submodules-new
|
||||
|
||||
- name: Calculate Docker metadata
|
||||
|
||||
4
.github/workflows/latest-release.yaml
vendored
4
.github/workflows/latest-release.yaml
vendored
@@ -18,6 +18,8 @@ jobs:
|
||||
tar -zcvf hgctl_latest_linux_arm64.tar.gz out/linux_arm64/
|
||||
tar -zcvf hgctl_latest_darwin_amd64.tar.gz out/darwin_amd64/
|
||||
tar -zcvf hgctl_latest_darwin_arm64.tar.gz out/darwin_arm64/
|
||||
zip -q -r hgctl_latest_windows_amd64.zip out/windows_amd64/
|
||||
zip -q -r hgctl_latest_windows_arm64.zip out/windows_arm64/
|
||||
|
||||
# Ignore the error when we delete the latest release, it might not exist.
|
||||
|
||||
@@ -54,6 +56,8 @@ jobs:
|
||||
hgctl_latest_linux_arm64.tar.gz
|
||||
hgctl_latest_darwin_amd64.tar.gz
|
||||
hgctl_latest_darwin_arm64.tar.gz
|
||||
hgctl_latest_windows_amd64.zip
|
||||
hgctl_latest_windows_arm64.zip
|
||||
body: |
|
||||
This is the "latest" release of **Higress**, which contains the most recent commits from the main branch.
|
||||
|
||||
|
||||
@@ -26,7 +26,8 @@ header:
|
||||
- 'VERSION'
|
||||
- 'tools/'
|
||||
- 'test/README.md'
|
||||
- 'pkg/cmd/hgctl/testdata/config'
|
||||
- 'cmd/hgctl/config/testdata/config'
|
||||
- 'pkg/cmd/hgctl/manifests'
|
||||
|
||||
comment: on-failure
|
||||
dependency:
|
||||
|
||||
14
CODEOWNERS
14
CODEOWNERS
@@ -1,10 +1,10 @@
|
||||
/api @johnlanni
|
||||
/envoy @gengleilei @johnlanni @Lynskylate
|
||||
/api @johnlanni @CH3CHO
|
||||
/envoy @gengleilei @johnlanni
|
||||
/istio @SpecialYang @johnlanni
|
||||
/pkg @SpecialYang @johnlanni @Charlie17Li @Xunzhuo
|
||||
/plugins @johnlanni
|
||||
/registry @NameHaibinZhang @johnlanni
|
||||
/test @Xunzhuo
|
||||
/tools @johnlanni @Xunzhuo
|
||||
/pkg @SpecialYang @johnlanni @CH3CHO @Xunzhuo
|
||||
/plugins @johnlanni @WeixinX
|
||||
/registry @NameHaibinZhang @2456868764 @johnlanni
|
||||
/test @Xunzhuo @2456868764 @CH3CHO
|
||||
/tools @johnlanni @Xunzhuo @2456868764
|
||||
|
||||
|
||||
|
||||
@@ -79,20 +79,21 @@ $(ARM64_OUT_LINUX)/higress:
|
||||
GOPROXY=$(GOPROXY) GOOS=linux GOARCH=arm64 LDFLAGS=$(RELEASE_LDFLAGS) tools/hack/gobuild.sh ./out/linux_arm64/ $(HIGRESS_BINARIES)
|
||||
|
||||
.PHONY: build-hgctl
|
||||
build-hgctl: $(OUT)
|
||||
build-hgctl: prebuild $(OUT)
|
||||
GOPROXY=$(GOPROXY) GOOS=$(GOOS_LOCAL) GOARCH=$(GOARCH_LOCAL) LDFLAGS=$(RELEASE_LDFLAGS) tools/hack/gobuild.sh $(OUT)/ $(HGCTL_BINARIES)
|
||||
|
||||
.PHONY: build-linux-hgctl
|
||||
build-linux-hgctl: $(OUT)
|
||||
build-linux-hgctl: prebuild $(OUT)
|
||||
GOPROXY=$(GOPROXY) GOOS=linux GOARCH=$(GOARCH_LOCAL) LDFLAGS=$(RELEASE_LDFLAGS) tools/hack/gobuild.sh $(OUT_LINUX)/ $(HGCTL_BINARIES)
|
||||
|
||||
.PHONY: build-hgctl-multiarch
|
||||
build-hgctl-multiarch: $(OUT)
|
||||
build-hgctl-multiarch: prebuild $(OUT)
|
||||
GOPROXY=$(GOPROXY) GOOS=linux GOARCH=amd64 LDFLAGS=$(RELEASE_LDFLAGS) tools/hack/gobuild.sh ./out/linux_amd64/ $(HGCTL_BINARIES)
|
||||
GOPROXY=$(GOPROXY) GOOS=linux GOARCH=arm64 LDFLAGS=$(RELEASE_LDFLAGS) tools/hack/gobuild.sh ./out/linux_arm64/ $(HGCTL_BINARIES)
|
||||
GOPROXY=$(GOPROXY) GOOS=darwin GOARCH=amd64 LDFLAGS=$(RELEASE_LDFLAGS) tools/hack/gobuild.sh ./out/darwin_amd64/ $(HGCTL_BINARIES)
|
||||
GOPROXY=$(GOPROXY) GOOS=darwin GOARCH=arm64 LDFLAGS=$(RELEASE_LDFLAGS) tools/hack/gobuild.sh ./out/darwin_arm64/ $(HGCTL_BINARIES)
|
||||
|
||||
GOPROXY=$(GOPROXY) GOOS=windows GOARCH=amd64 LDFLAGS=$(RELEASE_LDFLAGS) tools/hack/gobuild.sh ./out/windows_amd64/ $(HGCTL_BINARIES)
|
||||
GOPROXY=$(GOPROXY) GOOS=windows GOARCH=arm64 LDFLAGS=$(RELEASE_LDFLAGS) tools/hack/gobuild.sh ./out/windows_arm64/ $(HGCTL_BINARIES)
|
||||
# Create targets for OUT_LINUX/binary
|
||||
# There are two use cases here:
|
||||
# * Building all docker images (generally in CI). In this case we want to build everything at once, so they share work
|
||||
@@ -137,24 +138,32 @@ export ENVOY_TAR_PATH:=/home/package/envoy.tar.gz
|
||||
|
||||
external/package/envoy-amd64.tar.gz:
|
||||
# cd external/proxy; BUILD_WITH_CONTAINER=1 make test_release
|
||||
cd external/package; wget "https://github.com/alibaba/higress/releases/download/v1.0.0/envoy-amd64.tar.gz"
|
||||
cd external/package; wget "https://github.com/alibaba/higress/releases/download/v1.3.2/envoy-amd64.tar.gz"
|
||||
|
||||
external/package/envoy-arm64.tar.gz:
|
||||
# cd external/proxy; BUILD_WITH_CONTAINER=1 make test_release
|
||||
cd external/package; wget "https://github.com/alibaba/higress/releases/download/v1.0.0/envoy-arm64.tar.gz"
|
||||
|
||||
cd external/package; wget "https://github.com/alibaba/higress/releases/download/v1.3.2/envoy-arm64.tar.gz"
|
||||
|
||||
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_arm64; GOOS_LOCAL=linux TARGET_OS=linux TARGET_ARCH=arm64 BUILD_WITH_CONTAINER=1 make build-linux
|
||||
|
||||
build-pilot-local:
|
||||
cd external/istio; rm -rf out/linux_${GOARCH_LOCAL}; GOOS_LOCAL=linux TARGET_OS=linux TARGET_ARCH=${GOARCH_LOCAL} BUILD_WITH_CONTAINER=1 make build-linux
|
||||
|
||||
build-gateway: prebuild external/package/envoy-amd64.tar.gz external/package/envoy-arm64.tar.gz build-pilot
|
||||
cd external/istio; BUILD_WITH_CONTAINER=1 BUILDX_PLATFORM=true DOCKER_BUILD_VARIANTS=default DOCKER_TARGETS="docker.proxyv2" make docker
|
||||
|
||||
build-istio: prebuild build-pilot
|
||||
cd external/istio; BUILD_WITH_CONTAINER=1 BUILDX_PLATFORM=true DOCKER_BUILD_VARIANTS=default DOCKER_TARGETS="docker.pilot" make docker
|
||||
build-gateway-local: prebuild external/package/envoy-amd64.tar.gz external/package/envoy-arm64.tar.gz build-pilot
|
||||
cd external/istio; rm -rf out/linux_${GOARCH_LOCAL}; GOOS_LOCAL=linux TARGET_OS=linux BUILD_WITH_CONTAINER=1 BUILDX_PLATFORM=false DOCKER_BUILD_VARIANTS=default DOCKER_TARGETS="docker.proxyv2" make docker
|
||||
|
||||
build-wasmplugins:
|
||||
build-istio: prebuild build-pilot
|
||||
cd external/istio; BUILD_WITH_CONTAINER=1 BUILDX_PLATFORM=true DOCKER_BUILD_VARIANTS=default DOCKER_TARGETS="docker.pilot" make docker
|
||||
|
||||
build-istio-local: prebuild build-pilot-local
|
||||
cd external/istio; rm -rf out/linux_${GOARCH_LOCAL}; GOOS_LOCAL=linux TARGET_OS=linux BUILD_WITH_CONTAINER=1 BUILDX_PLATFORM=false DOCKER_BUILD_VARIANTS=default DOCKER_TARGETS="docker.pilot" make docker
|
||||
|
||||
build-wasmplugins:
|
||||
./tools/hack/build-wasm-plugins.sh
|
||||
|
||||
pre-install:
|
||||
@@ -168,14 +177,13 @@ install: pre-install
|
||||
cd helm/higress; helm dependency build
|
||||
helm install higress helm/higress -n higress-system --create-namespace --set 'global.local=true'
|
||||
|
||||
ENVOY_LATEST_IMAGE_TAG ?= 1.1.1
|
||||
ISTIO_LATEST_IMAGE_TAG ?= 1.1.1
|
||||
ENVOY_LATEST_IMAGE_TAG ?= sha-53ff28c
|
||||
ISTIO_LATEST_IMAGE_TAG ?= sha-53ff28c
|
||||
|
||||
install-dev: pre-install
|
||||
helm install higress helm/core -n higress-system --create-namespace --set 'controller.tag=$(TAG)' --set 'gateway.replicas=1' --set 'gateway.tag=$(ENVOY_LATEST_IMAGE_TAG)' --set 'global.local=true'
|
||||
|
||||
helm install higress helm/core -n higress-system --create-namespace --set 'controller.tag=$(TAG)' --set 'gateway.replicas=1' --set 'pilot.tag=$(ISTIO_LATEST_IMAGE_TAG)' --set 'gateway.tag=$(ENVOY_LATEST_IMAGE_TAG)' --set 'global.local=true'
|
||||
install-dev-wasmplugin: build-wasmplugins pre-install
|
||||
helm install higress helm/core -n higress-system --create-namespace --set 'controller.tag=$(TAG)' --set 'gateway.replicas=1' --set 'gateway.tag=$(ENVOY_LATEST_IMAGE_TAG)' --set 'global.local=true' --set 'global.volumeWasmPlugins=true'
|
||||
helm install higress helm/core -n higress-system --create-namespace --set 'controller.tag=$(TAG)' --set 'gateway.replicas=1' --set 'pilot.tag=$(ISTIO_LATEST_IMAGE_TAG)' --set 'gateway.tag=$(ENVOY_LATEST_IMAGE_TAG)' --set 'global.local=true' --set 'global.volumeWasmPlugins=true'
|
||||
|
||||
uninstall:
|
||||
helm uninstall higress -n higress-system
|
||||
@@ -250,16 +258,16 @@ delete-cluster: $(tools/kind) ## Delete kind cluster.
|
||||
.PHONY: kube-load-image
|
||||
kube-load-image: $(tools/kind) ## Install the Higress image to a kind cluster using the provided $IMAGE and $TAG.
|
||||
tools/hack/kind-load-image.sh higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/higress $(TAG)
|
||||
tools/hack/docker-pull-image.sh registry.cn-hangzhou.aliyuncs.com/hinsteny/dubbo-provider-demo 0.0.1
|
||||
tools/hack/docker-pull-image.sh registry.cn-hangzhou.aliyuncs.com/hinsteny/nacos-standlone-rc3 1.0.0-RC3
|
||||
tools/hack/docker-pull-image.sh higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/dubbo-provider-demo 0.0.3-x86
|
||||
tools/hack/docker-pull-image.sh docker.io/alihigress/nacos-standlone-rc3 1.0.0-RC3
|
||||
tools/hack/docker-pull-image.sh docker.io/hashicorp/consul 1.16.0
|
||||
tools/hack/docker-pull-image.sh docker.io/charlie1380/eureka-registry-provider v0.3.0
|
||||
tools/hack/docker-pull-image.sh docker.io/bitinit/eureka latest
|
||||
tools/hack/docker-pull-image.sh registry.cn-hangzhou.aliyuncs.com/2456868764/httpbin 1.0.2
|
||||
tools/hack/kind-load-image.sh registry.cn-hangzhou.aliyuncs.com/hinsteny/dubbo-provider-demo 0.0.1
|
||||
tools/hack/kind-load-image.sh registry.cn-hangzhou.aliyuncs.com/hinsteny/nacos-standlone-rc3 1.0.0-RC3
|
||||
tools/hack/docker-pull-image.sh docker.io/alihigress/httpbin 1.0.2
|
||||
tools/hack/kind-load-image.sh higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/dubbo-provider-demo 0.0.3-x86
|
||||
tools/hack/kind-load-image.sh docker.io/alihigress/nacos-standlone-rc3 1.0.0-RC3
|
||||
tools/hack/kind-load-image.sh docker.io/hashicorp/consul 1.16.0
|
||||
tools/hack/kind-load-image.sh registry.cn-hangzhou.aliyuncs.com/2456868764/httpbin 1.0.2
|
||||
tools/hack/kind-load-image.sh docker.io/alihigress/httpbin 1.0.2
|
||||
tools/hack/kind-load-image.sh docker.io/charlie1380/eureka-registry-provider v0.3.0
|
||||
tools/hack/kind-load-image.sh docker.io/bitinit/eureka latest
|
||||
# run-higress-e2e-test starts to run ingress e2e tests.
|
||||
|
||||
@@ -121,13 +121,7 @@ Higress 是基于阿里内部两年多的 Envoy Gateway 实践沉淀,以开源
|
||||
|
||||
### 联系我们
|
||||
|
||||
- Mailing list: higress@googlegroups.com
|
||||
|
||||
社区交流群:
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
开发者群:
|
||||
|
||||

|
||||
|
||||
@@ -154,6 +154,11 @@ spec:
|
||||
type: array
|
||||
httpPath:
|
||||
type: string
|
||||
paramFromEntireBody:
|
||||
properties:
|
||||
paramType:
|
||||
type: string
|
||||
type: object
|
||||
params:
|
||||
items:
|
||||
properties:
|
||||
|
||||
@@ -200,14 +200,15 @@ func (m *DubboService) GetMethods() []*Method {
|
||||
}
|
||||
|
||||
type Method struct {
|
||||
ServiceMethod string `protobuf:"bytes,1,opt,name=service_method,json=serviceMethod,proto3" json:"service_method,omitempty"`
|
||||
HeadersAttach string `protobuf:"bytes,2,opt,name=headers_attach,json=headersAttach,proto3" json:"headers_attach,omitempty"`
|
||||
HttpPath string `protobuf:"bytes,3,opt,name=http_path,json=httpPath,proto3" json:"http_path,omitempty"`
|
||||
HttpMethods []string `protobuf:"bytes,4,rep,name=http_methods,json=httpMethods,proto3" json:"http_methods,omitempty"`
|
||||
Params []*Param `protobuf:"bytes,5,rep,name=params,proto3" json:"params,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
ServiceMethod string `protobuf:"bytes,1,opt,name=service_method,json=serviceMethod,proto3" json:"service_method,omitempty"`
|
||||
HeadersAttach string `protobuf:"bytes,2,opt,name=headers_attach,json=headersAttach,proto3" json:"headers_attach,omitempty"`
|
||||
HttpPath string `protobuf:"bytes,3,opt,name=http_path,json=httpPath,proto3" json:"http_path,omitempty"`
|
||||
HttpMethods []string `protobuf:"bytes,4,rep,name=http_methods,json=httpMethods,proto3" json:"http_methods,omitempty"`
|
||||
Params []*Param `protobuf:"bytes,5,rep,name=params,proto3" json:"params,omitempty"`
|
||||
ParamFromEntireBody *ParamFromEntireBody `protobuf:"bytes,6,opt,name=paramFromEntireBody,proto3" json:"paramFromEntireBody,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Method) Reset() { *m = Method{} }
|
||||
@@ -278,6 +279,13 @@ func (m *Method) GetParams() []*Param {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Method) GetParamFromEntireBody() *ParamFromEntireBody {
|
||||
if m != nil {
|
||||
return m.ParamFromEntireBody
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Param struct {
|
||||
ParamSource string `protobuf:"bytes,1,opt,name=param_source,json=paramSource,proto3" json:"param_source,omitempty"`
|
||||
ParamKey string `protobuf:"bytes,2,opt,name=param_key,json=paramKey,proto3" json:"param_key,omitempty"`
|
||||
@@ -341,6 +349,53 @@ func (m *Param) GetParamType() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
type ParamFromEntireBody struct {
|
||||
ParamType string `protobuf:"bytes,1,opt,name=param_type,json=paramType,proto3" json:"param_type,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *ParamFromEntireBody) Reset() { *m = ParamFromEntireBody{} }
|
||||
func (m *ParamFromEntireBody) String() string { return proto.CompactTextString(m) }
|
||||
func (*ParamFromEntireBody) ProtoMessage() {}
|
||||
func (*ParamFromEntireBody) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_dc706c3b890c1c84, []int{4}
|
||||
}
|
||||
func (m *ParamFromEntireBody) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
}
|
||||
func (m *ParamFromEntireBody) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
if deterministic {
|
||||
return xxx_messageInfo_ParamFromEntireBody.Marshal(b, m, deterministic)
|
||||
} else {
|
||||
b = b[:cap(b)]
|
||||
n, err := m.MarshalToSizedBuffer(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[:n], nil
|
||||
}
|
||||
}
|
||||
func (m *ParamFromEntireBody) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ParamFromEntireBody.Merge(m, src)
|
||||
}
|
||||
func (m *ParamFromEntireBody) XXX_Size() int {
|
||||
return m.Size()
|
||||
}
|
||||
func (m *ParamFromEntireBody) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_ParamFromEntireBody.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_ParamFromEntireBody proto.InternalMessageInfo
|
||||
|
||||
func (m *ParamFromEntireBody) GetParamType() string {
|
||||
if m != nil {
|
||||
return m.ParamType
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type GrpcService struct {
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
@@ -351,7 +406,7 @@ func (m *GrpcService) Reset() { *m = GrpcService{} }
|
||||
func (m *GrpcService) String() string { return proto.CompactTextString(m) }
|
||||
func (*GrpcService) ProtoMessage() {}
|
||||
func (*GrpcService) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_dc706c3b890c1c84, []int{4}
|
||||
return fileDescriptor_dc706c3b890c1c84, []int{5}
|
||||
}
|
||||
func (m *GrpcService) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -385,42 +440,46 @@ func init() {
|
||||
proto.RegisterType((*DubboService)(nil), "higress.networking.v1.DubboService")
|
||||
proto.RegisterType((*Method)(nil), "higress.networking.v1.Method")
|
||||
proto.RegisterType((*Param)(nil), "higress.networking.v1.Param")
|
||||
proto.RegisterType((*ParamFromEntireBody)(nil), "higress.networking.v1.ParamFromEntireBody")
|
||||
proto.RegisterType((*GrpcService)(nil), "higress.networking.v1.GrpcService")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("networking/v1/http_2_rpc.proto", fileDescriptor_dc706c3b890c1c84) }
|
||||
|
||||
var fileDescriptor_dc706c3b890c1c84 = []byte{
|
||||
// 463 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x53, 0xcf, 0x8a, 0xd3, 0x40,
|
||||
0x18, 0x77, 0xba, 0x6d, 0xb7, 0xfb, 0x65, 0xeb, 0x61, 0x40, 0x08, 0x8b, 0xc6, 0x35, 0x7b, 0x70,
|
||||
0x41, 0x49, 0xd8, 0xea, 0x41, 0x14, 0x0f, 0x5b, 0x04, 0x17, 0x44, 0x58, 0xb2, 0x9e, 0xbc, 0x84,
|
||||
0x49, 0x32, 0x66, 0x86, 0x6d, 0x33, 0xc3, 0xcc, 0x34, 0xda, 0xb7, 0xf0, 0x35, 0x7c, 0x13, 0x8f,
|
||||
0x3e, 0x82, 0x14, 0x1f, 0x44, 0x32, 0x93, 0x6e, 0x13, 0xb1, 0xb7, 0xf0, 0xfb, 0x33, 0xdf, 0xef,
|
||||
0xc7, 0xf7, 0x05, 0x82, 0x8a, 0x9a, 0xaf, 0x42, 0xdd, 0xf2, 0xaa, 0x8c, 0xeb, 0x8b, 0x98, 0x19,
|
||||
0x23, 0xd3, 0x59, 0xaa, 0x64, 0x1e, 0x49, 0x25, 0x8c, 0xc0, 0x0f, 0x18, 0x2f, 0x15, 0xd5, 0x3a,
|
||||
0xda, 0xe9, 0xa2, 0xfa, 0xe2, 0xe4, 0x71, 0x29, 0x44, 0xb9, 0xa0, 0x31, 0x91, 0x3c, 0xfe, 0xc2,
|
||||
0xe9, 0xa2, 0x48, 0x33, 0xca, 0x48, 0xcd, 0x85, 0x72, 0xbe, 0xf0, 0x3b, 0x82, 0xc9, 0x95, 0x31,
|
||||
0x72, 0x96, 0xc8, 0x1c, 0xbf, 0x81, 0x51, 0xb1, 0xca, 0x32, 0xe1, 0xa3, 0x53, 0x74, 0xee, 0xcd,
|
||||
0xce, 0xa2, 0xff, 0x3e, 0x1a, 0xbd, 0x6b, 0x34, 0x37, 0x54, 0xd5, 0x3c, 0xa7, 0x57, 0xf7, 0x12,
|
||||
0xe7, 0xc1, 0xaf, 0x60, 0x58, 0x2a, 0x99, 0xfb, 0x03, 0xeb, 0x0d, 0xf7, 0x78, 0xdf, 0x2b, 0x99,
|
||||
0xef, 0xac, 0xd6, 0x31, 0x9f, 0x82, 0x57, 0x50, 0x6d, 0x78, 0x45, 0x0c, 0x17, 0x55, 0xf8, 0x03,
|
||||
0xc1, 0x71, 0x77, 0x04, 0x0e, 0xe0, 0x50, 0xbb, 0x4f, 0x1b, 0xec, 0x68, 0x3e, 0xdc, 0x5c, 0xa2,
|
||||
0x41, 0xb2, 0x05, 0x1b, 0xbe, 0xa6, 0x4a, 0x73, 0x51, 0xd9, 0xe1, 0x77, 0x7c, 0x0b, 0xe2, 0x13,
|
||||
0x18, 0x95, 0x4a, 0xac, 0xa4, 0x7f, 0x70, 0xc7, 0xa2, 0xc4, 0x41, 0xf8, 0x2d, 0x1c, 0x2e, 0xa9,
|
||||
0x61, 0xa2, 0xd0, 0xfe, 0xf0, 0xf4, 0xe0, 0xdc, 0x9b, 0x3d, 0xda, 0x13, 0xfc, 0xa3, 0x55, 0x6d,
|
||||
0x9f, 0x6e, 0x3d, 0xe1, 0x1f, 0x04, 0x63, 0xc7, 0xe0, 0x67, 0x70, 0xbf, 0x0d, 0x94, 0x3a, 0xb6,
|
||||
0x17, 0x76, 0xda, 0x72, 0x3b, 0x31, 0xa3, 0xa4, 0xa0, 0x4a, 0xa7, 0xc4, 0x18, 0x92, 0xb3, 0x4e,
|
||||
0x72, 0x94, 0x4c, 0x5b, 0xee, 0xd2, 0x52, 0xf8, 0x09, 0x1c, 0xd9, 0x7d, 0x4b, 0x62, 0x58, 0xa7,
|
||||
0xc3, 0x20, 0x99, 0x34, 0xf0, 0x35, 0x31, 0x0c, 0x3f, 0x85, 0x63, 0x2b, 0xe9, 0x76, 0xd9, 0xaa,
|
||||
0xbc, 0x86, 0x71, 0x73, 0x35, 0x7e, 0x09, 0x63, 0x49, 0x14, 0x59, 0x6a, 0x7f, 0x64, 0xeb, 0x3e,
|
||||
0xdc, 0x53, 0xf7, 0xba, 0x11, 0x25, 0xad, 0x36, 0xfc, 0x06, 0x23, 0x0b, 0x34, 0x73, 0x2c, 0x94,
|
||||
0x6a, 0xb1, 0x52, 0xff, 0xec, 0xc3, 0xb3, 0xcc, 0x8d, 0x25, 0x9a, 0xcc, 0x4e, 0x78, 0x4b, 0xd7,
|
||||
0xbd, 0xad, 0x4c, 0x2c, 0xfc, 0x81, 0xae, 0xf1, 0x19, 0x80, 0x93, 0x98, 0xb5, 0xa4, 0xbd, 0x5e,
|
||||
0xce, 0xfa, 0x69, 0x2d, 0x69, 0x38, 0x05, 0xaf, 0x73, 0x32, 0xf3, 0xd7, 0x3f, 0x37, 0x01, 0xfa,
|
||||
0xb5, 0x09, 0xd0, 0xef, 0x4d, 0x80, 0x3e, 0x3f, 0x2f, 0xb9, 0x61, 0xab, 0x2c, 0xca, 0xc5, 0x32,
|
||||
0x26, 0x0b, 0x9e, 0x91, 0x8c, 0xc4, 0x6d, 0x1d, 0x7b, 0xf1, 0xbd, 0x7f, 0x26, 0x1b, 0xdb, 0x8b,
|
||||
0x7f, 0xf1, 0x37, 0x00, 0x00, 0xff, 0xff, 0x75, 0x5c, 0x9e, 0x28, 0x4b, 0x03, 0x00, 0x00,
|
||||
// 506 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x93, 0xdf, 0x6e, 0xd3, 0x30,
|
||||
0x14, 0xc6, 0x71, 0xd7, 0x76, 0xdd, 0xc9, 0xca, 0x85, 0x27, 0xa4, 0x68, 0x82, 0x32, 0xb2, 0x0b,
|
||||
0x26, 0x40, 0x89, 0x56, 0xb8, 0x40, 0x43, 0x5c, 0xac, 0xe2, 0xcf, 0x24, 0x84, 0x34, 0x65, 0x5c,
|
||||
0x71, 0x13, 0x39, 0x89, 0x49, 0xac, 0xb5, 0xb1, 0x65, 0xbb, 0x85, 0xbc, 0x05, 0xaf, 0xc1, 0x9b,
|
||||
0xec, 0x92, 0x47, 0x40, 0x7d, 0x12, 0x14, 0x3b, 0x5d, 0x93, 0xa9, 0xdd, 0x5d, 0x74, 0xbe, 0xef,
|
||||
0x77, 0x7c, 0x3e, 0x9f, 0x18, 0x46, 0x05, 0xd5, 0x3f, 0xb9, 0xbc, 0x66, 0x45, 0x16, 0x2c, 0x4e,
|
||||
0x83, 0x5c, 0x6b, 0x11, 0x8d, 0x23, 0x29, 0x12, 0x5f, 0x48, 0xae, 0x39, 0x7e, 0x94, 0xb3, 0x4c,
|
||||
0x52, 0xa5, 0xfc, 0xb5, 0xcf, 0x5f, 0x9c, 0x1e, 0x3e, 0xcd, 0x38, 0xcf, 0xa6, 0x34, 0x20, 0x82,
|
||||
0x05, 0x3f, 0x18, 0x9d, 0xa6, 0x51, 0x4c, 0x73, 0xb2, 0x60, 0x5c, 0x5a, 0xce, 0xfb, 0x8d, 0x60,
|
||||
0x70, 0xa1, 0xb5, 0x18, 0x87, 0x22, 0xc1, 0xef, 0xa0, 0x97, 0xce, 0xe3, 0x98, 0xbb, 0xe8, 0x08,
|
||||
0x9d, 0x38, 0xe3, 0x63, 0x7f, 0x63, 0x53, 0xff, 0x43, 0xe5, 0xb9, 0xa2, 0x72, 0xc1, 0x12, 0x7a,
|
||||
0xf1, 0x20, 0xb4, 0x0c, 0x7e, 0x0b, 0xdd, 0x4c, 0x8a, 0xc4, 0xed, 0x18, 0xd6, 0xdb, 0xc2, 0x7e,
|
||||
0x96, 0x22, 0x59, 0xa3, 0x86, 0x98, 0x0c, 0xc1, 0x49, 0xa9, 0xd2, 0xac, 0x20, 0x9a, 0xf1, 0xc2,
|
||||
0xfb, 0x83, 0x60, 0xbf, 0x79, 0x04, 0x1e, 0xc1, 0xae, 0xb2, 0x9f, 0x66, 0xb0, 0xbd, 0x49, 0x77,
|
||||
0x79, 0x8e, 0x3a, 0xe1, 0xaa, 0x58, 0xe9, 0x0b, 0x2a, 0x15, 0xe3, 0x85, 0x39, 0xfc, 0x56, 0xaf,
|
||||
0x8b, 0xf8, 0x10, 0x7a, 0x99, 0xe4, 0x73, 0xe1, 0xee, 0xdc, 0xaa, 0x28, 0xb4, 0x25, 0xfc, 0x1e,
|
||||
0x76, 0x67, 0x54, 0xe7, 0x3c, 0x55, 0x6e, 0xf7, 0x68, 0xe7, 0xc4, 0x19, 0x3f, 0xd9, 0x32, 0xf8,
|
||||
0x57, 0xe3, 0x5a, 0xb5, 0xae, 0x19, 0xef, 0xa6, 0x03, 0x7d, 0xab, 0xe0, 0x97, 0xf0, 0xb0, 0x1e,
|
||||
0x28, 0xb2, 0x6a, 0x6b, 0xd8, 0x61, 0xad, 0xad, 0xcd, 0x39, 0x25, 0x29, 0x95, 0x2a, 0x22, 0x5a,
|
||||
0x93, 0x24, 0x6f, 0x4c, 0x8e, 0xc2, 0x61, 0xad, 0x9d, 0x1b, 0x09, 0x3f, 0x83, 0x3d, 0xb3, 0x6f,
|
||||
0x41, 0x74, 0xde, 0xc8, 0xd0, 0x09, 0x07, 0x55, 0xf9, 0x92, 0xe8, 0x1c, 0x3f, 0x87, 0x7d, 0x63,
|
||||
0x69, 0x66, 0x59, 0xb9, 0x9c, 0x4a, 0xb1, 0xe7, 0x2a, 0xfc, 0x06, 0xfa, 0x82, 0x48, 0x32, 0x53,
|
||||
0x6e, 0xcf, 0xc4, 0x7d, 0xbc, 0x25, 0xee, 0x65, 0x65, 0x0a, 0x6b, 0x2f, 0x8e, 0xe1, 0xc0, 0x7c,
|
||||
0x7d, 0x92, 0x7c, 0xf6, 0xb1, 0xd0, 0x4c, 0xd2, 0x09, 0x4f, 0x4b, 0xb7, 0x6f, 0x56, 0xfd, 0xe2,
|
||||
0xbe, 0x16, 0x6d, 0xa2, 0xce, 0xb7, 0xa9, 0x99, 0xf7, 0x0b, 0x7a, 0x86, 0xa8, 0xb2, 0x18, 0x3d,
|
||||
0x52, 0x7c, 0x2e, 0xef, 0xec, 0xdc, 0x31, 0xca, 0x95, 0x11, 0xaa, 0x7b, 0xb1, 0xc6, 0x6b, 0x5a,
|
||||
0xb6, 0x36, 0x3f, 0x30, 0xe5, 0x2f, 0xb4, 0xc4, 0xc7, 0x00, 0xd6, 0xa2, 0x4b, 0x41, 0x5b, 0x77,
|
||||
0x67, 0xd1, 0x6f, 0xa5, 0xa0, 0xde, 0x19, 0x1c, 0x6c, 0x98, 0xf5, 0x0e, 0x8b, 0x36, 0xb3, 0x43,
|
||||
0x70, 0x1a, 0xbf, 0xf4, 0xe4, 0xec, 0x66, 0x39, 0x42, 0x7f, 0x97, 0x23, 0xf4, 0x6f, 0x39, 0x42,
|
||||
0xdf, 0x5f, 0x65, 0x4c, 0xe7, 0xf3, 0xd8, 0x4f, 0xf8, 0x2c, 0x20, 0x53, 0x16, 0x93, 0x98, 0x04,
|
||||
0xf5, 0x5d, 0x99, 0x17, 0xd9, 0x7a, 0xd3, 0x71, 0xdf, 0xbc, 0xc8, 0xd7, 0xff, 0x03, 0x00, 0x00,
|
||||
0xff, 0xff, 0x30, 0xef, 0x3d, 0xa9, 0xeb, 0x03, 0x00, 0x00,
|
||||
}
|
||||
|
||||
func (m *Http2Rpc) Marshal() (dAtA []byte, err error) {
|
||||
@@ -587,6 +646,18 @@ func (m *Method) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i -= len(m.XXX_unrecognized)
|
||||
copy(dAtA[i:], m.XXX_unrecognized)
|
||||
}
|
||||
if m.ParamFromEntireBody != nil {
|
||||
{
|
||||
size, err := m.ParamFromEntireBody.MarshalToSizedBuffer(dAtA[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= size
|
||||
i = encodeVarintHttp_2Rpc(dAtA, i, uint64(size))
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0x32
|
||||
}
|
||||
if len(m.Params) > 0 {
|
||||
for iNdEx := len(m.Params) - 1; iNdEx >= 0; iNdEx-- {
|
||||
{
|
||||
@@ -682,6 +753,40 @@ func (m *Param) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func (m *ParamFromEntireBody) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dAtA[:n], nil
|
||||
}
|
||||
|
||||
func (m *ParamFromEntireBody) MarshalTo(dAtA []byte) (int, error) {
|
||||
size := m.Size()
|
||||
return m.MarshalToSizedBuffer(dAtA[:size])
|
||||
}
|
||||
|
||||
func (m *ParamFromEntireBody) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i := len(dAtA)
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if m.XXX_unrecognized != nil {
|
||||
i -= len(m.XXX_unrecognized)
|
||||
copy(dAtA[i:], m.XXX_unrecognized)
|
||||
}
|
||||
if len(m.ParamType) > 0 {
|
||||
i -= len(m.ParamType)
|
||||
copy(dAtA[i:], m.ParamType)
|
||||
i = encodeVarintHttp_2Rpc(dAtA, i, uint64(len(m.ParamType)))
|
||||
i--
|
||||
dAtA[i] = 0xa
|
||||
}
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func (m *GrpcService) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
@@ -819,6 +924,10 @@ func (m *Method) Size() (n int) {
|
||||
n += 1 + l + sovHttp_2Rpc(uint64(l))
|
||||
}
|
||||
}
|
||||
if m.ParamFromEntireBody != nil {
|
||||
l = m.ParamFromEntireBody.Size()
|
||||
n += 1 + l + sovHttp_2Rpc(uint64(l))
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
n += len(m.XXX_unrecognized)
|
||||
}
|
||||
@@ -849,6 +958,22 @@ func (m *Param) Size() (n int) {
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *ParamFromEntireBody) Size() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
var l int
|
||||
_ = l
|
||||
l = len(m.ParamType)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovHttp_2Rpc(uint64(l))
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
n += len(m.XXX_unrecognized)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *GrpcService) Size() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
@@ -1360,6 +1485,42 @@ func (m *Method) Unmarshal(dAtA []byte) error {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
case 6:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field ParamFromEntireBody", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowHttp_2Rpc
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthHttp_2Rpc
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthHttp_2Rpc
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if m.ParamFromEntireBody == nil {
|
||||
m.ParamFromEntireBody = &ParamFromEntireBody{}
|
||||
}
|
||||
if err := m.ParamFromEntireBody.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipHttp_2Rpc(dAtA[iNdEx:])
|
||||
@@ -1529,6 +1690,89 @@ func (m *Param) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *ParamFromEntireBody) Unmarshal(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowHttp_2Rpc
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: ParamFromEntireBody: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: ParamFromEntireBody: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field ParamType", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowHttp_2Rpc
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthHttp_2Rpc
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthHttp_2Rpc
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.ParamType = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipHttp_2Rpc(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
return ErrInvalidLengthHttp_2Rpc
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *GrpcService) Unmarshal(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
|
||||
@@ -62,6 +62,7 @@ message Method {
|
||||
string http_path = 3 [(google.api.field_behavior) = REQUIRED];
|
||||
repeated string http_methods = 4 [(google.api.field_behavior) = REQUIRED];
|
||||
repeated Param params = 5;
|
||||
ParamFromEntireBody paramFromEntireBody = 6 [(google.api.field_behavior) = OPTIONAL];
|
||||
}
|
||||
|
||||
message Param {
|
||||
@@ -70,5 +71,9 @@ message Param {
|
||||
string param_type = 3 [(google.api.field_behavior) = REQUIRED];
|
||||
}
|
||||
|
||||
message ParamFromEntireBody {
|
||||
string param_type = 1 [(google.api.field_behavior) = REQUIRED];
|
||||
}
|
||||
|
||||
message GrpcService {
|
||||
}
|
||||
|
||||
@@ -99,6 +99,27 @@ func (in *Param) DeepCopyInterface() interface{} {
|
||||
return in.DeepCopy()
|
||||
}
|
||||
|
||||
// DeepCopyInto supports using ParamFromEntireBody within kubernetes types, where deepcopy-gen is used.
|
||||
func (in *ParamFromEntireBody) DeepCopyInto(out *ParamFromEntireBody) {
|
||||
p := proto.Clone(in).(*ParamFromEntireBody)
|
||||
*out = *p
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ParamFromEntireBody. Required by controller-gen.
|
||||
func (in *ParamFromEntireBody) DeepCopy() *ParamFromEntireBody {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ParamFromEntireBody)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new ParamFromEntireBody. Required by controller-gen.
|
||||
func (in *ParamFromEntireBody) DeepCopyInterface() interface{} {
|
||||
return in.DeepCopy()
|
||||
}
|
||||
|
||||
// DeepCopyInto supports using GrpcService within kubernetes types, where deepcopy-gen is used.
|
||||
func (in *GrpcService) DeepCopyInto(out *GrpcService) {
|
||||
p := proto.Clone(in).(*GrpcService)
|
||||
|
||||
@@ -61,6 +61,17 @@ func (this *Param) UnmarshalJSON(b []byte) error {
|
||||
return Http_2RpcUnmarshaler.Unmarshal(bytes.NewReader(b), this)
|
||||
}
|
||||
|
||||
// MarshalJSON is a custom marshaler for ParamFromEntireBody
|
||||
func (this *ParamFromEntireBody) MarshalJSON() ([]byte, error) {
|
||||
str, err := Http_2RpcMarshaler.MarshalToString(this)
|
||||
return []byte(str), err
|
||||
}
|
||||
|
||||
// UnmarshalJSON is a custom unmarshaler for ParamFromEntireBody
|
||||
func (this *ParamFromEntireBody) UnmarshalJSON(b []byte) error {
|
||||
return Http_2RpcUnmarshaler.Unmarshal(bytes.NewReader(b), this)
|
||||
}
|
||||
|
||||
// MarshalJSON is a custom marshaler for GrpcService
|
||||
func (this *GrpcService) MarshalJSON() ([]byte, error) {
|
||||
str, err := Http_2RpcMarshaler.MarshalToString(this)
|
||||
|
||||
236
cmd/hgctl/config/gateway_config.go
Normal file
236
cmd/hgctl/config/gateway_config.go
Normal file
@@ -0,0 +1,236 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/kubernetes"
|
||||
"github.com/alibaba/higress/pkg/cmd/options"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
var (
|
||||
BootstrapEnvoyConfigType EnvoyConfigType = "bootstrap"
|
||||
ClusterEnvoyConfigType EnvoyConfigType = "cluster"
|
||||
EndpointEnvoyConfigType EnvoyConfigType = "endpoint"
|
||||
ListenerEnvoyConfigType EnvoyConfigType = "listener"
|
||||
RouteEnvoyConfigType EnvoyConfigType = "route"
|
||||
AllEnvoyConfigType EnvoyConfigType = "all"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultProxyAdminPort = 15000
|
||||
)
|
||||
|
||||
type EnvoyConfigType string
|
||||
|
||||
type GetEnvoyConfigOptions struct {
|
||||
IncludeEds bool
|
||||
PodName string
|
||||
PodNamespace string
|
||||
BindAddress string
|
||||
Output string
|
||||
EnvoyConfigType EnvoyConfigType
|
||||
}
|
||||
|
||||
func NewDefaultGetEnvoyConfigOptions() *GetEnvoyConfigOptions {
|
||||
return &GetEnvoyConfigOptions{
|
||||
IncludeEds: true,
|
||||
PodName: "",
|
||||
PodNamespace: "higress-system",
|
||||
BindAddress: "localhost",
|
||||
Output: "json",
|
||||
EnvoyConfigType: AllEnvoyConfigType,
|
||||
}
|
||||
}
|
||||
|
||||
func GetEnvoyConfig(config *GetEnvoyConfigOptions) ([]byte, error) {
|
||||
configDump, err := retrieveConfigDump(config.PodName, config.PodNamespace, config.BindAddress, config.IncludeEds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if config.EnvoyConfigType == AllEnvoyConfigType {
|
||||
return configDump, nil
|
||||
}
|
||||
resource, err := getXDSResource(config.EnvoyConfigType, configDump)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return formatGatewayConfig(resource, config.Output)
|
||||
}
|
||||
|
||||
func retrieveConfigDump(podName, podNamespace, bindAddress string, includeEds bool) ([]byte, error) {
|
||||
if podNamespace == "" {
|
||||
return nil, fmt.Errorf("pod namespace is required")
|
||||
}
|
||||
|
||||
if podName == "" {
|
||||
c, err := kubernetes.NewCLIClient(options.DefaultConfigFlags.ToRawKubeConfigLoader())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build kubernetes client: %w", err)
|
||||
}
|
||||
podList, err := c.PodsForSelector(podNamespace, "app=higress-gateway")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(podList.Items) == 0 {
|
||||
return nil, fmt.Errorf("higress gateway pod is not existed in namespace %s", podNamespace)
|
||||
}
|
||||
|
||||
podName = podList.Items[0].GetName()
|
||||
}
|
||||
|
||||
fw, err := portForwarder(types.NamespacedName{
|
||||
Namespace: podNamespace,
|
||||
Name: podName,
|
||||
}, bindAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := fw.Start(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer fw.Stop()
|
||||
|
||||
configDump, err := fetchGatewayConfig(fw, includeEds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return configDump, nil
|
||||
}
|
||||
|
||||
func portForwarder(nn types.NamespacedName, bindAddress string) (kubernetes.PortForwarder, error) {
|
||||
c, err := kubernetes.NewCLIClient(options.DefaultConfigFlags.ToRawKubeConfigLoader())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("build CLI client fail: %w", err)
|
||||
}
|
||||
|
||||
pod, err := c.Pod(nn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get pod %s fail: %w", nn, err)
|
||||
}
|
||||
if pod.Status.Phase != "Running" {
|
||||
return nil, fmt.Errorf("pod %s is not running", nn)
|
||||
}
|
||||
|
||||
fw, err := kubernetes.NewLocalPortForwarder(c, nn, 0, defaultProxyAdminPort, bindAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return fw, nil
|
||||
}
|
||||
|
||||
func formatGatewayConfig(configDump any, output string) ([]byte, error) {
|
||||
out, err := json.MarshalIndent(configDump, "", " ")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if output == "yaml" {
|
||||
out, err = yaml.JSONToYAML(out)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func fetchGatewayConfig(fw kubernetes.PortForwarder, includeEds bool) ([]byte, error) {
|
||||
out, err := configDumpRequest(fw.Address(), includeEds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func configDumpRequest(address string, includeEds bool) ([]byte, error) {
|
||||
url := fmt.Sprintf("http://%s/config_dump", address)
|
||||
if includeEds {
|
||||
url = fmt.Sprintf("%s?include_eds", url)
|
||||
}
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
return io.ReadAll(resp.Body)
|
||||
}
|
||||
|
||||
func getXDSResource(resourceType EnvoyConfigType, configDump []byte) (any, error) {
|
||||
cd := map[string]any{}
|
||||
if err := json.Unmarshal(configDump, &cd); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resourceType == AllEnvoyConfigType {
|
||||
return cd, nil
|
||||
}
|
||||
configs := cd["configs"]
|
||||
globalConfigs := configs.([]any)
|
||||
|
||||
switch resourceType {
|
||||
case BootstrapEnvoyConfigType:
|
||||
for _, config := range globalConfigs {
|
||||
if config.(map[string]interface{})["@type"] == "type.googleapis.com/envoy.admin.v3.BootstrapConfigDump" {
|
||||
return config, nil
|
||||
}
|
||||
}
|
||||
case EndpointEnvoyConfigType:
|
||||
for _, config := range globalConfigs {
|
||||
if config.(map[string]interface{})["@type"] == "type.googleapis.com/envoy.admin.v3.EndpointsConfigDump" {
|
||||
return config, nil
|
||||
}
|
||||
}
|
||||
|
||||
case ClusterEnvoyConfigType:
|
||||
for _, config := range globalConfigs {
|
||||
if config.(map[string]interface{})["@type"] == "type.googleapis.com/envoy.admin.v3.ClustersConfigDump" {
|
||||
return config, nil
|
||||
}
|
||||
}
|
||||
case ListenerEnvoyConfigType:
|
||||
for _, config := range globalConfigs {
|
||||
if config.(map[string]interface{})["@type"] == "type.googleapis.com/envoy.admin.v3.ListenersConfigDump" {
|
||||
return config, nil
|
||||
}
|
||||
}
|
||||
case RouteEnvoyConfigType:
|
||||
for _, config := range globalConfigs {
|
||||
if config.(map[string]interface{})["@type"] == "type.googleapis.com/envoy.admin.v3.RoutesConfigDump" {
|
||||
return config, nil
|
||||
}
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown resourceType %s", resourceType)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unknown resourceType %s", resourceType)
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package hgctl
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -34,10 +34,11 @@ type fakePortForwarder struct {
|
||||
localPort int
|
||||
l net.Listener
|
||||
mux *http.ServeMux
|
||||
stopCh chan struct{}
|
||||
}
|
||||
|
||||
func newFakePortForwarder(b []byte) (kubernetes.PortForwarder, error) {
|
||||
p, err := kubernetes.LocalAvailablePort()
|
||||
p, err := kubernetes.LocalAvailablePort("localhost")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -46,6 +47,7 @@ func newFakePortForwarder(b []byte) (kubernetes.PortForwarder, error) {
|
||||
responseBody: b,
|
||||
localPort: p,
|
||||
mux: http.NewServeMux(),
|
||||
stopCh: make(chan struct{}),
|
||||
}
|
||||
fw.mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
_, _ = w.Write(fw.responseBody)
|
||||
@@ -54,6 +56,10 @@ func newFakePortForwarder(b []byte) (kubernetes.PortForwarder, error) {
|
||||
return fw, nil
|
||||
}
|
||||
|
||||
func (fw *fakePortForwarder) WaitForStop() {
|
||||
<-fw.stopCh
|
||||
}
|
||||
|
||||
func (fw *fakePortForwarder) Start() error {
|
||||
l, err := net.Listen("tcp", fw.Address())
|
||||
if err != nil {
|
||||
@@ -103,7 +109,7 @@ func TestExtractAllConfigDump(t *testing.T) {
|
||||
t.Run(tc.output, func(t *testing.T) {
|
||||
configDump, err := fetchGatewayConfig(fw, true)
|
||||
assert.NoError(t, err)
|
||||
data, err := GetXDSResource(AllEnvoyConfigType, configDump)
|
||||
data, err := getXDSResource(AllEnvoyConfigType, configDump)
|
||||
assert.NoError(t, err)
|
||||
got, err := formatGatewayConfig(data, tc.output)
|
||||
assert.NoError(t, err)
|
||||
@@ -131,7 +137,7 @@ func TestExtractSubResourcesConfigDump(t *testing.T) {
|
||||
cases := []struct {
|
||||
output string
|
||||
expected string
|
||||
resourceType envoyConfigType
|
||||
resourceType EnvoyConfigType
|
||||
}{
|
||||
{
|
||||
output: "json",
|
||||
@@ -186,7 +192,7 @@ func TestExtractSubResourcesConfigDump(t *testing.T) {
|
||||
t.Run(tc.output, func(t *testing.T) {
|
||||
configDump, err := fetchGatewayConfig(fw, false)
|
||||
assert.NoError(t, err)
|
||||
resource, err := GetXDSResource(tc.resourceType, configDump)
|
||||
resource, err := getXDSResource(tc.resourceType, configDump)
|
||||
assert.NoError(t, err)
|
||||
got, err := formatGatewayConfig(resource, tc.output)
|
||||
assert.NoError(t, err)
|
||||
111
envoy/1.20/patches/envoy/20231008-fallback-origin-cluster.patch
Normal file
111
envoy/1.20/patches/envoy/20231008-fallback-origin-cluster.patch
Normal file
@@ -0,0 +1,111 @@
|
||||
diff -Naur envoy/contrib/custom_cluster_plugins/cluster_fallback/source/filter.cc envoy-new/contrib/custom_cluster_plugins/cluster_fallback/source/filter.cc
|
||||
--- envoy/contrib/custom_cluster_plugins/cluster_fallback/source/filter.cc 2023-10-08 15:01:21.960871500 +0800
|
||||
+++ envoy-new/contrib/custom_cluster_plugins/cluster_fallback/source/filter.cc 2023-09-27 17:03:41.613256338 +0800
|
||||
@@ -60,7 +60,7 @@
|
||||
|
||||
for (const auto& cluster_name : first_item->second) {
|
||||
if (hasHealthHost(cluster_name)) {
|
||||
- return base.clone(cluster_name);
|
||||
+ return base.clone(cluster_name, first_item->first);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,8 @@
|
||||
|
||||
auto search = clusters_config_.find(route_entry.clusterName());
|
||||
if (search == clusters_config_.end()) {
|
||||
- ENVOY_LOG(warn, "there is no fallback cluster config, the original routing cluster is returned");
|
||||
+ ENVOY_LOG(warn,
|
||||
+ "there is no fallback cluster config, the original routing cluster is returned");
|
||||
return cluster_entry.getRouteConstSharedPtr();
|
||||
}
|
||||
|
||||
@@ -87,7 +88,7 @@
|
||||
|
||||
for (const auto& cluster_name : search->second) {
|
||||
if (hasHealthHost(cluster_name)) {
|
||||
- return cluster_entry.clone(cluster_name);
|
||||
+ return cluster_entry.clone(cluster_name, search->first);
|
||||
}
|
||||
}
|
||||
|
||||
diff -Naur envoy/source/common/http/headers.h envoy-new/source/common/http/headers.h
|
||||
--- envoy/source/common/http/headers.h 2023-10-08 15:01:21.968871828 +0800
|
||||
+++ envoy-new/source/common/http/headers.h 2023-09-27 18:48:50.059419606 +0800
|
||||
@@ -124,6 +124,7 @@
|
||||
const LowerCaseString TriStartTime{"req-start-time"};
|
||||
const LowerCaseString TriRespStartTime{"resp-start-time"};
|
||||
const LowerCaseString EnvoyOriginalHost{"original-host"};
|
||||
+ const LowerCaseString HigressOriginalService{"x-higress-original-service"};
|
||||
} AliExtendedValues;
|
||||
#endif
|
||||
};
|
||||
diff -Naur envoy/source/common/router/config_impl.cc envoy-new/source/common/router/config_impl.cc
|
||||
--- envoy/source/common/router/config_impl.cc 2023-10-08 15:01:21.968871828 +0800
|
||||
+++ envoy-new/source/common/router/config_impl.cc 2023-09-27 18:49:18.656592237 +0800
|
||||
@@ -563,7 +563,6 @@
|
||||
route.name());
|
||||
}
|
||||
// End Added
|
||||
-
|
||||
}
|
||||
|
||||
bool RouteEntryImplBase::evaluateRuntimeMatch(const uint64_t random_value) const {
|
||||
@@ -662,6 +661,10 @@
|
||||
}
|
||||
|
||||
#if defined(ALIMESH)
|
||||
+ if (!origin_cluster_name_.empty()) {
|
||||
+ headers.addCopy(Http::CustomHeaders::get().AliExtendedValues.HigressOriginalService,
|
||||
+ origin_cluster_name_);
|
||||
+ }
|
||||
headers.setReferenceKey(Http::CustomHeaders::get().AliExtendedValues.EnvoyOriginalHost,
|
||||
headers.getHostValue());
|
||||
#endif
|
||||
diff -Naur envoy/source/common/router/config_impl.h envoy-new/source/common/router/config_impl.h
|
||||
--- envoy/source/common/router/config_impl.h 2023-10-08 15:01:21.968871828 +0800
|
||||
+++ envoy-new/source/common/router/config_impl.h 2023-09-27 18:59:11.196893507 +0800
|
||||
@@ -584,9 +584,13 @@
|
||||
return internal_active_redirect_policy_;
|
||||
}
|
||||
|
||||
- RouteConstSharedPtr clone(const std::string& name) const {
|
||||
- return std::make_shared<DynamicRouteEntry>(this, name);
|
||||
+ RouteConstSharedPtr clone(const std::string& name, const std::string& origin_cluster = "") const {
|
||||
+ auto entry = std::make_shared<DynamicRouteEntry>(this, name);
|
||||
+ entry->setOriginClusterName(origin_cluster);
|
||||
+ return entry;
|
||||
}
|
||||
+
|
||||
+ void setOriginClusterName(const std::string& name) const { origin_cluster_name_ = name; }
|
||||
#endif
|
||||
uint32_t retryShadowBufferLimit() const override { return retry_shadow_buffer_limit_; }
|
||||
const std::vector<ShadowPolicyPtr>& shadowPolicies() const override { return shadow_policies_; }
|
||||
@@ -787,11 +791,17 @@
|
||||
return parent_->internalActiveRedirectPolicy();
|
||||
}
|
||||
|
||||
- RouteConstSharedPtr clone(const std::string& name) const {
|
||||
- return std::make_shared<Envoy::Router::RouteEntryImplBase::DynamicRouteEntry>(parent_, name);
|
||||
+ RouteConstSharedPtr clone(const std::string& name,
|
||||
+ const std::string& origin_cluster = "") const {
|
||||
+ auto entry =
|
||||
+ std::make_shared<Envoy::Router::RouteEntryImplBase::DynamicRouteEntry>(parent_, name);
|
||||
+ entry->setOriginClusterName(origin_cluster);
|
||||
+ return entry;
|
||||
}
|
||||
|
||||
virtual RouteConstSharedPtr getRouteConstSharedPtr() const { return shared_from_this(); }
|
||||
+
|
||||
+ void setOriginClusterName(const std::string& name) { parent_->setOriginClusterName(name); }
|
||||
#endif
|
||||
|
||||
private:
|
||||
@@ -1039,6 +1049,7 @@
|
||||
|
||||
#if defined(ALIMESH)
|
||||
const InternalActiveRedirectPoliciesImpl internal_active_redirect_policy_;
|
||||
+ mutable std::string origin_cluster_name_;
|
||||
#endif
|
||||
};
|
||||
|
||||
315
envoy/1.20/patches/envoy/20231124-rds-optimize.patch
Normal file
315
envoy/1.20/patches/envoy/20231124-rds-optimize.patch
Normal file
@@ -0,0 +1,315 @@
|
||||
diff -Naur envoy/envoy/router/rds.h envoy-new/envoy/router/rds.h
|
||||
--- envoy/envoy/router/rds.h 2023-11-24 10:52:39.914235488 +0800
|
||||
+++ envoy-new/envoy/router/rds.h 2023-11-24 10:47:36.293873127 +0800
|
||||
@@ -51,12 +51,6 @@
|
||||
virtual void onConfigUpdate() PURE;
|
||||
|
||||
/**
|
||||
- * Validate if the route configuration can be applied to the context of the route config provider.
|
||||
- */
|
||||
- virtual void
|
||||
- validateConfig(const envoy::config::route::v3::RouteConfiguration& config) const PURE;
|
||||
-
|
||||
- /**
|
||||
* Callback used to request an update to the route configuration from the management server.
|
||||
* @param for_domain supplies the domain name that virtual hosts must match on
|
||||
* @param thread_local_dispatcher thread-local dispatcher
|
||||
diff -Naur envoy/envoy/router/route_config_update_receiver.h envoy-new/envoy/router/route_config_update_receiver.h
|
||||
--- envoy/envoy/router/route_config_update_receiver.h 2023-11-24 10:52:39.918235651 +0800
|
||||
+++ envoy-new/envoy/router/route_config_update_receiver.h 2023-11-24 10:47:36.293873127 +0800
|
||||
@@ -27,6 +27,7 @@
|
||||
* @param rc supplies the RouteConfiguration.
|
||||
* @param version_info supplies RouteConfiguration version.
|
||||
* @return bool whether RouteConfiguration has been updated.
|
||||
+ * @throw EnvoyException if the new config can't be applied.
|
||||
*/
|
||||
virtual bool onRdsUpdate(const envoy::config::route::v3::RouteConfiguration& rc,
|
||||
const std::string& version_info) PURE;
|
||||
diff -Naur envoy/source/common/router/rds_impl.cc envoy-new/source/common/router/rds_impl.cc
|
||||
--- envoy/source/common/router/rds_impl.cc 2023-11-24 10:52:40.194246888 +0800
|
||||
+++ envoy-new/source/common/router/rds_impl.cc 2023-11-24 10:47:36.293873127 +0800
|
||||
@@ -122,9 +122,6 @@
|
||||
throw EnvoyException(fmt::format("Unexpected RDS configuration (expecting {}): {}",
|
||||
route_config_name_, route_config.name()));
|
||||
}
|
||||
- if (route_config_provider_opt_.has_value()) {
|
||||
- route_config_provider_opt_.value()->validateConfig(route_config);
|
||||
- }
|
||||
std::unique_ptr<Init::ManagerImpl> noop_init_manager;
|
||||
std::unique_ptr<Cleanup> resume_rds;
|
||||
if (config_update_info_->onRdsUpdate(route_config, version_info)) {
|
||||
@@ -292,12 +289,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
-void RdsRouteConfigProviderImpl::validateConfig(
|
||||
- const envoy::config::route::v3::RouteConfiguration& config) const {
|
||||
- // TODO(lizan): consider cache the config here until onConfigUpdate.
|
||||
- ConfigImpl validation_config(config, optional_http_filters_, factory_context_, validator_, false);
|
||||
-}
|
||||
-
|
||||
// Schedules a VHDS request on the main thread and queues up the callback to use when the VHDS
|
||||
// response has been propagated to the worker thread that was the request origin.
|
||||
void RdsRouteConfigProviderImpl::requestVirtualHostsUpdate(
|
||||
diff -Naur envoy/source/common/router/rds_impl.h envoy-new/source/common/router/rds_impl.h
|
||||
--- envoy/source/common/router/rds_impl.h 2023-11-24 10:52:40.194246888 +0800
|
||||
+++ envoy-new/source/common/router/rds_impl.h 2023-11-24 10:47:36.293873127 +0800
|
||||
@@ -81,7 +81,6 @@
|
||||
}
|
||||
SystemTime lastUpdated() const override { return last_updated_; }
|
||||
void onConfigUpdate() override {}
|
||||
- void validateConfig(const envoy::config::route::v3::RouteConfiguration&) const override {}
|
||||
void requestVirtualHostsUpdate(const std::string&, Event::Dispatcher&,
|
||||
std::weak_ptr<Http::RouteConfigUpdatedCallback>) override {
|
||||
NOT_IMPLEMENTED_GCOVR_EXCL_LINE;
|
||||
@@ -209,7 +208,6 @@
|
||||
void requestVirtualHostsUpdate(
|
||||
const std::string& for_domain, Event::Dispatcher& thread_local_dispatcher,
|
||||
std::weak_ptr<Http::RouteConfigUpdatedCallback> route_config_updated_cb) override;
|
||||
- void validateConfig(const envoy::config::route::v3::RouteConfiguration& config) const override;
|
||||
|
||||
private:
|
||||
struct ThreadLocalConfig : public ThreadLocal::ThreadLocalObject {
|
||||
diff -Naur envoy/source/common/router/route_config_update_receiver_impl.cc envoy-new/source/common/router/route_config_update_receiver_impl.cc
|
||||
--- envoy/source/common/router/route_config_update_receiver_impl.cc 2023-11-24 10:52:40.194246888 +0800
|
||||
+++ envoy-new/source/common/router/route_config_update_receiver_impl.cc 2023-11-24 10:47:36.297873290 +0800
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "source/common/router/route_config_update_receiver_impl.h"
|
||||
|
||||
#include <string>
|
||||
+#include <utility>
|
||||
|
||||
#include "envoy/config/route/v3/route.pb.h"
|
||||
#include "envoy/service/discovery/v3/discovery.pb.h"
|
||||
@@ -14,23 +15,49 @@
|
||||
namespace Envoy {
|
||||
namespace Router {
|
||||
|
||||
+namespace {
|
||||
+
|
||||
+// Resets 'route_config::virtual_hosts' by merging VirtualHost contained in
|
||||
+// 'rds_vhosts' and 'vhds_vhosts'.
|
||||
+void rebuildRouteConfigVirtualHosts(
|
||||
+ const std::map<std::string, envoy::config::route::v3::VirtualHost>& rds_vhosts,
|
||||
+ const std::map<std::string, envoy::config::route::v3::VirtualHost>& vhds_vhosts,
|
||||
+ envoy::config::route::v3::RouteConfiguration& route_config) {
|
||||
+ route_config.clear_virtual_hosts();
|
||||
+ for (const auto& vhost : rds_vhosts) {
|
||||
+ route_config.mutable_virtual_hosts()->Add()->CopyFrom(vhost.second);
|
||||
+ }
|
||||
+ for (const auto& vhost : vhds_vhosts) {
|
||||
+ route_config.mutable_virtual_hosts()->Add()->CopyFrom(vhost.second);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+} // namespace
|
||||
+
|
||||
bool RouteConfigUpdateReceiverImpl::onRdsUpdate(
|
||||
const envoy::config::route::v3::RouteConfiguration& rc, const std::string& version_info) {
|
||||
const uint64_t new_hash = MessageUtil::hash(rc);
|
||||
if (new_hash == last_config_hash_) {
|
||||
return false;
|
||||
}
|
||||
- route_config_proto_ = std::make_unique<envoy::config::route::v3::RouteConfiguration>(rc);
|
||||
- last_config_hash_ = new_hash;
|
||||
const uint64_t new_vhds_config_hash = rc.has_vhds() ? MessageUtil::hash(rc.vhds()) : 0ul;
|
||||
+ std::map<std::string, envoy::config::route::v3::VirtualHost> rds_virtual_hosts;
|
||||
+ for (const auto& vhost : rc.virtual_hosts()) {
|
||||
+ rds_virtual_hosts.emplace(vhost.name(), vhost);
|
||||
+ }
|
||||
+ envoy::config::route::v3::RouteConfiguration new_route_config = rc;
|
||||
+ rebuildRouteConfigVirtualHosts(rds_virtual_hosts, *vhds_virtual_hosts_, new_route_config);
|
||||
+ auto new_config = std::make_shared<ConfigImpl>(
|
||||
+ new_route_config, optional_http_filters_, factory_context_,
|
||||
+ factory_context_.messageValidationContext().dynamicValidationVisitor(), false);
|
||||
+ // If the above validation/validation doesn't raise exception, update the
|
||||
+ // other cached config entries.
|
||||
+ config_ = new_config;
|
||||
+ rds_virtual_hosts_ = std::move(rds_virtual_hosts);
|
||||
+ last_config_hash_ = new_hash;
|
||||
+ *route_config_proto_ = std::move(new_route_config);
|
||||
vhds_configuration_changed_ = new_vhds_config_hash != last_vhds_config_hash_;
|
||||
last_vhds_config_hash_ = new_vhds_config_hash;
|
||||
- initializeRdsVhosts(*route_config_proto_);
|
||||
-
|
||||
- rebuildRouteConfig(rds_virtual_hosts_, *vhds_virtual_hosts_, *route_config_proto_);
|
||||
- config_ = std::make_shared<ConfigImpl>(
|
||||
- *route_config_proto_, optional_http_filters_, factory_context_,
|
||||
- factory_context_.messageValidationContext().dynamicValidationVisitor(), false);
|
||||
|
||||
onUpdateCommon(version_info);
|
||||
return true;
|
||||
@@ -50,8 +77,8 @@
|
||||
auto route_config_after_this_update =
|
||||
std::make_unique<envoy::config::route::v3::RouteConfiguration>();
|
||||
route_config_after_this_update->CopyFrom(*route_config_proto_);
|
||||
- rebuildRouteConfig(rds_virtual_hosts_, *vhosts_after_this_update,
|
||||
- *route_config_after_this_update);
|
||||
+ rebuildRouteConfigVirtualHosts(rds_virtual_hosts_, *vhosts_after_this_update,
|
||||
+ *route_config_after_this_update);
|
||||
|
||||
auto new_config = std::make_shared<ConfigImpl>(
|
||||
*route_config_after_this_update, optional_http_filters_, factory_context_,
|
||||
@@ -73,14 +100,6 @@
|
||||
config_info_.emplace(RouteConfigProvider::ConfigInfo{*route_config_proto_, last_config_version_});
|
||||
}
|
||||
|
||||
-void RouteConfigUpdateReceiverImpl::initializeRdsVhosts(
|
||||
- const envoy::config::route::v3::RouteConfiguration& route_configuration) {
|
||||
- rds_virtual_hosts_.clear();
|
||||
- for (const auto& vhost : route_configuration.virtual_hosts()) {
|
||||
- rds_virtual_hosts_.emplace(vhost.name(), vhost);
|
||||
- }
|
||||
-}
|
||||
-
|
||||
bool RouteConfigUpdateReceiverImpl::removeVhosts(
|
||||
std::map<std::string, envoy::config::route::v3::VirtualHost>& vhosts,
|
||||
const Protobuf::RepeatedPtrField<std::string>& removed_vhost_names) {
|
||||
@@ -110,18 +129,5 @@
|
||||
return vhosts_added;
|
||||
}
|
||||
|
||||
-void RouteConfigUpdateReceiverImpl::rebuildRouteConfig(
|
||||
- const std::map<std::string, envoy::config::route::v3::VirtualHost>& rds_vhosts,
|
||||
- const std::map<std::string, envoy::config::route::v3::VirtualHost>& vhds_vhosts,
|
||||
- envoy::config::route::v3::RouteConfiguration& route_config) {
|
||||
- route_config.clear_virtual_hosts();
|
||||
- for (const auto& vhost : rds_vhosts) {
|
||||
- route_config.mutable_virtual_hosts()->Add()->CopyFrom(vhost.second);
|
||||
- }
|
||||
- for (const auto& vhost : vhds_vhosts) {
|
||||
- route_config.mutable_virtual_hosts()->Add()->CopyFrom(vhost.second);
|
||||
- }
|
||||
-}
|
||||
-
|
||||
} // namespace Router
|
||||
} // namespace Envoy
|
||||
diff -Naur envoy/source/common/router/route_config_update_receiver_impl.h envoy-new/source/common/router/route_config_update_receiver_impl.h
|
||||
--- envoy/source/common/router/route_config_update_receiver_impl.h 2023-11-24 10:52:40.194246888 +0800
|
||||
+++ envoy-new/source/common/router/route_config_update_receiver_impl.h 2023-11-24 10:47:36.297873290 +0800
|
||||
@@ -27,15 +27,10 @@
|
||||
std::make_unique<std::map<std::string, envoy::config::route::v3::VirtualHost>>()),
|
||||
vhds_configuration_changed_(true), optional_http_filters_(optional_http_filters) {}
|
||||
|
||||
- void initializeRdsVhosts(const envoy::config::route::v3::RouteConfiguration& route_configuration);
|
||||
bool removeVhosts(std::map<std::string, envoy::config::route::v3::VirtualHost>& vhosts,
|
||||
const Protobuf::RepeatedPtrField<std::string>& removed_vhost_names);
|
||||
bool updateVhosts(std::map<std::string, envoy::config::route::v3::VirtualHost>& vhosts,
|
||||
const VirtualHostRefVector& added_vhosts);
|
||||
- void rebuildRouteConfig(
|
||||
- const std::map<std::string, envoy::config::route::v3::VirtualHost>& rds_vhosts,
|
||||
- const std::map<std::string, envoy::config::route::v3::VirtualHost>& vhds_vhosts,
|
||||
- envoy::config::route::v3::RouteConfiguration& route_config);
|
||||
bool onDemandFetchFailed(const envoy::service::discovery::v3::Resource& resource) const;
|
||||
void onUpdateCommon(const std::string& version_info);
|
||||
|
||||
diff -Naur envoy/source/server/admin/admin.h envoy-new/source/server/admin/admin.h
|
||||
--- envoy/source/server/admin/admin.h 2023-11-24 10:52:41.358294284 +0800
|
||||
+++ envoy-new/source/server/admin/admin.h 2023-11-24 10:47:36.297873290 +0800
|
||||
@@ -234,7 +234,6 @@
|
||||
absl::optional<ConfigInfo> configInfo() const override { return {}; }
|
||||
SystemTime lastUpdated() const override { return time_source_.systemTime(); }
|
||||
void onConfigUpdate() override {}
|
||||
- void validateConfig(const envoy::config::route::v3::RouteConfiguration&) const override {}
|
||||
void requestVirtualHostsUpdate(const std::string&, Event::Dispatcher&,
|
||||
std::weak_ptr<Http::RouteConfigUpdatedCallback>) override {
|
||||
NOT_IMPLEMENTED_GCOVR_EXCL_LINE;
|
||||
diff -Naur envoy/test/common/router/rds_impl_test.cc envoy-new/test/common/router/rds_impl_test.cc
|
||||
--- envoy/test/common/router/rds_impl_test.cc 2023-11-24 10:52:40.714268062 +0800
|
||||
+++ envoy-new/test/common/router/rds_impl_test.cc 2023-11-24 10:47:36.297873290 +0800
|
||||
@@ -528,34 +528,66 @@
|
||||
rds_callbacks_->onConfigUpdate(decoded_resources.refvec_, response1.version_info());
|
||||
}
|
||||
|
||||
-// Validate behavior when the config is delivered but it fails PGV validation.
|
||||
+// Validates behavior when the config is delivered but it fails PGV validation.
|
||||
+// The invalid config won't affect existing valid config.
|
||||
TEST_F(RdsImplTest, FailureInvalidConfig) {
|
||||
InSequence s;
|
||||
|
||||
setup();
|
||||
+ EXPECT_CALL(init_watcher_, ready());
|
||||
|
||||
- const std::string response1_json = R"EOF(
|
||||
+ const std::string valid_json = R"EOF(
|
||||
{
|
||||
"version_info": "1",
|
||||
"resources": [
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration",
|
||||
- "name": "INVALID_NAME_FOR_route_config",
|
||||
+ "name": "foo_route_config",
|
||||
"virtual_hosts": null
|
||||
}
|
||||
]
|
||||
}
|
||||
)EOF";
|
||||
+
|
||||
auto response1 =
|
||||
- TestUtility::parseYaml<envoy::service::discovery::v3::DiscoveryResponse>(response1_json);
|
||||
+ TestUtility::parseYaml<envoy::service::discovery::v3::DiscoveryResponse>(valid_json);
|
||||
const auto decoded_resources =
|
||||
TestUtility::decodeResources<envoy::config::route::v3::RouteConfiguration>(response1);
|
||||
+ EXPECT_NO_THROW(
|
||||
+ rds_callbacks_->onConfigUpdate(decoded_resources.refvec_, response1.version_info()));
|
||||
+ // Sadly the RdsRouteConfigSubscription privately inherited from
|
||||
+ // SubscriptionCallbacks, so we has to use reinterpret_cast here.
|
||||
+ RdsRouteConfigSubscription* rds_subscription =
|
||||
+ reinterpret_cast<RdsRouteConfigSubscription*>(rds_callbacks_);
|
||||
+ auto config_impl_pointer = rds_subscription->routeConfigProvider().value()->config();
|
||||
+ // Now send an invalid config update.
|
||||
+ const std::string invalid_json =
|
||||
+ R"EOF(
|
||||
+{
|
||||
+ "version_info": "1",
|
||||
+ "resources": [
|
||||
+ {
|
||||
+ "@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration",
|
||||
+ "name": "INVALID_NAME_FOR_route_config",
|
||||
+ "virtual_hosts": null
|
||||
+ }
|
||||
+ ]
|
||||
+}
|
||||
+)EOF";
|
||||
+
|
||||
+ auto response2 =
|
||||
+ TestUtility::parseYaml<envoy::service::discovery::v3::DiscoveryResponse>(invalid_json);
|
||||
+ const auto decoded_resources_2 =
|
||||
+ TestUtility::decodeResources<envoy::config::route::v3::RouteConfiguration>(response2);
|
||||
|
||||
- EXPECT_CALL(init_watcher_, ready());
|
||||
EXPECT_THROW_WITH_MESSAGE(
|
||||
- rds_callbacks_->onConfigUpdate(decoded_resources.refvec_, response1.version_info()),
|
||||
+ rds_callbacks_->onConfigUpdate(decoded_resources_2.refvec_, response2.version_info()),
|
||||
EnvoyException,
|
||||
- "Unexpected RDS configuration (expecting foo_route_config): INVALID_NAME_FOR_route_config");
|
||||
+ "Unexpected RDS configuration (expecting foo_route_config): "
|
||||
+ "INVALID_NAME_FOR_route_config");
|
||||
+
|
||||
+ // Verify that the config is still the old value.
|
||||
+ ASSERT_EQ(config_impl_pointer, rds_subscription->routeConfigProvider().value()->config());
|
||||
}
|
||||
|
||||
// rds and vhds configurations change together
|
||||
diff -Naur envoy/test/mocks/router/mocks.h envoy-new/test/mocks/router/mocks.h
|
||||
--- envoy/test/mocks/router/mocks.h 2023-11-24 10:52:41.370294773 +0800
|
||||
+++ envoy-new/test/mocks/router/mocks.h 2023-11-24 10:47:36.301873453 +0800
|
||||
@@ -538,7 +538,6 @@
|
||||
MOCK_METHOD(absl::optional<ConfigInfo>, configInfo, (), (const));
|
||||
MOCK_METHOD(SystemTime, lastUpdated, (), (const));
|
||||
MOCK_METHOD(void, onConfigUpdate, ());
|
||||
- MOCK_METHOD(void, validateConfig, (const envoy::config::route::v3::RouteConfiguration&), (const));
|
||||
MOCK_METHOD(void, requestVirtualHostsUpdate,
|
||||
(const std::string&, Event::Dispatcher&,
|
||||
std::weak_ptr<Http::RouteConfigUpdatedCallback> route_config_updated_cb));
|
||||
diff -Naur envoy/tools/spelling/spelling_dictionary.txt envoy-new/tools/spelling/spelling_dictionary.txt
|
||||
--- envoy/tools/spelling/spelling_dictionary.txt 2023-11-24 10:52:41.370294773 +0800
|
||||
+++ envoy-new/tools/spelling/spelling_dictionary.txt 2023-11-24 10:48:54.969076506 +0800
|
||||
@@ -1303,6 +1303,7 @@
|
||||
ep
|
||||
suri
|
||||
transid
|
||||
+vhosts
|
||||
WAF
|
||||
TRI
|
||||
tmd
|
||||
1502
envoy/1.20/patches/envoy/20231218-dubbo-optimize.patch
Normal file
1502
envoy/1.20/patches/envoy/20231218-dubbo-optimize.patch
Normal file
File diff suppressed because one or more lines are too long
483
envoy/1.20/patches/envoy/20240104-enhance-srds.patch
Normal file
483
envoy/1.20/patches/envoy/20240104-enhance-srds.patch
Normal file
@@ -0,0 +1,483 @@
|
||||
diff -Naur envoy/api/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto envoy-new/api/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto
|
||||
--- envoy/api/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto 2024-01-04 21:07:40.000000000 +0800
|
||||
+++ envoy-new/api/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto 2024-01-04 21:09:13.000000000 +0800
|
||||
@@ -888,11 +888,31 @@
|
||||
}
|
||||
}
|
||||
|
||||
+ message HostValueExtractor {
|
||||
+ option (udpa.annotations.versioning).previous_message_type =
|
||||
+ "envoy.config.filter.network.http_connection_manager.v2.ScopedRoutes.ScopeKeyBuilder."
|
||||
+ "FragmentBuilder.HostValueExtractor";
|
||||
+
|
||||
+ // The maximum number of host superset recomputes. If not specified, defaults to 100.
|
||||
+ google.protobuf.UInt32Value max_recompute_num = 1;
|
||||
+ }
|
||||
+
|
||||
+ message LocalPortValueExtractor {
|
||||
+ option (udpa.annotations.versioning).previous_message_type =
|
||||
+ "envoy.config.filter.network.http_connection_manager.v2.ScopedRoutes.ScopeKeyBuilder."
|
||||
+ "FragmentBuilder.LocalPortValueExtractor";
|
||||
+ }
|
||||
+
|
||||
+
|
||||
oneof type {
|
||||
option (validate.required) = true;
|
||||
|
||||
// Specifies how a header field's value should be extracted.
|
||||
HeaderValueExtractor header_value_extractor = 1;
|
||||
+
|
||||
+ HostValueExtractor host_value_extractor = 101;
|
||||
+
|
||||
+ LocalPortValueExtractor local_port_value_extractor = 102;
|
||||
}
|
||||
}
|
||||
|
||||
diff -Naur envoy/envoy/router/scopes.h envoy-new/envoy/router/scopes.h
|
||||
--- envoy/envoy/router/scopes.h 2024-01-04 21:07:38.000000000 +0800
|
||||
+++ envoy-new/envoy/router/scopes.h 2024-01-04 21:09:13.000000000 +0800
|
||||
@@ -92,7 +92,12 @@
|
||||
* @param headers the request headers to match the scoped routing configuration against.
|
||||
* @return ConfigConstSharedPtr the router's Config matching the request headers.
|
||||
*/
|
||||
+#if defined ALIMESH
|
||||
+ virtual ConfigConstSharedPtr getRouteConfig(const Http::HeaderMap& headers,
|
||||
+ const StreamInfo::StreamInfo& info) const PURE;
|
||||
+#else
|
||||
virtual ConfigConstSharedPtr getRouteConfig(const Http::HeaderMap& headers) const PURE;
|
||||
+#endif
|
||||
|
||||
/**
|
||||
* Based on the incoming HTTP request headers, returns the hash value of its scope key.
|
||||
@@ -100,6 +105,12 @@
|
||||
* @return unique_ptr of the scope key computed from header.
|
||||
*/
|
||||
virtual ScopeKeyPtr computeScopeKey(const Http::HeaderMap&) const { return {}; }
|
||||
+
|
||||
+#if defined(ALIMESH)
|
||||
+ virtual ScopeKeyPtr computeScopeKey(const Http::HeaderMap&, const StreamInfo::StreamInfo&) const {
|
||||
+ return {};
|
||||
+ };
|
||||
+#endif
|
||||
};
|
||||
|
||||
using ScopedConfigConstSharedPtr = std::shared_ptr<const ScopedConfig>;
|
||||
diff -Naur envoy/source/common/http/conn_manager_impl.cc envoy-new/source/common/http/conn_manager_impl.cc
|
||||
--- envoy/source/common/http/conn_manager_impl.cc 2024-01-04 21:07:41.000000000 +0800
|
||||
+++ envoy-new/source/common/http/conn_manager_impl.cc 2024-01-04 21:09:13.000000000 +0800
|
||||
@@ -577,8 +577,13 @@
|
||||
requestVhdsUpdate(host_header, thread_local_dispatcher, std::move(route_config_updated_cb));
|
||||
return;
|
||||
} else if (parent_.snapped_scoped_routes_config_ != nullptr) {
|
||||
+#if defined(ALIMESH)
|
||||
+ Router::ScopeKeyPtr scope_key = parent_.snapped_scoped_routes_config_->computeScopeKey(
|
||||
+ *parent_.request_headers_, parent_.connection()->streamInfo());
|
||||
+#else
|
||||
Router::ScopeKeyPtr scope_key =
|
||||
parent_.snapped_scoped_routes_config_->computeScopeKey(*parent_.request_headers_);
|
||||
+#endif
|
||||
// If scope_key is not null, the scope exists but RouteConfiguration is not initialized.
|
||||
if (scope_key != nullptr) {
|
||||
requestSrdsUpdate(std::move(scope_key), thread_local_dispatcher,
|
||||
@@ -1197,7 +1202,13 @@
|
||||
void ConnectionManagerImpl::ActiveStream::snapScopedRouteConfig() {
|
||||
// NOTE: if a RDS subscription hasn't got a RouteConfiguration back, a Router::NullConfigImpl is
|
||||
// returned, in that case we let it pass.
|
||||
+#if defined(ALIMESH)
|
||||
+ snapped_route_config_ =
|
||||
+ snapped_scoped_routes_config_->getRouteConfig(*request_headers_, connection()->streamInfo());
|
||||
+#else
|
||||
snapped_route_config_ = snapped_scoped_routes_config_->getRouteConfig(*request_headers_);
|
||||
+
|
||||
+#endif
|
||||
if (snapped_route_config_ == nullptr) {
|
||||
ENVOY_STREAM_LOG(trace, "can't find SRDS scope.", *this);
|
||||
// TODO(stevenzzzz): Consider to pass an error message to router filter, so that it can
|
||||
diff -Naur envoy/source/common/router/scoped_config_impl.cc envoy-new/source/common/router/scoped_config_impl.cc
|
||||
--- envoy/source/common/router/scoped_config_impl.cc 2024-01-04 21:07:36.000000000 +0800
|
||||
+++ envoy-new/source/common/router/scoped_config_impl.cc 2024-01-04 21:09:13.000000000 +0800
|
||||
@@ -6,6 +6,160 @@
|
||||
namespace Envoy {
|
||||
namespace Router {
|
||||
|
||||
+#if defined(ALIMESH)
|
||||
+namespace {
|
||||
+
|
||||
+std::string maskFirstDNSLabel(absl::string_view host) {
|
||||
+ if (host == "*") {
|
||||
+ return std::string(host);
|
||||
+ }
|
||||
+ if (host.size() < 2) {
|
||||
+ return "*";
|
||||
+ }
|
||||
+ size_t start_pos = (host[0] == '*' && host[1] == '.') ? 2 : 0;
|
||||
+ size_t dot_pos = host.find('.', start_pos);
|
||||
+ if (dot_pos != absl::string_view::npos) {
|
||||
+ return absl::StrCat("*", absl::string_view(host.data() + dot_pos, host.size() - dot_pos));
|
||||
+ }
|
||||
+ return "*";
|
||||
+}
|
||||
+
|
||||
+} // namespace
|
||||
+
|
||||
+LocalPortValueExtractorImpl::LocalPortValueExtractorImpl(
|
||||
+ ScopedRoutes::ScopeKeyBuilder::FragmentBuilder&& config)
|
||||
+ : FragmentBuilderBase(std::move(config)) {
|
||||
+ ASSERT(config_.type_case() ==
|
||||
+ ScopedRoutes::ScopeKeyBuilder::FragmentBuilder::kLocalPortValueExtractor,
|
||||
+ "local_port_value_extractor is not set.");
|
||||
+}
|
||||
+
|
||||
+std::unique_ptr<ScopeKeyFragmentBase> LocalPortValueExtractorImpl::computeFragment(
|
||||
+ const Http::HeaderMap&, const StreamInfo::StreamInfo& info, ReComputeCbPtr&) const {
|
||||
+ auto port = info.downstreamAddressProvider().localAddress()->ip()->port();
|
||||
+ return std::make_unique<StringKeyFragment>(std::to_string(long(port)));
|
||||
+}
|
||||
+
|
||||
+HostValueExtractorImpl::HostValueExtractorImpl(
|
||||
+ ScopedRoutes::ScopeKeyBuilder::FragmentBuilder&& config)
|
||||
+ : FragmentBuilderBase(std::move(config)),
|
||||
+ host_value_extractor_config_(config_.host_value_extractor()),
|
||||
+ max_recompute_num_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(
|
||||
+ host_value_extractor_config_, max_recompute_num, DefaultMaxRecomputeNum)) {
|
||||
+ ASSERT(config_.type_case() == ScopedRoutes::ScopeKeyBuilder::FragmentBuilder::kHostValueExtractor,
|
||||
+ "host_value_extractor is not set.");
|
||||
+}
|
||||
+
|
||||
+std::unique_ptr<ScopeKeyFragmentBase>
|
||||
+HostValueExtractorImpl::reComputeHelper(const std::string& host, ReComputeCbPtr& next_recompute,
|
||||
+ uint32_t recompute_seq) const {
|
||||
+ if (recompute_seq == max_recompute_num_) {
|
||||
+ ENVOY_LOG_MISC(warn,
|
||||
+ "recompute host fragment failed, maximum number of recalculations exceeded");
|
||||
+ return nullptr;
|
||||
+ }
|
||||
+ if (host == "*") {
|
||||
+ *next_recompute = nullptr;
|
||||
+ return nullptr;
|
||||
+ }
|
||||
+ auto masked_host = maskFirstDNSLabel(host);
|
||||
+ *next_recompute = [this, masked_host, recompute_seq,
|
||||
+ next_recompute]() mutable -> std::unique_ptr<ScopeKeyFragmentBase> {
|
||||
+ return reComputeHelper(masked_host, next_recompute, recompute_seq + 1);
|
||||
+ };
|
||||
+ return std::make_unique<StringKeyFragment>(masked_host);
|
||||
+}
|
||||
+
|
||||
+std::unique_ptr<ScopeKeyFragmentBase>
|
||||
+HostValueExtractorImpl::computeFragment(const Http::HeaderMap& headers,
|
||||
+ const StreamInfo::StreamInfo&,
|
||||
+ ReComputeCbPtr& recompute) const {
|
||||
+ auto fragment = computeFragment(headers);
|
||||
+ auto host = static_cast<const Http::RequestHeaderMap&>(headers).getHostValue();
|
||||
+ *recompute = [this, host, recompute]() mutable -> std::unique_ptr<ScopeKeyFragmentBase> {
|
||||
+ return reComputeHelper(std::string(host), recompute, 0);
|
||||
+ };
|
||||
+ return fragment;
|
||||
+}
|
||||
+
|
||||
+std::unique_ptr<ScopeKeyFragmentBase>
|
||||
+HostValueExtractorImpl::computeFragment(const Http::HeaderMap& headers) const {
|
||||
+ return std::make_unique<StringKeyFragment>(
|
||||
+ static_cast<const Http::RequestHeaderMap&>(headers).getHostValue());
|
||||
+}
|
||||
+
|
||||
+std::unique_ptr<ScopeKeyFragmentBase>
|
||||
+HeaderValueExtractorImpl::computeFragment(const Http::HeaderMap& headers,
|
||||
+ const StreamInfo::StreamInfo&, ReComputeCbPtr&) const {
|
||||
+ return computeFragment(headers);
|
||||
+}
|
||||
+
|
||||
+ScopeKeyPtr ScopeKeyBuilderImpl::computeScopeKey(const Http::HeaderMap& headers,
|
||||
+ const StreamInfo::StreamInfo& info,
|
||||
+ std::function<ScopeKeyPtr()>& recompute) const {
|
||||
+ ScopeKey key;
|
||||
+ bool recomputeable = false;
|
||||
+ auto recompute_cbs = std::make_shared<std::vector<ReComputeCbPtr>>();
|
||||
+ for (const auto& builder : fragment_builders_) {
|
||||
+ // returns nullopt if a null fragment is found.
|
||||
+ ReComputeCbPtr recompute_fragment_cb = std::make_shared<ReComputeCb>();
|
||||
+ std::unique_ptr<ScopeKeyFragmentBase> fragment =
|
||||
+ builder->computeFragment(headers, info, recompute_fragment_cb);
|
||||
+ if (fragment == nullptr) {
|
||||
+ return nullptr;
|
||||
+ }
|
||||
+ if (*recompute_fragment_cb == nullptr) {
|
||||
+ auto key_fragment = static_cast<StringKeyFragment*>(fragment.get());
|
||||
+ auto copied_fragment = std::make_shared<StringKeyFragment>(*key_fragment);
|
||||
+ auto recompute_cb =
|
||||
+ std::make_shared<ReComputeCb>([copied_fragment]() -> std::unique_ptr<StringKeyFragment> {
|
||||
+ return std::make_unique<StringKeyFragment>(*copied_fragment);
|
||||
+ });
|
||||
+ recompute_cbs->push_back(recompute_cb);
|
||||
+ } else {
|
||||
+ recomputeable = true;
|
||||
+ recompute_cbs->push_back(recompute_fragment_cb);
|
||||
+ }
|
||||
+ key.addFragment(std::move(fragment));
|
||||
+ }
|
||||
+ if (recomputeable) {
|
||||
+ recompute = [&recompute, recompute_cbs]() mutable -> ScopeKeyPtr {
|
||||
+ ScopeKey new_key;
|
||||
+ for (auto& cb : *recompute_cbs) {
|
||||
+ auto new_fragment = (*cb)();
|
||||
+ if (new_fragment == nullptr) {
|
||||
+ return nullptr;
|
||||
+ }
|
||||
+ if (*cb == nullptr) {
|
||||
+ recompute = nullptr;
|
||||
+ }
|
||||
+ new_key.addFragment(std::move(new_fragment));
|
||||
+ }
|
||||
+ return std::make_unique<ScopeKey>(std::move(new_key));
|
||||
+ };
|
||||
+ }
|
||||
+ return std::make_unique<ScopeKey>(std::move(key));
|
||||
+}
|
||||
+
|
||||
+ScopeKeyPtr ScopedConfigImpl::computeScopeKey(const Http::HeaderMap& headers,
|
||||
+ const StreamInfo::StreamInfo& info) const {
|
||||
+ std::function<Router::ScopeKeyPtr()> recompute;
|
||||
+ ScopeKeyPtr scope_key = scope_key_builder_.computeScopeKey(headers, info, recompute);
|
||||
+ if (scope_key == nullptr) {
|
||||
+ return nullptr;
|
||||
+ }
|
||||
+ decltype(scoped_route_info_by_key_.begin()) iter;
|
||||
+ do {
|
||||
+ iter = scoped_route_info_by_key_.find(scope_key->hash());
|
||||
+ if (iter != scoped_route_info_by_key_.end()) {
|
||||
+ return scope_key;
|
||||
+ }
|
||||
+ } while (recompute != nullptr && (scope_key = recompute()));
|
||||
+ return nullptr;
|
||||
+}
|
||||
+
|
||||
+#endif
|
||||
+
|
||||
bool ScopeKey::operator!=(const ScopeKey& other) const { return !(*this == other); }
|
||||
|
||||
bool ScopeKey::operator==(const ScopeKey& other) const {
|
||||
@@ -95,6 +249,16 @@
|
||||
: ScopeKeyBuilderBase(std::move(config)) {
|
||||
for (const auto& fragment_builder : config_.fragments()) {
|
||||
switch (fragment_builder.type_case()) {
|
||||
+#if defined(ALIMESH)
|
||||
+ case ScopedRoutes::ScopeKeyBuilder::FragmentBuilder::kHostValueExtractor:
|
||||
+ fragment_builders_.emplace_back(std::make_unique<HostValueExtractorImpl>(
|
||||
+ ScopedRoutes::ScopeKeyBuilder::FragmentBuilder(fragment_builder)));
|
||||
+ break;
|
||||
+ case ScopedRoutes::ScopeKeyBuilder::FragmentBuilder::kLocalPortValueExtractor:
|
||||
+ fragment_builders_.emplace_back(std::make_unique<LocalPortValueExtractorImpl>(
|
||||
+ ScopedRoutes::ScopeKeyBuilder::FragmentBuilder(fragment_builder)));
|
||||
+ break;
|
||||
+#endif
|
||||
case ScopedRoutes::ScopeKeyBuilder::FragmentBuilder::kHeaderValueExtractor:
|
||||
fragment_builders_.emplace_back(std::make_unique<HeaderValueExtractorImpl>(
|
||||
ScopedRoutes::ScopeKeyBuilder::FragmentBuilder(fragment_builder)));
|
||||
@@ -143,6 +307,22 @@
|
||||
}
|
||||
|
||||
Router::ConfigConstSharedPtr
|
||||
+#if defined(ALIMESH)
|
||||
+ScopedConfigImpl::getRouteConfig(const Http::HeaderMap& headers,
|
||||
+ const StreamInfo::StreamInfo& info) const {
|
||||
+ std::function<ScopeKeyPtr()> recompute;
|
||||
+ ScopeKeyPtr scope_key = scope_key_builder_.computeScopeKey(headers, info, recompute);
|
||||
+ if (scope_key == nullptr) {
|
||||
+ return nullptr;
|
||||
+ }
|
||||
+ decltype(scoped_route_info_by_key_.begin()) iter;
|
||||
+ do {
|
||||
+ iter = scoped_route_info_by_key_.find(scope_key->hash());
|
||||
+ if (iter != scoped_route_info_by_key_.end()) {
|
||||
+ return iter->second->routeConfig();
|
||||
+ }
|
||||
+ } while (recompute != nullptr && (scope_key = recompute()));
|
||||
+#else
|
||||
ScopedConfigImpl::getRouteConfig(const Http::HeaderMap& headers) const {
|
||||
ScopeKeyPtr scope_key = scope_key_builder_.computeScopeKey(headers);
|
||||
if (scope_key == nullptr) {
|
||||
@@ -152,6 +332,7 @@
|
||||
if (iter != scoped_route_info_by_key_.end()) {
|
||||
return iter->second->routeConfig();
|
||||
}
|
||||
+#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
diff -Naur envoy/source/common/router/scoped_config_impl.h envoy-new/source/common/router/scoped_config_impl.h
|
||||
--- envoy/source/common/router/scoped_config_impl.h 2024-01-04 21:07:36.000000000 +0800
|
||||
+++ envoy-new/source/common/router/scoped_config_impl.h 2024-01-04 21:09:13.000000000 +0800
|
||||
@@ -22,6 +22,11 @@
|
||||
|
||||
using envoy::extensions::filters::network::http_connection_manager::v3::ScopedRoutes;
|
||||
|
||||
+#if defined(ALIMESH)
|
||||
+using ReComputeCb = std::function<std::unique_ptr<ScopeKeyFragmentBase>()>;
|
||||
+using ReComputeCbPtr = std::shared_ptr<ReComputeCb>;
|
||||
+#endif
|
||||
+
|
||||
/**
|
||||
* Base class for fragment builders.
|
||||
*/
|
||||
@@ -36,6 +41,12 @@
|
||||
virtual std::unique_ptr<ScopeKeyFragmentBase>
|
||||
computeFragment(const Http::HeaderMap& headers) const PURE;
|
||||
|
||||
+#if defined(ALIMESH)
|
||||
+ virtual std::unique_ptr<ScopeKeyFragmentBase>
|
||||
+ computeFragment(const Http::HeaderMap& headers, const StreamInfo::StreamInfo& info,
|
||||
+ ReComputeCbPtr& recompute) const PURE;
|
||||
+#endif
|
||||
+
|
||||
protected:
|
||||
const ScopedRoutes::ScopeKeyBuilder::FragmentBuilder config_;
|
||||
};
|
||||
@@ -47,11 +58,54 @@
|
||||
std::unique_ptr<ScopeKeyFragmentBase>
|
||||
computeFragment(const Http::HeaderMap& headers) const override;
|
||||
|
||||
+#if defined(ALIMESH)
|
||||
+ std::unique_ptr<ScopeKeyFragmentBase> computeFragment(const Http::HeaderMap& headers,
|
||||
+ const StreamInfo::StreamInfo& info,
|
||||
+ ReComputeCbPtr& recompute) const override;
|
||||
+#endif
|
||||
+
|
||||
private:
|
||||
const ScopedRoutes::ScopeKeyBuilder::FragmentBuilder::HeaderValueExtractor&
|
||||
header_value_extractor_config_;
|
||||
};
|
||||
|
||||
+#if defined(ALIMESH)
|
||||
+class HostValueExtractorImpl : public FragmentBuilderBase {
|
||||
+public:
|
||||
+ explicit HostValueExtractorImpl(ScopedRoutes::ScopeKeyBuilder::FragmentBuilder&& config);
|
||||
+
|
||||
+ std::unique_ptr<ScopeKeyFragmentBase>
|
||||
+ computeFragment(const Http::HeaderMap& headers) const override;
|
||||
+
|
||||
+ std::unique_ptr<ScopeKeyFragmentBase> computeFragment(const Http::HeaderMap& headers,
|
||||
+ const StreamInfo::StreamInfo& info,
|
||||
+ ReComputeCbPtr& recompute) const override;
|
||||
+
|
||||
+private:
|
||||
+ std::unique_ptr<ScopeKeyFragmentBase> reComputeHelper(const std::string& host,
|
||||
+ ReComputeCbPtr& next_recompute,
|
||||
+ uint32_t recompute_seq) const;
|
||||
+
|
||||
+ static constexpr uint32_t DefaultMaxRecomputeNum = 100;
|
||||
+
|
||||
+ const ScopedRoutes::ScopeKeyBuilder::FragmentBuilder::HostValueExtractor&
|
||||
+ host_value_extractor_config_;
|
||||
+ const uint32_t max_recompute_num_;
|
||||
+};
|
||||
+
|
||||
+class LocalPortValueExtractorImpl : public FragmentBuilderBase {
|
||||
+public:
|
||||
+ explicit LocalPortValueExtractorImpl(ScopedRoutes::ScopeKeyBuilder::FragmentBuilder&& config);
|
||||
+
|
||||
+ std::unique_ptr<ScopeKeyFragmentBase> computeFragment(const Http::HeaderMap&) const override {
|
||||
+ return nullptr;
|
||||
+ };
|
||||
+
|
||||
+ std::unique_ptr<ScopeKeyFragmentBase> computeFragment(const Http::HeaderMap& headers,
|
||||
+ const StreamInfo::StreamInfo& info,
|
||||
+ ReComputeCbPtr& recompute) const override;
|
||||
+};
|
||||
+#endif
|
||||
/**
|
||||
* Base class for ScopeKeyBuilder implementations.
|
||||
*/
|
||||
@@ -64,6 +118,12 @@
|
||||
// Computes scope key for given headers, returns nullptr if a key can't be computed.
|
||||
virtual ScopeKeyPtr computeScopeKey(const Http::HeaderMap& headers) const PURE;
|
||||
|
||||
+#if defined(ALIMESH)
|
||||
+ virtual ScopeKeyPtr computeScopeKey(const Http::HeaderMap& headers,
|
||||
+ const StreamInfo::StreamInfo& info,
|
||||
+ std::function<ScopeKeyPtr()>& recompute) const PURE;
|
||||
+#endif
|
||||
+
|
||||
protected:
|
||||
const ScopedRoutes::ScopeKeyBuilder config_;
|
||||
};
|
||||
@@ -74,6 +134,11 @@
|
||||
|
||||
ScopeKeyPtr computeScopeKey(const Http::HeaderMap& headers) const override;
|
||||
|
||||
+#if defined(ALIMESH)
|
||||
+ ScopeKeyPtr computeScopeKey(const Http::HeaderMap& headers, const StreamInfo::StreamInfo& info,
|
||||
+ std::function<ScopeKeyPtr()>& recompute) const override;
|
||||
+#endif
|
||||
+
|
||||
private:
|
||||
std::vector<std::unique_ptr<FragmentBuilderBase>> fragment_builders_;
|
||||
};
|
||||
@@ -118,10 +183,20 @@
|
||||
void removeRoutingScopes(const std::vector<std::string>& scope_names);
|
||||
|
||||
// Envoy::Router::ScopedConfig
|
||||
+#if defined(ALIMESH)
|
||||
+ Router::ConfigConstSharedPtr getRouteConfig(const Http::HeaderMap& headers,
|
||||
+ const StreamInfo::StreamInfo& info) const override;
|
||||
+#else
|
||||
Router::ConfigConstSharedPtr getRouteConfig(const Http::HeaderMap& headers) const override;
|
||||
+#endif
|
||||
// The return value is not null only if the scope corresponding to the header exists.
|
||||
ScopeKeyPtr computeScopeKey(const Http::HeaderMap& headers) const override;
|
||||
|
||||
+#if defined(ALIMESH)
|
||||
+ ScopeKeyPtr computeScopeKey(const Http::HeaderMap& headers,
|
||||
+ const StreamInfo::StreamInfo& info) const override;
|
||||
+#endif
|
||||
+
|
||||
private:
|
||||
ScopeKeyBuilderImpl scope_key_builder_;
|
||||
// From scope name to cached ScopedRouteInfo.
|
||||
@@ -135,9 +210,16 @@
|
||||
*/
|
||||
class NullScopedConfigImpl : public ScopedConfig {
|
||||
public:
|
||||
+#if defined(ALIMESH)
|
||||
+ Router::ConfigConstSharedPtr getRouteConfig(const Http::HeaderMap&,
|
||||
+ const StreamInfo::StreamInfo&) const override {
|
||||
+ return std::make_shared<const NullConfigImpl>();
|
||||
+ }
|
||||
+#else
|
||||
Router::ConfigConstSharedPtr getRouteConfig(const Http::HeaderMap&) const override {
|
||||
return std::make_shared<const NullConfigImpl>();
|
||||
}
|
||||
+#endif
|
||||
};
|
||||
|
||||
} // namespace Router
|
||||
diff -Naur envoy/source/extensions/filters/http/on_demand/on_demand_update.cc envoy-new/source/extensions/filters/http/on_demand/on_demand_update.cc
|
||||
--- envoy/source/extensions/filters/http/on_demand/on_demand_update.cc 2024-01-04 21:07:33.000000000 +0800
|
||||
+++ envoy-new/source/extensions/filters/http/on_demand/on_demand_update.cc 2024-01-04 21:09:13.000000000 +0800
|
||||
@@ -50,7 +50,11 @@
|
||||
// This is the callback which is called when an update requested in requestRouteConfigUpdate()
|
||||
// has been propagated to workers, at which point the request processing is restarted from the
|
||||
// beginning.
|
||||
+#if defined(ALIMESH)
|
||||
+void OnDemandRouteUpdate::onRouteConfigUpdateCompletion(bool) {
|
||||
+#else
|
||||
void OnDemandRouteUpdate::onRouteConfigUpdateCompletion(bool route_exists) {
|
||||
+#endif
|
||||
filter_iteration_state_ = Http::FilterHeadersStatus::Continue;
|
||||
|
||||
// Don't call continueDecoding in the middle of decodeHeaders()
|
||||
@@ -58,12 +62,14 @@
|
||||
return;
|
||||
}
|
||||
|
||||
+#if !defined(ALIMESH)
|
||||
if (route_exists && // route can be resolved after an on-demand
|
||||
// VHDS update
|
||||
!callbacks_->decodingBuffer() && // Redirects with body not yet supported.
|
||||
callbacks_->recreateStream(/*headers=*/nullptr)) {
|
||||
return;
|
||||
}
|
||||
+#endif
|
||||
|
||||
// route cannot be resolved after an on-demand VHDS update or
|
||||
// recreating stream failed, continue the filter-chain
|
||||
931
envoy/1.20/patches/go-control-plane/20240104-enhance-srds.patch
Normal file
931
envoy/1.20/patches/go-control-plane/20240104-enhance-srds.patch
Normal file
@@ -0,0 +1,931 @@
|
||||
diff -Naur go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.pb.go go-control-plane-new/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.pb.go
|
||||
--- go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.pb.go 2024-01-04 21:07:22.000000000 +0800
|
||||
+++ go-control-plane-new/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.pb.go 2024-01-04 21:02:10.000000000 +0800
|
||||
@@ -2286,6 +2286,8 @@
|
||||
|
||||
// Types that are assignable to Type:
|
||||
// *ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HeaderValueExtractor_
|
||||
+ // *ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractor_
|
||||
+ // *ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractor_
|
||||
Type isScopedRoutes_ScopeKeyBuilder_FragmentBuilder_Type `protobuf_oneof:"type"`
|
||||
}
|
||||
|
||||
@@ -2335,6 +2337,20 @@
|
||||
return nil
|
||||
}
|
||||
|
||||
+func (x *ScopedRoutes_ScopeKeyBuilder_FragmentBuilder) GetHostValueExtractor() *ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractor {
|
||||
+ if x, ok := x.GetType().(*ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractor_); ok {
|
||||
+ return x.HostValueExtractor
|
||||
+ }
|
||||
+ return nil
|
||||
+}
|
||||
+
|
||||
+func (x *ScopedRoutes_ScopeKeyBuilder_FragmentBuilder) GetLocalPortValueExtractor() *ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractor {
|
||||
+ if x, ok := x.GetType().(*ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractor_); ok {
|
||||
+ return x.LocalPortValueExtractor
|
||||
+ }
|
||||
+ return nil
|
||||
+}
|
||||
+
|
||||
type isScopedRoutes_ScopeKeyBuilder_FragmentBuilder_Type interface {
|
||||
isScopedRoutes_ScopeKeyBuilder_FragmentBuilder_Type()
|
||||
}
|
||||
@@ -2344,9 +2360,23 @@
|
||||
HeaderValueExtractor *ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HeaderValueExtractor `protobuf:"bytes,1,opt,name=header_value_extractor,json=headerValueExtractor,proto3,oneof"`
|
||||
}
|
||||
|
||||
+type ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractor_ struct {
|
||||
+ HostValueExtractor *ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractor `protobuf:"bytes,101,opt,name=host_value_extractor,json=hostValueExtractor,proto3,oneof"`
|
||||
+}
|
||||
+
|
||||
+type ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractor_ struct {
|
||||
+ LocalPortValueExtractor *ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractor `protobuf:"bytes,102,opt,name=local_port_value_extractor,json=localPortValueExtractor,proto3,oneof"`
|
||||
+}
|
||||
+
|
||||
func (*ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HeaderValueExtractor_) isScopedRoutes_ScopeKeyBuilder_FragmentBuilder_Type() {
|
||||
}
|
||||
|
||||
+func (*ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractor_) isScopedRoutes_ScopeKeyBuilder_FragmentBuilder_Type() {
|
||||
+}
|
||||
+
|
||||
+func (*ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractor_) isScopedRoutes_ScopeKeyBuilder_FragmentBuilder_Type() {
|
||||
+}
|
||||
+
|
||||
// Specifies how the value of a header should be extracted.
|
||||
// The following example maps the structure of a header to the fields in this message.
|
||||
//
|
||||
@@ -2475,6 +2505,92 @@
|
||||
func (*ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HeaderValueExtractor_Element) isScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HeaderValueExtractor_ExtractType() {
|
||||
}
|
||||
|
||||
+type ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractor struct {
|
||||
+ state protoimpl.MessageState
|
||||
+ sizeCache protoimpl.SizeCache
|
||||
+ unknownFields protoimpl.UnknownFields
|
||||
+
|
||||
+ // The maximum number of host superset recomputes. If not specified, defaults to 100.
|
||||
+ MaxRecomputeNum *wrappers.UInt32Value `protobuf:"bytes,1,opt,name=max_recompute_num,json=maxRecomputeNum,proto3" json:"max_recompute_num,omitempty"`
|
||||
+}
|
||||
+
|
||||
+func (x *ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractor) Reset() {
|
||||
+ *x = ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractor{}
|
||||
+ if protoimpl.UnsafeEnabled {
|
||||
+ mi := &file_envoy_extensions_filters_network_http_connection_manager_v3_http_connection_manager_proto_msgTypes[18]
|
||||
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
+ ms.StoreMessageInfo(mi)
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+func (x *ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractor) String() string {
|
||||
+ return protoimpl.X.MessageStringOf(x)
|
||||
+}
|
||||
+
|
||||
+func (*ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractor) ProtoMessage() {}
|
||||
+
|
||||
+func (x *ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractor) ProtoReflect() protoreflect.Message {
|
||||
+ mi := &file_envoy_extensions_filters_network_http_connection_manager_v3_http_connection_manager_proto_msgTypes[18]
|
||||
+ if protoimpl.UnsafeEnabled && x != nil {
|
||||
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
+ if ms.LoadMessageInfo() == nil {
|
||||
+ ms.StoreMessageInfo(mi)
|
||||
+ }
|
||||
+ return ms
|
||||
+ }
|
||||
+ return mi.MessageOf(x)
|
||||
+}
|
||||
+
|
||||
+// Deprecated: Use ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractor.ProtoReflect.Descriptor instead.
|
||||
+func (*ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractor) Descriptor() ([]byte, []int) {
|
||||
+ return file_envoy_extensions_filters_network_http_connection_manager_v3_http_connection_manager_proto_rawDescGZIP(), []int{5, 0, 0, 1}
|
||||
+}
|
||||
+
|
||||
+func (x *ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractor) GetMaxRecomputeNum() *wrappers.UInt32Value {
|
||||
+ if x != nil {
|
||||
+ return x.MaxRecomputeNum
|
||||
+ }
|
||||
+ return nil
|
||||
+}
|
||||
+
|
||||
+type ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractor struct {
|
||||
+ state protoimpl.MessageState
|
||||
+ sizeCache protoimpl.SizeCache
|
||||
+ unknownFields protoimpl.UnknownFields
|
||||
+}
|
||||
+
|
||||
+func (x *ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractor) Reset() {
|
||||
+ *x = ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractor{}
|
||||
+ if protoimpl.UnsafeEnabled {
|
||||
+ mi := &file_envoy_extensions_filters_network_http_connection_manager_v3_http_connection_manager_proto_msgTypes[19]
|
||||
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
+ ms.StoreMessageInfo(mi)
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+func (x *ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractor) String() string {
|
||||
+ return protoimpl.X.MessageStringOf(x)
|
||||
+}
|
||||
+
|
||||
+func (*ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractor) ProtoMessage() {}
|
||||
+
|
||||
+func (x *ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractor) ProtoReflect() protoreflect.Message {
|
||||
+ mi := &file_envoy_extensions_filters_network_http_connection_manager_v3_http_connection_manager_proto_msgTypes[19]
|
||||
+ if protoimpl.UnsafeEnabled && x != nil {
|
||||
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
+ if ms.LoadMessageInfo() == nil {
|
||||
+ ms.StoreMessageInfo(mi)
|
||||
+ }
|
||||
+ return ms
|
||||
+ }
|
||||
+ return mi.MessageOf(x)
|
||||
+}
|
||||
+
|
||||
+// Deprecated: Use ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractor.ProtoReflect.Descriptor instead.
|
||||
+func (*ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractor) Descriptor() ([]byte, []int) {
|
||||
+ return file_envoy_extensions_filters_network_http_connection_manager_v3_http_connection_manager_proto_rawDescGZIP(), []int{5, 0, 0, 2}
|
||||
+}
|
||||
+
|
||||
// Specifies a header field's key value pair to match on.
|
||||
type ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HeaderValueExtractor_KvElement struct {
|
||||
state protoimpl.MessageState
|
||||
@@ -2494,7 +2610,7 @@
|
||||
func (x *ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HeaderValueExtractor_KvElement) Reset() {
|
||||
*x = ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HeaderValueExtractor_KvElement{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
- mi := &file_envoy_extensions_filters_network_http_connection_manager_v3_http_connection_manager_proto_msgTypes[18]
|
||||
+ mi := &file_envoy_extensions_filters_network_http_connection_manager_v3_http_connection_manager_proto_msgTypes[20]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -2507,7 +2623,7 @@
|
||||
func (*ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HeaderValueExtractor_KvElement) ProtoMessage() {}
|
||||
|
||||
func (x *ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HeaderValueExtractor_KvElement) ProtoReflect() protoreflect.Message {
|
||||
- mi := &file_envoy_extensions_filters_network_http_connection_manager_v3_http_connection_manager_proto_msgTypes[18]
|
||||
+ mi := &file_envoy_extensions_filters_network_http_connection_manager_v3_http_connection_manager_proto_msgTypes[20]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -3079,7 +3195,7 @@
|
||||
0x74, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61,
|
||||
0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x64, 0x52,
|
||||
0x6f, 0x75, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f,
|
||||
- 0x6e, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x22, 0xe9, 0x0e, 0x0a, 0x0c, 0x53, 0x63, 0x6f, 0x70, 0x65,
|
||||
+ 0x6e, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x22, 0xe1, 0x14, 0x0a, 0x0c, 0x53, 0x63, 0x6f, 0x70, 0x65,
|
||||
0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04,
|
||||
0x6e, 0x61, 0x6d, 0x65, 0x12, 0x8f, 0x01, 0x0a, 0x11, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x6b,
|
||||
@@ -3114,7 +3230,7 @@
|
||||
0x68, 0x74, 0x74, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f,
|
||||
0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65,
|
||||
0x64, 0x52, 0x64, 0x73, 0x48, 0x00, 0x52, 0x09, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x64, 0x52, 0x64,
|
||||
- 0x73, 0x1a, 0xd9, 0x09, 0x0a, 0x0f, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x4b, 0x65, 0x79, 0x42, 0x75,
|
||||
+ 0x73, 0x1a, 0xd1, 0x0f, 0x0a, 0x0f, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x4b, 0x65, 0x79, 0x42, 0x75,
|
||||
0x69, 0x6c, 0x64, 0x65, 0x72, 0x12, 0x91, 0x01, 0x0a, 0x09, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65,
|
||||
0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x69, 0x2e, 0x65, 0x6e, 0x76, 0x6f,
|
||||
0x79, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x66, 0x69, 0x6c,
|
||||
@@ -3124,7 +3240,7 @@
|
||||
0x75, 0x74, 0x65, 0x73, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x69,
|
||||
0x6c, 0x64, 0x65, 0x72, 0x2e, 0x46, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x75, 0x69,
|
||||
0x6c, 0x64, 0x65, 0x72, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x92, 0x01, 0x02, 0x08, 0x01, 0x52, 0x09,
|
||||
- 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0xd5, 0x07, 0x0a, 0x0f, 0x46, 0x72,
|
||||
+ 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0xcd, 0x0d, 0x0a, 0x0f, 0x46, 0x72,
|
||||
0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x12, 0xb6, 0x01,
|
||||
0x0a, 0x16, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x65,
|
||||
0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x7e,
|
||||
@@ -3137,131 +3253,178 @@
|
||||
0x6e, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72,
|
||||
0x56, 0x61, 0x6c, 0x75, 0x65, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x48, 0x00,
|
||||
0x52, 0x14, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x45, 0x78, 0x74,
|
||||
- 0x72, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x1a, 0x8f, 0x05, 0x0a, 0x14, 0x48, 0x65, 0x61, 0x64, 0x65,
|
||||
- 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x12,
|
||||
- 0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa,
|
||||
- 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x11,
|
||||
- 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f,
|
||||
- 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74,
|
||||
- 0x53, 0x65, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x16, 0x0a, 0x05, 0x69, 0x6e, 0x64,
|
||||
- 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65,
|
||||
- 0x78, 0x12, 0xa5, 0x01, 0x0a, 0x07, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20,
|
||||
- 0x01, 0x28, 0x0b, 0x32, 0x88, 0x01, 0x2e, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e, 0x65, 0x78, 0x74,
|
||||
- 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x2e,
|
||||
+ 0x72, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x12, 0xb0, 0x01, 0x0a, 0x14, 0x68, 0x6f, 0x73, 0x74, 0x5f,
|
||||
+ 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x18,
|
||||
+ 0x65, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x7c, 0x2e, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e, 0x65, 0x78,
|
||||
+ 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73,
|
||||
+ 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x63, 0x6f,
|
||||
+ 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72,
|
||||
+ 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73,
|
||||
+ 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72,
|
||||
+ 0x2e, 0x46, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72,
|
||||
+ 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63,
|
||||
+ 0x74, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x12, 0x68, 0x6f, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65,
|
||||
+ 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x12, 0xc1, 0x01, 0x0a, 0x1a, 0x6c, 0x6f,
|
||||
+ 0x63, 0x61, 0x6c, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x65,
|
||||
+ 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x66, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x81,
|
||||
+ 0x01, 0x2e, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f,
|
||||
+ 0x6e, 0x73, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f,
|
||||
+ 0x72, 0x6b, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69,
|
||||
+ 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x63,
|
||||
+ 0x6f, 0x70, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65,
|
||||
+ 0x4b, 0x65, 0x79, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x46, 0x72, 0x61, 0x67, 0x6d,
|
||||
+ 0x65, 0x6e, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x6c,
|
||||
+ 0x50, 0x6f, 0x72, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74,
|
||||
+ 0x6f, 0x72, 0x48, 0x00, 0x52, 0x17, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x6f, 0x72, 0x74, 0x56,
|
||||
+ 0x61, 0x6c, 0x75, 0x65, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x1a, 0x8f, 0x05,
|
||||
+ 0x0a, 0x14, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x45, 0x78, 0x74,
|
||||
+ 0x72, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
|
||||
+ 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x6e,
|
||||
+ 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x73,
|
||||
+ 0x65, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10,
|
||||
+ 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72,
|
||||
+ 0x12, 0x16, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x48,
|
||||
+ 0x00, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0xa5, 0x01, 0x0a, 0x07, 0x65, 0x6c, 0x65,
|
||||
+ 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x88, 0x01, 0x2e, 0x65, 0x6e,
|
||||
+ 0x76, 0x6f, 0x79, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x66,
|
||||
+ 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x68,
|
||||
+ 0x74, 0x74, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d,
|
||||
+ 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x64,
|
||||
+ 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x4b, 0x65, 0x79, 0x42,
|
||||
+ 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x46, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x42,
|
||||
+ 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x56, 0x61, 0x6c,
|
||||
+ 0x75, 0x65, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x4b, 0x76, 0x45, 0x6c,
|
||||
+ 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x07, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74,
|
||||
+ 0x1a, 0xdb, 0x01, 0x0a, 0x09, 0x4b, 0x76, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x25,
|
||||
+ 0x0a, 0x09, 0x73, 0x65, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
+ 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x73, 0x65, 0x70, 0x61,
|
||||
+ 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x19, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01,
|
||||
+ 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x03, 0x6b, 0x65, 0x79,
|
||||
+ 0x3a, 0x8b, 0x01, 0x9a, 0xc5, 0x88, 0x1e, 0x85, 0x01, 0x0a, 0x82, 0x01, 0x65, 0x6e, 0x76, 0x6f,
|
||||
+ 0x79, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2e,
|
||||
0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x63, 0x6f, 0x6e,
|
||||
0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e,
|
||||
- 0x76, 0x33, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x2e,
|
||||
+ 0x76, 0x32, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x2e,
|
||||
0x53, 0x63, 0x6f, 0x70, 0x65, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e,
|
||||
0x46, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e,
|
||||
0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x45, 0x78, 0x74, 0x72, 0x61,
|
||||
- 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x4b, 0x76, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x00,
|
||||
- 0x52, 0x07, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x1a, 0xdb, 0x01, 0x0a, 0x09, 0x4b, 0x76,
|
||||
- 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x25, 0x0a, 0x09, 0x73, 0x65, 0x70, 0x61, 0x72,
|
||||
- 0x61, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72,
|
||||
- 0x02, 0x10, 0x01, 0x52, 0x09, 0x73, 0x65, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x19,
|
||||
- 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04,
|
||||
- 0x72, 0x02, 0x10, 0x01, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x3a, 0x8b, 0x01, 0x9a, 0xc5, 0x88, 0x1e,
|
||||
- 0x85, 0x01, 0x0a, 0x82, 0x01, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69,
|
||||
+ 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x4b, 0x76, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x3a, 0x7f,
|
||||
+ 0x9a, 0xc5, 0x88, 0x1e, 0x7a, 0x0a, 0x78, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e, 0x63, 0x6f, 0x6e,
|
||||
+ 0x66, 0x69, 0x67, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f,
|
||||
+ 0x72, 0x6b, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69,
|
||||
+ 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x63,
|
||||
+ 0x6f, 0x70, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65,
|
||||
+ 0x4b, 0x65, 0x79, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x46, 0x72, 0x61, 0x67, 0x6d,
|
||||
+ 0x65, 0x6e, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65,
|
||||
+ 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x42,
|
||||
+ 0x0e, 0x0a, 0x0c, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x1a,
|
||||
+ 0xdd, 0x01, 0x0a, 0x12, 0x48, 0x6f, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x45, 0x78, 0x74,
|
||||
+ 0x72, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x48, 0x0a, 0x11, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x65,
|
||||
+ 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x5f, 0x6e, 0x75, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
+ 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
+ 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52,
|
||||
+ 0x0f, 0x6d, 0x61, 0x78, 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x4e, 0x75, 0x6d,
|
||||
+ 0x3a, 0x7d, 0x9a, 0xc5, 0x88, 0x1e, 0x78, 0x0a, 0x76, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e, 0x63,
|
||||
+ 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2e, 0x6e, 0x65, 0x74,
|
||||
+ 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63,
|
||||
+ 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e,
|
||||
+ 0x53, 0x63, 0x6f, 0x70, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x53, 0x63, 0x6f,
|
||||
+ 0x70, 0x65, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x46, 0x72, 0x61,
|
||||
+ 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x48, 0x6f, 0x73,
|
||||
+ 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x1a,
|
||||
+ 0x9e, 0x01, 0x0a, 0x17, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x6f, 0x72, 0x74, 0x56, 0x61, 0x6c,
|
||||
+ 0x75, 0x65, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x3a, 0x82, 0x01, 0x9a, 0xc5,
|
||||
+ 0x88, 0x1e, 0x7d, 0x0a, 0x7b, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69,
|
||||
0x67, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
|
||||
0x2e, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x63, 0x6f, 0x70,
|
||||
0x65, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x4b, 0x65,
|
||||
0x79, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x46, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e,
|
||||
- 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x56,
|
||||
- 0x61, 0x6c, 0x75, 0x65, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x4b, 0x76,
|
||||
- 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x3a, 0x7f, 0x9a, 0xc5, 0x88, 0x1e, 0x7a, 0x0a, 0x78,
|
||||
- 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x66, 0x69, 0x6c,
|
||||
- 0x74, 0x65, 0x72, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x68, 0x74, 0x74, 0x70,
|
||||
- 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x6e, 0x61,
|
||||
- 0x67, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x64, 0x52, 0x6f, 0x75,
|
||||
- 0x74, 0x65, 0x73, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x69, 0x6c,
|
||||
- 0x64, 0x65, 0x72, 0x2e, 0x46, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x75, 0x69, 0x6c,
|
||||
- 0x64, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x45,
|
||||
- 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x42, 0x0e, 0x0a, 0x0c, 0x65, 0x78, 0x74, 0x72,
|
||||
- 0x61, 0x63, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3a, 0x6a, 0x9a, 0xc5, 0x88, 0x1e, 0x65, 0x0a,
|
||||
- 0x63, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x66, 0x69,
|
||||
- 0x6c, 0x74, 0x65, 0x72, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x68, 0x74, 0x74,
|
||||
- 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x6e,
|
||||
- 0x61, 0x67, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x64, 0x52, 0x6f,
|
||||
- 0x75, 0x74, 0x65, 0x73, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x69,
|
||||
- 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x46, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x75, 0x69,
|
||||
- 0x6c, 0x64, 0x65, 0x72, 0x42, 0x0b, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x03, 0xf8, 0x42,
|
||||
- 0x01, 0x3a, 0x5a, 0x9a, 0xc5, 0x88, 0x1e, 0x55, 0x0a, 0x53, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e,
|
||||
- 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2e, 0x6e, 0x65,
|
||||
- 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65,
|
||||
- 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x76, 0x32,
|
||||
- 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x53, 0x63,
|
||||
- 0x6f, 0x70, 0x65, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x3a, 0x4a, 0x9a,
|
||||
- 0xc5, 0x88, 0x1e, 0x45, 0x0a, 0x43, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e, 0x63, 0x6f, 0x6e, 0x66,
|
||||
- 0x69, 0x67, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72,
|
||||
- 0x6b, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f,
|
||||
- 0x6e, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x63, 0x6f,
|
||||
- 0x70, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x42, 0x17, 0x0a, 0x10, 0x63, 0x6f, 0x6e,
|
||||
- 0x66, 0x69, 0x67, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x03, 0xf8,
|
||||
- 0x42, 0x01, 0x22, 0xf1, 0x01, 0x0a, 0x09, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x64, 0x52, 0x64, 0x73,
|
||||
- 0x12, 0x65, 0x0a, 0x18, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x64, 0x5f, 0x72, 0x64, 0x73, 0x5f, 0x63,
|
||||
- 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01,
|
||||
- 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69,
|
||||
- 0x67, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x33, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||
- 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01,
|
||||
- 0x52, 0x15, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x64, 0x52, 0x64, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
||||
- 0x67, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x34, 0x0a, 0x16, 0x73, 0x72, 0x64, 0x73, 0x5f,
|
||||
- 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x6f,
|
||||
- 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x73, 0x72, 0x64, 0x73, 0x52, 0x65, 0x73,
|
||||
- 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x3a, 0x47, 0x9a,
|
||||
- 0xc5, 0x88, 0x1e, 0x42, 0x0a, 0x40, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e, 0x63, 0x6f, 0x6e, 0x66,
|
||||
- 0x69, 0x67, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72,
|
||||
- 0x6b, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f,
|
||||
- 0x6e, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x63, 0x6f,
|
||||
- 0x70, 0x65, 0x64, 0x52, 0x64, 0x73, 0x22, 0xcc, 0x02, 0x0a, 0x0a, 0x48, 0x74, 0x74, 0x70, 0x46,
|
||||
- 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20,
|
||||
- 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x6e, 0x61,
|
||||
- 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x0c, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x66,
|
||||
- 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
|
||||
- 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x48, 0x00,
|
||||
- 0x52, 0x0b, 0x74, 0x79, 0x70, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x58, 0x0a,
|
||||
- 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72,
|
||||
- 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e,
|
||||
- 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x33, 0x2e, 0x45,
|
||||
- 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x6f,
|
||||
- 0x75, 0x72, 0x63, 0x65, 0x48, 0x00, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x44, 0x69,
|
||||
- 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x73, 0x5f, 0x6f, 0x70,
|
||||
- 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x73,
|
||||
- 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x3a, 0x48, 0x9a, 0xc5, 0x88, 0x1e, 0x43, 0x0a,
|
||||
- 0x41, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x66, 0x69,
|
||||
- 0x6c, 0x74, 0x65, 0x72, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x68, 0x74, 0x74,
|
||||
- 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x6e,
|
||||
- 0x61, 0x67, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x46, 0x69, 0x6c, 0x74,
|
||||
- 0x65, 0x72, 0x42, 0x0d, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x74, 0x79, 0x70,
|
||||
- 0x65, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x52, 0x06, 0x63,
|
||||
- 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x9f, 0x01, 0x0a, 0x12, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
- 0x74, 0x49, 0x44, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x0a, 0x0c,
|
||||
- 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01,
|
||||
- 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||
- 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x0b, 0x74, 0x79, 0x70, 0x65, 0x64, 0x43,
|
||||
- 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x3a, 0x50, 0x9a, 0xc5, 0x88, 0x1e, 0x4b, 0x0a, 0x49, 0x65, 0x6e,
|
||||
+ 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x6f,
|
||||
+ 0x72, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x6f, 0x72,
|
||||
+ 0x3a, 0x6a, 0x9a, 0xc5, 0x88, 0x1e, 0x65, 0x0a, 0x63, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e, 0x63,
|
||||
+ 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2e, 0x6e, 0x65, 0x74,
|
||||
+ 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63,
|
||||
+ 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e,
|
||||
+ 0x53, 0x63, 0x6f, 0x70, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x53, 0x63, 0x6f,
|
||||
+ 0x70, 0x65, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x46, 0x72, 0x61,
|
||||
+ 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x42, 0x0b, 0x0a, 0x04,
|
||||
+ 0x74, 0x79, 0x70, 0x65, 0x12, 0x03, 0xf8, 0x42, 0x01, 0x3a, 0x5a, 0x9a, 0xc5, 0x88, 0x1e, 0x55,
|
||||
+ 0x0a, 0x53, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x66,
|
||||
+ 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x68, 0x74,
|
||||
+ 0x74, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61,
|
||||
+ 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x64, 0x52,
|
||||
+ 0x6f, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x4b, 0x65, 0x79, 0x42, 0x75,
|
||||
+ 0x69, 0x6c, 0x64, 0x65, 0x72, 0x3a, 0x4a, 0x9a, 0xc5, 0x88, 0x1e, 0x45, 0x0a, 0x43, 0x65, 0x6e,
|
||||
0x76, 0x6f, 0x79, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65,
|
||||
0x72, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x63,
|
||||
0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
|
||||
- 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x44, 0x45, 0x78,
|
||||
- 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x8e, 0x01, 0x0a, 0x20, 0x45, 0x6e, 0x76, 0x6f,
|
||||
- 0x79, 0x4d, 0x6f, 0x62, 0x69, 0x6c, 0x65, 0x48, 0x74, 0x74, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x65,
|
||||
- 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x12, 0x6a, 0x0a, 0x06,
|
||||
- 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x52, 0x2e, 0x65,
|
||||
- 0x6e, 0x76, 0x6f, 0x79, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e,
|
||||
- 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e,
|
||||
- 0x68, 0x74, 0x74, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f,
|
||||
- 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x76, 0x33, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x43,
|
||||
- 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72,
|
||||
- 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x71, 0x0a, 0x49, 0x69, 0x6f, 0x2e, 0x65,
|
||||
- 0x6e, 0x76, 0x6f, 0x79, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e,
|
||||
- 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65,
|
||||
- 0x72, 0x73, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x5f,
|
||||
- 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67,
|
||||
- 0x65, 0x72, 0x2e, 0x76, 0x33, 0x42, 0x1a, 0x48, 0x74, 0x74, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x65,
|
||||
- 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x74,
|
||||
- 0x6f, 0x50, 0x01, 0xba, 0x80, 0xc8, 0xd1, 0x06, 0x02, 0x10, 0x02, 0x62, 0x06, 0x70, 0x72, 0x6f,
|
||||
- 0x74, 0x6f, 0x33,
|
||||
+ 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65,
|
||||
+ 0x73, 0x42, 0x17, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x73, 0x70, 0x65, 0x63,
|
||||
+ 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x03, 0xf8, 0x42, 0x01, 0x22, 0xf1, 0x01, 0x0a, 0x09, 0x53,
|
||||
+ 0x63, 0x6f, 0x70, 0x65, 0x64, 0x52, 0x64, 0x73, 0x12, 0x65, 0x0a, 0x18, 0x73, 0x63, 0x6f, 0x70,
|
||||
+ 0x65, 0x64, 0x5f, 0x72, 0x64, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x73, 0x6f,
|
||||
+ 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x6e, 0x76,
|
||||
+ 0x6f, 0x79, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76,
|
||||
+ 0x33, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, 0x08,
|
||||
+ 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x15, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x64,
|
||||
+ 0x52, 0x64, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12,
|
||||
+ 0x34, 0x0a, 0x16, 0x73, 0x72, 0x64, 0x73, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
|
||||
+ 0x73, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
+ 0x14, 0x73, 0x72, 0x64, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4c, 0x6f,
|
||||
+ 0x63, 0x61, 0x74, 0x6f, 0x72, 0x3a, 0x47, 0x9a, 0xc5, 0x88, 0x1e, 0x42, 0x0a, 0x40, 0x65, 0x6e,
|
||||
+ 0x76, 0x6f, 0x79, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65,
|
||||
+ 0x72, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x63,
|
||||
+ 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
|
||||
+ 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x64, 0x52, 0x64, 0x73, 0x22, 0xcc,
|
||||
+ 0x02, 0x0a, 0x0a, 0x48, 0x74, 0x74, 0x70, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x1b, 0x0a,
|
||||
+ 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04,
|
||||
+ 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x0c, 0x74, 0x79,
|
||||
+ 0x70, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b,
|
||||
+ 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
||||
+ 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x48, 0x00, 0x52, 0x0b, 0x74, 0x79, 0x70, 0x65, 0x64, 0x43,
|
||||
+ 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x58, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f,
|
||||
+ 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
+ 0x2b, 0x2e, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x63,
|
||||
+ 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x33, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e,
|
||||
+ 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x48, 0x00, 0x52, 0x0f,
|
||||
+ 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x12,
|
||||
+ 0x1f, 0x0a, 0x0b, 0x69, 0x73, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x18, 0x06,
|
||||
+ 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x73, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c,
|
||||
+ 0x3a, 0x48, 0x9a, 0xc5, 0x88, 0x1e, 0x43, 0x0a, 0x41, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e, 0x63,
|
||||
+ 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2e, 0x6e, 0x65, 0x74,
|
||||
+ 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63,
|
||||
+ 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e,
|
||||
+ 0x48, 0x74, 0x74, 0x70, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x42, 0x0d, 0x0a, 0x0b, 0x63, 0x6f,
|
||||
+ 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a,
|
||||
+ 0x04, 0x08, 0x02, 0x10, 0x03, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x9f, 0x01,
|
||||
+ 0x0a, 0x12, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x44, 0x45, 0x78, 0x74, 0x65, 0x6e,
|
||||
+ 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x0a, 0x0c, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x63, 0x6f,
|
||||
+ 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f,
|
||||
+ 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79,
|
||||
+ 0x52, 0x0b, 0x74, 0x79, 0x70, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x3a, 0x50, 0x9a,
|
||||
+ 0xc5, 0x88, 0x1e, 0x4b, 0x0a, 0x49, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e, 0x63, 0x6f, 0x6e, 0x66,
|
||||
+ 0x69, 0x67, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72,
|
||||
+ 0x6b, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f,
|
||||
+ 0x6e, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x52, 0x65, 0x71,
|
||||
+ 0x75, 0x65, 0x73, 0x74, 0x49, 0x44, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22,
|
||||
+ 0x8e, 0x01, 0x0a, 0x20, 0x45, 0x6e, 0x76, 0x6f, 0x79, 0x4d, 0x6f, 0x62, 0x69, 0x6c, 0x65, 0x48,
|
||||
+ 0x74, 0x74, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x6e,
|
||||
+ 0x61, 0x67, 0x65, 0x72, 0x12, 0x6a, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01,
|
||||
+ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x52, 0x2e, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e, 0x65, 0x78, 0x74,
|
||||
+ 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x2e,
|
||||
+ 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x63, 0x6f, 0x6e,
|
||||
+ 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e,
|
||||
+ 0x76, 0x33, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f,
|
||||
+ 0x6e, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||
+ 0x42, 0x71, 0x0a, 0x49, 0x69, 0x6f, 0x2e, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x70, 0x72, 0x6f, 0x78,
|
||||
+ 0x79, 0x2e, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f,
|
||||
+ 0x6e, 0x73, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f,
|
||||
+ 0x72, 0x6b, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69,
|
||||
+ 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x76, 0x33, 0x42, 0x1a, 0x48,
|
||||
+ 0x74, 0x74, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x6e,
|
||||
+ 0x61, 0x67, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0xba, 0x80, 0xc8, 0xd1, 0x06,
|
||||
+ 0x02, 0x10, 0x02, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -3277,7 +3440,7 @@
|
||||
}
|
||||
|
||||
var file_envoy_extensions_filters_network_http_connection_manager_v3_http_connection_manager_proto_enumTypes = make([]protoimpl.EnumInfo, 5)
|
||||
-var file_envoy_extensions_filters_network_http_connection_manager_v3_http_connection_manager_proto_msgTypes = make([]protoimpl.MessageInfo, 19)
|
||||
+var file_envoy_extensions_filters_network_http_connection_manager_v3_http_connection_manager_proto_msgTypes = make([]protoimpl.MessageInfo, 21)
|
||||
var file_envoy_extensions_filters_network_http_connection_manager_v3_http_connection_manager_proto_goTypes = []interface{}{
|
||||
(HttpConnectionManager_CodecType)(0), // 0: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.CodecType
|
||||
(HttpConnectionManager_ServerHeaderTransformation)(0), // 1: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.ServerHeaderTransformation
|
||||
@@ -3302,102 +3465,107 @@
|
||||
(*ScopedRoutes_ScopeKeyBuilder)(nil), // 20: envoy.extensions.filters.network.http_connection_manager.v3.ScopedRoutes.ScopeKeyBuilder
|
||||
(*ScopedRoutes_ScopeKeyBuilder_FragmentBuilder)(nil), // 21: envoy.extensions.filters.network.http_connection_manager.v3.ScopedRoutes.ScopeKeyBuilder.FragmentBuilder
|
||||
(*ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HeaderValueExtractor)(nil), // 22: envoy.extensions.filters.network.http_connection_manager.v3.ScopedRoutes.ScopeKeyBuilder.FragmentBuilder.HeaderValueExtractor
|
||||
- (*ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HeaderValueExtractor_KvElement)(nil), // 23: envoy.extensions.filters.network.http_connection_manager.v3.ScopedRoutes.ScopeKeyBuilder.FragmentBuilder.HeaderValueExtractor.KvElement
|
||||
- (*v32.RouteConfiguration)(nil), // 24: envoy.config.route.v3.RouteConfiguration
|
||||
- (*wrappers.BoolValue)(nil), // 25: google.protobuf.BoolValue
|
||||
- (*v3.HttpProtocolOptions)(nil), // 26: envoy.config.core.v3.HttpProtocolOptions
|
||||
- (*v3.Http1ProtocolOptions)(nil), // 27: envoy.config.core.v3.Http1ProtocolOptions
|
||||
- (*v3.Http2ProtocolOptions)(nil), // 28: envoy.config.core.v3.Http2ProtocolOptions
|
||||
- (*v3.Http3ProtocolOptions)(nil), // 29: envoy.config.core.v3.Http3ProtocolOptions
|
||||
- (*v3.SchemeHeaderTransformation)(nil), // 30: envoy.config.core.v3.SchemeHeaderTransformation
|
||||
- (*wrappers.UInt32Value)(nil), // 31: google.protobuf.UInt32Value
|
||||
- (*duration.Duration)(nil), // 32: google.protobuf.Duration
|
||||
- (*v31.AccessLog)(nil), // 33: envoy.config.accesslog.v3.AccessLog
|
||||
- (*v3.TypedExtensionConfig)(nil), // 34: envoy.config.core.v3.TypedExtensionConfig
|
||||
- (*v3.SubstitutionFormatString)(nil), // 35: envoy.config.core.v3.SubstitutionFormatString
|
||||
- (*v31.AccessLogFilter)(nil), // 36: envoy.config.accesslog.v3.AccessLogFilter
|
||||
- (*v3.DataSource)(nil), // 37: envoy.config.core.v3.DataSource
|
||||
- (*v3.HeaderValueOption)(nil), // 38: envoy.config.core.v3.HeaderValueOption
|
||||
- (*v3.ConfigSource)(nil), // 39: envoy.config.core.v3.ConfigSource
|
||||
- (*v32.ScopedRouteConfiguration)(nil), // 40: envoy.config.route.v3.ScopedRouteConfiguration
|
||||
- (*any.Any)(nil), // 41: google.protobuf.Any
|
||||
- (*v3.ExtensionConfigSource)(nil), // 42: envoy.config.core.v3.ExtensionConfigSource
|
||||
- (*v33.Percent)(nil), // 43: envoy.type.v3.Percent
|
||||
- (*v34.CustomTag)(nil), // 44: envoy.type.tracing.v3.CustomTag
|
||||
- (*v35.Tracing_Http)(nil), // 45: envoy.config.trace.v3.Tracing.Http
|
||||
- (*v36.PathTransformation)(nil), // 46: envoy.type.http.v3.PathTransformation
|
||||
+ (*ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractor)(nil), // 23: envoy.extensions.filters.network.http_connection_manager.v3.ScopedRoutes.ScopeKeyBuilder.FragmentBuilder.HostValueExtractor
|
||||
+ (*ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractor)(nil), // 24: envoy.extensions.filters.network.http_connection_manager.v3.ScopedRoutes.ScopeKeyBuilder.FragmentBuilder.LocalPortValueExtractor
|
||||
+ (*ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HeaderValueExtractor_KvElement)(nil), // 25: envoy.extensions.filters.network.http_connection_manager.v3.ScopedRoutes.ScopeKeyBuilder.FragmentBuilder.HeaderValueExtractor.KvElement
|
||||
+ (*v32.RouteConfiguration)(nil), // 26: envoy.config.route.v3.RouteConfiguration
|
||||
+ (*wrappers.BoolValue)(nil), // 27: google.protobuf.BoolValue
|
||||
+ (*v3.HttpProtocolOptions)(nil), // 28: envoy.config.core.v3.HttpProtocolOptions
|
||||
+ (*v3.Http1ProtocolOptions)(nil), // 29: envoy.config.core.v3.Http1ProtocolOptions
|
||||
+ (*v3.Http2ProtocolOptions)(nil), // 30: envoy.config.core.v3.Http2ProtocolOptions
|
||||
+ (*v3.Http3ProtocolOptions)(nil), // 31: envoy.config.core.v3.Http3ProtocolOptions
|
||||
+ (*v3.SchemeHeaderTransformation)(nil), // 32: envoy.config.core.v3.SchemeHeaderTransformation
|
||||
+ (*wrappers.UInt32Value)(nil), // 33: google.protobuf.UInt32Value
|
||||
+ (*duration.Duration)(nil), // 34: google.protobuf.Duration
|
||||
+ (*v31.AccessLog)(nil), // 35: envoy.config.accesslog.v3.AccessLog
|
||||
+ (*v3.TypedExtensionConfig)(nil), // 36: envoy.config.core.v3.TypedExtensionConfig
|
||||
+ (*v3.SubstitutionFormatString)(nil), // 37: envoy.config.core.v3.SubstitutionFormatString
|
||||
+ (*v31.AccessLogFilter)(nil), // 38: envoy.config.accesslog.v3.AccessLogFilter
|
||||
+ (*v3.DataSource)(nil), // 39: envoy.config.core.v3.DataSource
|
||||
+ (*v3.HeaderValueOption)(nil), // 40: envoy.config.core.v3.HeaderValueOption
|
||||
+ (*v3.ConfigSource)(nil), // 41: envoy.config.core.v3.ConfigSource
|
||||
+ (*v32.ScopedRouteConfiguration)(nil), // 42: envoy.config.route.v3.ScopedRouteConfiguration
|
||||
+ (*any.Any)(nil), // 43: google.protobuf.Any
|
||||
+ (*v3.ExtensionConfigSource)(nil), // 44: envoy.config.core.v3.ExtensionConfigSource
|
||||
+ (*v33.Percent)(nil), // 45: envoy.type.v3.Percent
|
||||
+ (*v34.CustomTag)(nil), // 46: envoy.type.tracing.v3.CustomTag
|
||||
+ (*v35.Tracing_Http)(nil), // 47: envoy.config.trace.v3.Tracing.Http
|
||||
+ (*v36.PathTransformation)(nil), // 48: envoy.type.http.v3.PathTransformation
|
||||
}
|
||||
var file_envoy_extensions_filters_network_http_connection_manager_v3_http_connection_manager_proto_depIdxs = []int32{
|
||||
0, // 0: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.codec_type:type_name -> envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.CodecType
|
||||
8, // 1: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.rds:type_name -> envoy.extensions.filters.network.http_connection_manager.v3.Rds
|
||||
- 24, // 2: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.route_config:type_name -> envoy.config.route.v3.RouteConfiguration
|
||||
+ 26, // 2: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.route_config:type_name -> envoy.config.route.v3.RouteConfiguration
|
||||
10, // 3: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.scoped_routes:type_name -> envoy.extensions.filters.network.http_connection_manager.v3.ScopedRoutes
|
||||
12, // 4: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.http_filters:type_name -> envoy.extensions.filters.network.http_connection_manager.v3.HttpFilter
|
||||
- 25, // 5: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.add_user_agent:type_name -> google.protobuf.BoolValue
|
||||
+ 27, // 5: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.add_user_agent:type_name -> google.protobuf.BoolValue
|
||||
15, // 6: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.tracing:type_name -> envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.Tracing
|
||||
- 26, // 7: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.common_http_protocol_options:type_name -> envoy.config.core.v3.HttpProtocolOptions
|
||||
- 27, // 8: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.http_protocol_options:type_name -> envoy.config.core.v3.Http1ProtocolOptions
|
||||
- 28, // 9: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.http2_protocol_options:type_name -> envoy.config.core.v3.Http2ProtocolOptions
|
||||
- 29, // 10: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.http3_protocol_options:type_name -> envoy.config.core.v3.Http3ProtocolOptions
|
||||
+ 28, // 7: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.common_http_protocol_options:type_name -> envoy.config.core.v3.HttpProtocolOptions
|
||||
+ 29, // 8: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.http_protocol_options:type_name -> envoy.config.core.v3.Http1ProtocolOptions
|
||||
+ 30, // 9: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.http2_protocol_options:type_name -> envoy.config.core.v3.Http2ProtocolOptions
|
||||
+ 31, // 10: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.http3_protocol_options:type_name -> envoy.config.core.v3.Http3ProtocolOptions
|
||||
1, // 11: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.server_header_transformation:type_name -> envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.ServerHeaderTransformation
|
||||
- 30, // 12: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.scheme_header_transformation:type_name -> envoy.config.core.v3.SchemeHeaderTransformation
|
||||
- 31, // 13: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.max_request_headers_kb:type_name -> google.protobuf.UInt32Value
|
||||
- 32, // 14: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.stream_idle_timeout:type_name -> google.protobuf.Duration
|
||||
- 32, // 15: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.request_timeout:type_name -> google.protobuf.Duration
|
||||
- 32, // 16: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.request_headers_timeout:type_name -> google.protobuf.Duration
|
||||
- 32, // 17: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.drain_timeout:type_name -> google.protobuf.Duration
|
||||
- 32, // 18: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.delayed_close_timeout:type_name -> google.protobuf.Duration
|
||||
- 33, // 19: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.access_log:type_name -> envoy.config.accesslog.v3.AccessLog
|
||||
- 25, // 20: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.use_remote_address:type_name -> google.protobuf.BoolValue
|
||||
- 34, // 21: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.original_ip_detection_extensions:type_name -> envoy.config.core.v3.TypedExtensionConfig
|
||||
+ 32, // 12: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.scheme_header_transformation:type_name -> envoy.config.core.v3.SchemeHeaderTransformation
|
||||
+ 33, // 13: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.max_request_headers_kb:type_name -> google.protobuf.UInt32Value
|
||||
+ 34, // 14: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.stream_idle_timeout:type_name -> google.protobuf.Duration
|
||||
+ 34, // 15: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.request_timeout:type_name -> google.protobuf.Duration
|
||||
+ 34, // 16: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.request_headers_timeout:type_name -> google.protobuf.Duration
|
||||
+ 34, // 17: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.drain_timeout:type_name -> google.protobuf.Duration
|
||||
+ 34, // 18: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.delayed_close_timeout:type_name -> google.protobuf.Duration
|
||||
+ 35, // 19: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.access_log:type_name -> envoy.config.accesslog.v3.AccessLog
|
||||
+ 27, // 20: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.use_remote_address:type_name -> google.protobuf.BoolValue
|
||||
+ 36, // 21: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.original_ip_detection_extensions:type_name -> envoy.config.core.v3.TypedExtensionConfig
|
||||
16, // 22: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.internal_address_config:type_name -> envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.InternalAddressConfig
|
||||
- 25, // 23: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.generate_request_id:type_name -> google.protobuf.BoolValue
|
||||
+ 27, // 23: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.generate_request_id:type_name -> google.protobuf.BoolValue
|
||||
2, // 24: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.forward_client_cert_details:type_name -> envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.ForwardClientCertDetails
|
||||
17, // 25: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.set_current_client_cert_details:type_name -> envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.SetCurrentClientCertDetails
|
||||
18, // 26: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.upgrade_configs:type_name -> envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.UpgradeConfig
|
||||
- 25, // 27: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.normalize_path:type_name -> google.protobuf.BoolValue
|
||||
+ 27, // 27: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.normalize_path:type_name -> google.protobuf.BoolValue
|
||||
3, // 28: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.path_with_escaped_slashes_action:type_name -> envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.PathWithEscapedSlashesAction
|
||||
13, // 29: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.request_id_extension:type_name -> envoy.extensions.filters.network.http_connection_manager.v3.RequestIDExtension
|
||||
6, // 30: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.local_reply_config:type_name -> envoy.extensions.filters.network.http_connection_manager.v3.LocalReplyConfig
|
||||
- 25, // 31: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.stream_error_on_invalid_http_message:type_name -> google.protobuf.BoolValue
|
||||
+ 27, // 31: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.stream_error_on_invalid_http_message:type_name -> google.protobuf.BoolValue
|
||||
19, // 32: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.path_normalization_options:type_name -> envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.PathNormalizationOptions
|
||||
7, // 33: envoy.extensions.filters.network.http_connection_manager.v3.LocalReplyConfig.mappers:type_name -> envoy.extensions.filters.network.http_connection_manager.v3.ResponseMapper
|
||||
- 35, // 34: envoy.extensions.filters.network.http_connection_manager.v3.LocalReplyConfig.body_format:type_name -> envoy.config.core.v3.SubstitutionFormatString
|
||||
- 36, // 35: envoy.extensions.filters.network.http_connection_manager.v3.ResponseMapper.filter:type_name -> envoy.config.accesslog.v3.AccessLogFilter
|
||||
- 31, // 36: envoy.extensions.filters.network.http_connection_manager.v3.ResponseMapper.status_code:type_name -> google.protobuf.UInt32Value
|
||||
- 37, // 37: envoy.extensions.filters.network.http_connection_manager.v3.ResponseMapper.body:type_name -> envoy.config.core.v3.DataSource
|
||||
- 35, // 38: envoy.extensions.filters.network.http_connection_manager.v3.ResponseMapper.body_format_override:type_name -> envoy.config.core.v3.SubstitutionFormatString
|
||||
- 38, // 39: envoy.extensions.filters.network.http_connection_manager.v3.ResponseMapper.headers_to_add:type_name -> envoy.config.core.v3.HeaderValueOption
|
||||
- 39, // 40: envoy.extensions.filters.network.http_connection_manager.v3.Rds.config_source:type_name -> envoy.config.core.v3.ConfigSource
|
||||
- 40, // 41: envoy.extensions.filters.network.http_connection_manager.v3.ScopedRouteConfigurationsList.scoped_route_configurations:type_name -> envoy.config.route.v3.ScopedRouteConfiguration
|
||||
+ 37, // 34: envoy.extensions.filters.network.http_connection_manager.v3.LocalReplyConfig.body_format:type_name -> envoy.config.core.v3.SubstitutionFormatString
|
||||
+ 38, // 35: envoy.extensions.filters.network.http_connection_manager.v3.ResponseMapper.filter:type_name -> envoy.config.accesslog.v3.AccessLogFilter
|
||||
+ 33, // 36: envoy.extensions.filters.network.http_connection_manager.v3.ResponseMapper.status_code:type_name -> google.protobuf.UInt32Value
|
||||
+ 39, // 37: envoy.extensions.filters.network.http_connection_manager.v3.ResponseMapper.body:type_name -> envoy.config.core.v3.DataSource
|
||||
+ 37, // 38: envoy.extensions.filters.network.http_connection_manager.v3.ResponseMapper.body_format_override:type_name -> envoy.config.core.v3.SubstitutionFormatString
|
||||
+ 40, // 39: envoy.extensions.filters.network.http_connection_manager.v3.ResponseMapper.headers_to_add:type_name -> envoy.config.core.v3.HeaderValueOption
|
||||
+ 41, // 40: envoy.extensions.filters.network.http_connection_manager.v3.Rds.config_source:type_name -> envoy.config.core.v3.ConfigSource
|
||||
+ 42, // 41: envoy.extensions.filters.network.http_connection_manager.v3.ScopedRouteConfigurationsList.scoped_route_configurations:type_name -> envoy.config.route.v3.ScopedRouteConfiguration
|
||||
20, // 42: envoy.extensions.filters.network.http_connection_manager.v3.ScopedRoutes.scope_key_builder:type_name -> envoy.extensions.filters.network.http_connection_manager.v3.ScopedRoutes.ScopeKeyBuilder
|
||||
- 39, // 43: envoy.extensions.filters.network.http_connection_manager.v3.ScopedRoutes.rds_config_source:type_name -> envoy.config.core.v3.ConfigSource
|
||||
+ 41, // 43: envoy.extensions.filters.network.http_connection_manager.v3.ScopedRoutes.rds_config_source:type_name -> envoy.config.core.v3.ConfigSource
|
||||
9, // 44: envoy.extensions.filters.network.http_connection_manager.v3.ScopedRoutes.scoped_route_configurations_list:type_name -> envoy.extensions.filters.network.http_connection_manager.v3.ScopedRouteConfigurationsList
|
||||
11, // 45: envoy.extensions.filters.network.http_connection_manager.v3.ScopedRoutes.scoped_rds:type_name -> envoy.extensions.filters.network.http_connection_manager.v3.ScopedRds
|
||||
- 39, // 46: envoy.extensions.filters.network.http_connection_manager.v3.ScopedRds.scoped_rds_config_source:type_name -> envoy.config.core.v3.ConfigSource
|
||||
- 41, // 47: envoy.extensions.filters.network.http_connection_manager.v3.HttpFilter.typed_config:type_name -> google.protobuf.Any
|
||||
- 42, // 48: envoy.extensions.filters.network.http_connection_manager.v3.HttpFilter.config_discovery:type_name -> envoy.config.core.v3.ExtensionConfigSource
|
||||
- 41, // 49: envoy.extensions.filters.network.http_connection_manager.v3.RequestIDExtension.typed_config:type_name -> google.protobuf.Any
|
||||
+ 41, // 46: envoy.extensions.filters.network.http_connection_manager.v3.ScopedRds.scoped_rds_config_source:type_name -> envoy.config.core.v3.ConfigSource
|
||||
+ 43, // 47: envoy.extensions.filters.network.http_connection_manager.v3.HttpFilter.typed_config:type_name -> google.protobuf.Any
|
||||
+ 44, // 48: envoy.extensions.filters.network.http_connection_manager.v3.HttpFilter.config_discovery:type_name -> envoy.config.core.v3.ExtensionConfigSource
|
||||
+ 43, // 49: envoy.extensions.filters.network.http_connection_manager.v3.RequestIDExtension.typed_config:type_name -> google.protobuf.Any
|
||||
5, // 50: envoy.extensions.filters.network.http_connection_manager.v3.EnvoyMobileHttpConnectionManager.config:type_name -> envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
|
||||
- 43, // 51: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.Tracing.client_sampling:type_name -> envoy.type.v3.Percent
|
||||
- 43, // 52: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.Tracing.random_sampling:type_name -> envoy.type.v3.Percent
|
||||
- 43, // 53: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.Tracing.overall_sampling:type_name -> envoy.type.v3.Percent
|
||||
- 31, // 54: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.Tracing.max_path_tag_length:type_name -> google.protobuf.UInt32Value
|
||||
- 44, // 55: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.Tracing.custom_tags:type_name -> envoy.type.tracing.v3.CustomTag
|
||||
- 45, // 56: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.Tracing.provider:type_name -> envoy.config.trace.v3.Tracing.Http
|
||||
- 25, // 57: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.SetCurrentClientCertDetails.subject:type_name -> google.protobuf.BoolValue
|
||||
+ 45, // 51: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.Tracing.client_sampling:type_name -> envoy.type.v3.Percent
|
||||
+ 45, // 52: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.Tracing.random_sampling:type_name -> envoy.type.v3.Percent
|
||||
+ 45, // 53: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.Tracing.overall_sampling:type_name -> envoy.type.v3.Percent
|
||||
+ 33, // 54: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.Tracing.max_path_tag_length:type_name -> google.protobuf.UInt32Value
|
||||
+ 46, // 55: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.Tracing.custom_tags:type_name -> envoy.type.tracing.v3.CustomTag
|
||||
+ 47, // 56: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.Tracing.provider:type_name -> envoy.config.trace.v3.Tracing.Http
|
||||
+ 27, // 57: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.SetCurrentClientCertDetails.subject:type_name -> google.protobuf.BoolValue
|
||||
12, // 58: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.UpgradeConfig.filters:type_name -> envoy.extensions.filters.network.http_connection_manager.v3.HttpFilter
|
||||
- 25, // 59: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.UpgradeConfig.enabled:type_name -> google.protobuf.BoolValue
|
||||
- 46, // 60: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.PathNormalizationOptions.forwarding_transformation:type_name -> envoy.type.http.v3.PathTransformation
|
||||
- 46, // 61: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.PathNormalizationOptions.http_filter_transformation:type_name -> envoy.type.http.v3.PathTransformation
|
||||
+ 27, // 59: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.UpgradeConfig.enabled:type_name -> google.protobuf.BoolValue
|
||||
+ 48, // 60: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.PathNormalizationOptions.forwarding_transformation:type_name -> envoy.type.http.v3.PathTransformation
|
||||
+ 48, // 61: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.PathNormalizationOptions.http_filter_transformation:type_name -> envoy.type.http.v3.PathTransformation
|
||||
21, // 62: envoy.extensions.filters.network.http_connection_manager.v3.ScopedRoutes.ScopeKeyBuilder.fragments:type_name -> envoy.extensions.filters.network.http_connection_manager.v3.ScopedRoutes.ScopeKeyBuilder.FragmentBuilder
|
||||
22, // 63: envoy.extensions.filters.network.http_connection_manager.v3.ScopedRoutes.ScopeKeyBuilder.FragmentBuilder.header_value_extractor:type_name -> envoy.extensions.filters.network.http_connection_manager.v3.ScopedRoutes.ScopeKeyBuilder.FragmentBuilder.HeaderValueExtractor
|
||||
- 23, // 64: envoy.extensions.filters.network.http_connection_manager.v3.ScopedRoutes.ScopeKeyBuilder.FragmentBuilder.HeaderValueExtractor.element:type_name -> envoy.extensions.filters.network.http_connection_manager.v3.ScopedRoutes.ScopeKeyBuilder.FragmentBuilder.HeaderValueExtractor.KvElement
|
||||
- 65, // [65:65] is the sub-list for method output_type
|
||||
- 65, // [65:65] is the sub-list for method input_type
|
||||
- 65, // [65:65] is the sub-list for extension type_name
|
||||
- 65, // [65:65] is the sub-list for extension extendee
|
||||
- 0, // [0:65] is the sub-list for field type_name
|
||||
+ 23, // 64: envoy.extensions.filters.network.http_connection_manager.v3.ScopedRoutes.ScopeKeyBuilder.FragmentBuilder.host_value_extractor:type_name -> envoy.extensions.filters.network.http_connection_manager.v3.ScopedRoutes.ScopeKeyBuilder.FragmentBuilder.HostValueExtractor
|
||||
+ 24, // 65: envoy.extensions.filters.network.http_connection_manager.v3.ScopedRoutes.ScopeKeyBuilder.FragmentBuilder.local_port_value_extractor:type_name -> envoy.extensions.filters.network.http_connection_manager.v3.ScopedRoutes.ScopeKeyBuilder.FragmentBuilder.LocalPortValueExtractor
|
||||
+ 25, // 66: envoy.extensions.filters.network.http_connection_manager.v3.ScopedRoutes.ScopeKeyBuilder.FragmentBuilder.HeaderValueExtractor.element:type_name -> envoy.extensions.filters.network.http_connection_manager.v3.ScopedRoutes.ScopeKeyBuilder.FragmentBuilder.HeaderValueExtractor.KvElement
|
||||
+ 33, // 67: envoy.extensions.filters.network.http_connection_manager.v3.ScopedRoutes.ScopeKeyBuilder.FragmentBuilder.HostValueExtractor.max_recompute_num:type_name -> google.protobuf.UInt32Value
|
||||
+ 68, // [68:68] is the sub-list for method output_type
|
||||
+ 68, // [68:68] is the sub-list for method input_type
|
||||
+ 68, // [68:68] is the sub-list for extension type_name
|
||||
+ 68, // [68:68] is the sub-list for extension extendee
|
||||
+ 0, // [0:68] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() {
|
||||
@@ -3625,6 +3793,30 @@
|
||||
}
|
||||
}
|
||||
file_envoy_extensions_filters_network_http_connection_manager_v3_http_connection_manager_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} {
|
||||
+ switch v := v.(*ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractor); i {
|
||||
+ case 0:
|
||||
+ return &v.state
|
||||
+ case 1:
|
||||
+ return &v.sizeCache
|
||||
+ case 2:
|
||||
+ return &v.unknownFields
|
||||
+ default:
|
||||
+ return nil
|
||||
+ }
|
||||
+ }
|
||||
+ file_envoy_extensions_filters_network_http_connection_manager_v3_http_connection_manager_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} {
|
||||
+ switch v := v.(*ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractor); i {
|
||||
+ case 0:
|
||||
+ return &v.state
|
||||
+ case 1:
|
||||
+ return &v.sizeCache
|
||||
+ case 2:
|
||||
+ return &v.unknownFields
|
||||
+ default:
|
||||
+ return nil
|
||||
+ }
|
||||
+ }
|
||||
+ file_envoy_extensions_filters_network_http_connection_manager_v3_http_connection_manager_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HeaderValueExtractor_KvElement); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@@ -3653,6 +3845,8 @@
|
||||
}
|
||||
file_envoy_extensions_filters_network_http_connection_manager_v3_http_connection_manager_proto_msgTypes[16].OneofWrappers = []interface{}{
|
||||
(*ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HeaderValueExtractor_)(nil),
|
||||
+ (*ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractor_)(nil),
|
||||
+ (*ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractor_)(nil),
|
||||
}
|
||||
file_envoy_extensions_filters_network_http_connection_manager_v3_http_connection_manager_proto_msgTypes[17].OneofWrappers = []interface{}{
|
||||
(*ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HeaderValueExtractor_Index)(nil),
|
||||
@@ -3664,7 +3858,7 @@
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_envoy_extensions_filters_network_http_connection_manager_v3_http_connection_manager_proto_rawDesc,
|
||||
NumEnums: 5,
|
||||
- NumMessages: 19,
|
||||
+ NumMessages: 21,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
diff -Naur go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.pb.validate.go go-control-plane-new/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.pb.validate.go
|
||||
--- go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.pb.validate.go 2024-01-04 21:07:22.000000000 +0800
|
||||
+++ go-control-plane-new/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.pb.validate.go 2024-01-04 21:02:10.000000000 +0800
|
||||
@@ -1986,6 +1986,30 @@
|
||||
}
|
||||
}
|
||||
|
||||
+ case *ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractor_:
|
||||
+
|
||||
+ if v, ok := interface{}(m.GetHostValueExtractor()).(interface{ Validate() error }); ok {
|
||||
+ if err := v.Validate(); err != nil {
|
||||
+ return ScopedRoutes_ScopeKeyBuilder_FragmentBuilderValidationError{
|
||||
+ field: "HostValueExtractor",
|
||||
+ reason: "embedded message failed validation",
|
||||
+ cause: err,
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ case *ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractor_:
|
||||
+
|
||||
+ if v, ok := interface{}(m.GetLocalPortValueExtractor()).(interface{ Validate() error }); ok {
|
||||
+ if err := v.Validate(); err != nil {
|
||||
+ return ScopedRoutes_ScopeKeyBuilder_FragmentBuilderValidationError{
|
||||
+ field: "LocalPortValueExtractor",
|
||||
+ reason: "embedded message failed validation",
|
||||
+ cause: err,
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
default:
|
||||
return ScopedRoutes_ScopeKeyBuilder_FragmentBuilderValidationError{
|
||||
field: "Type",
|
||||
@@ -2162,6 +2186,172 @@
|
||||
} = ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HeaderValueExtractorValidationError{}
|
||||
|
||||
// Validate checks the field values on
|
||||
+// ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractor with the
|
||||
+// rules defined in the proto definition for this message. If any rules are
|
||||
+// violated, an error is returned.
|
||||
+func (m *ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractor) Validate() error {
|
||||
+ if m == nil {
|
||||
+ return nil
|
||||
+ }
|
||||
+
|
||||
+ if v, ok := interface{}(m.GetMaxRecomputeNum()).(interface{ Validate() error }); ok {
|
||||
+ if err := v.Validate(); err != nil {
|
||||
+ return ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractorValidationError{
|
||||
+ field: "MaxRecomputeNum",
|
||||
+ reason: "embedded message failed validation",
|
||||
+ cause: err,
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return nil
|
||||
+}
|
||||
+
|
||||
+// ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractorValidationError
|
||||
+// is the validation error returned by
|
||||
+// ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractor.Validate if
|
||||
+// the designated constraints aren't met.
|
||||
+type ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractorValidationError struct {
|
||||
+ field string
|
||||
+ reason string
|
||||
+ cause error
|
||||
+ key bool
|
||||
+}
|
||||
+
|
||||
+// Field function returns field value.
|
||||
+func (e ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractorValidationError) Field() string {
|
||||
+ return e.field
|
||||
+}
|
||||
+
|
||||
+// Reason function returns reason value.
|
||||
+func (e ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractorValidationError) Reason() string {
|
||||
+ return e.reason
|
||||
+}
|
||||
+
|
||||
+// Cause function returns cause value.
|
||||
+func (e ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractorValidationError) Cause() error {
|
||||
+ return e.cause
|
||||
+}
|
||||
+
|
||||
+// Key function returns key value.
|
||||
+func (e ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractorValidationError) Key() bool {
|
||||
+ return e.key
|
||||
+}
|
||||
+
|
||||
+// ErrorName returns error name.
|
||||
+func (e ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractorValidationError) ErrorName() string {
|
||||
+ return "ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractorValidationError"
|
||||
+}
|
||||
+
|
||||
+// Error satisfies the builtin error interface
|
||||
+func (e ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractorValidationError) Error() string {
|
||||
+ cause := ""
|
||||
+ if e.cause != nil {
|
||||
+ cause = fmt.Sprintf(" | caused by: %v", e.cause)
|
||||
+ }
|
||||
+
|
||||
+ key := ""
|
||||
+ if e.key {
|
||||
+ key = "key for "
|
||||
+ }
|
||||
+
|
||||
+ return fmt.Sprintf(
|
||||
+ "invalid %sScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractor.%s: %s%s",
|
||||
+ key,
|
||||
+ e.field,
|
||||
+ e.reason,
|
||||
+ cause)
|
||||
+}
|
||||
+
|
||||
+var _ error = ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractorValidationError{}
|
||||
+
|
||||
+var _ interface {
|
||||
+ Field() string
|
||||
+ Reason() string
|
||||
+ Key() bool
|
||||
+ Cause() error
|
||||
+ ErrorName() string
|
||||
+} = ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractorValidationError{}
|
||||
+
|
||||
+// Validate checks the field values on
|
||||
+// ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractor with
|
||||
+// the rules defined in the proto definition for this message. If any rules
|
||||
+// are violated, an error is returned.
|
||||
+func (m *ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractor) Validate() error {
|
||||
+ if m == nil {
|
||||
+ return nil
|
||||
+ }
|
||||
+
|
||||
+ return nil
|
||||
+}
|
||||
+
|
||||
+// ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractorValidationError
|
||||
+// is the validation error returned by
|
||||
+// ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractor.Validate
|
||||
+// if the designated constraints aren't met.
|
||||
+type ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractorValidationError struct {
|
||||
+ field string
|
||||
+ reason string
|
||||
+ cause error
|
||||
+ key bool
|
||||
+}
|
||||
+
|
||||
+// Field function returns field value.
|
||||
+func (e ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractorValidationError) Field() string {
|
||||
+ return e.field
|
||||
+}
|
||||
+
|
||||
+// Reason function returns reason value.
|
||||
+func (e ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractorValidationError) Reason() string {
|
||||
+ return e.reason
|
||||
+}
|
||||
+
|
||||
+// Cause function returns cause value.
|
||||
+func (e ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractorValidationError) Cause() error {
|
||||
+ return e.cause
|
||||
+}
|
||||
+
|
||||
+// Key function returns key value.
|
||||
+func (e ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractorValidationError) Key() bool {
|
||||
+ return e.key
|
||||
+}
|
||||
+
|
||||
+// ErrorName returns error name.
|
||||
+func (e ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractorValidationError) ErrorName() string {
|
||||
+ return "ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractorValidationError"
|
||||
+}
|
||||
+
|
||||
+// Error satisfies the builtin error interface
|
||||
+func (e ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractorValidationError) Error() string {
|
||||
+ cause := ""
|
||||
+ if e.cause != nil {
|
||||
+ cause = fmt.Sprintf(" | caused by: %v", e.cause)
|
||||
+ }
|
||||
+
|
||||
+ key := ""
|
||||
+ if e.key {
|
||||
+ key = "key for "
|
||||
+ }
|
||||
+
|
||||
+ return fmt.Sprintf(
|
||||
+ "invalid %sScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractor.%s: %s%s",
|
||||
+ key,
|
||||
+ e.field,
|
||||
+ e.reason,
|
||||
+ cause)
|
||||
+}
|
||||
+
|
||||
+var _ error = ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractorValidationError{}
|
||||
+
|
||||
+var _ interface {
|
||||
+ Field() string
|
||||
+ Reason() string
|
||||
+ Key() bool
|
||||
+ Cause() error
|
||||
+ ErrorName() string
|
||||
+} = ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractorValidationError{}
|
||||
+
|
||||
+// Validate checks the field values on
|
||||
// ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HeaderValueExtractor_KvElement
|
||||
// with the rules defined in the proto definition for this message. If any
|
||||
// rules are violated, an error is returned.
|
||||
214
go.mod
214
go.mod
@@ -10,17 +10,22 @@ replace github.com/chzyer/logex => github.com/chzyer/logex v1.1.11-0.20170329064
|
||||
// Avoid pulling in incompatible libraries
|
||||
replace github.com/docker/distribution => github.com/docker/distribution v0.0.0-20191216044856-a8371794149d
|
||||
|
||||
replace github.com/docker/docker => github.com/moby/moby v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible
|
||||
|
||||
// Client-go does not handle different versions of mergo due to some breaking changes - use the matching version
|
||||
replace github.com/imdario/mergo => github.com/imdario/mergo v0.3.5
|
||||
|
||||
require (
|
||||
github.com/AlecAivazis/survey/v2 v2.3.6
|
||||
github.com/agiledragon/gomonkey/v2 v2.9.0
|
||||
github.com/avast/retry-go/v4 v4.3.4
|
||||
github.com/compose-spec/compose-go v1.8.2
|
||||
github.com/docker/cli v20.10.20+incompatible
|
||||
github.com/docker/compose/v2 v2.0.0-00010101000000-000000000000
|
||||
github.com/docker/docker v20.10.20+incompatible
|
||||
github.com/dubbogo/go-zookeeper v1.0.4-0.20211212162352-f9d2183d89d5
|
||||
github.com/dubbogo/gost v1.13.1
|
||||
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1
|
||||
github.com/fatih/color v1.14.1
|
||||
github.com/fatih/structtag v1.2.0
|
||||
github.com/gogo/protobuf v1.3.2
|
||||
github.com/golang/protobuf v1.5.2
|
||||
github.com/google/go-cmp v0.5.9
|
||||
@@ -28,32 +33,38 @@ require (
|
||||
github.com/hashicorp/consul/api v1.23.0
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/hudl/fargo v1.4.0
|
||||
github.com/iancoleman/orderedmap v0.3.0
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/nacos-group/nacos-sdk-go v1.0.8
|
||||
github.com/nacos-group/nacos-sdk-go/v2 v2.1.2
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/spf13/cobra v1.2.1
|
||||
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
|
||||
github.com/spf13/cobra v1.6.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/spf13/viper v1.8.1
|
||||
github.com/stretchr/testify v1.8.3
|
||||
go.uber.org/atomic v1.9.0
|
||||
google.golang.org/grpc v1.48.0
|
||||
google.golang.org/protobuf v1.28.0
|
||||
google.golang.org/protobuf v1.28.1
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
istio.io/api v0.0.0-20211122181927-8da52c66ff23
|
||||
istio.io/client-go v1.12.0-rc.1.0.20211118171212-b744b6f111e4
|
||||
istio.io/gogo-genproto v0.0.0-20211115195057-0e34bdd2be67
|
||||
istio.io/istio v0.0.0
|
||||
istio.io/pkg v0.0.0-20211115195056-e379f31ee62a
|
||||
k8s.io/api v0.22.2
|
||||
k8s.io/apimachinery v0.22.2
|
||||
k8s.io/api v0.24.1
|
||||
k8s.io/apimachinery v0.24.1
|
||||
k8s.io/cli-runtime v0.22.2
|
||||
k8s.io/client-go v0.22.2
|
||||
k8s.io/client-go v0.24.1
|
||||
k8s.io/kubectl v0.22.2
|
||||
sigs.k8s.io/controller-runtime v0.10.2
|
||||
sigs.k8s.io/yaml v1.3.0
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.97.0 // indirect
|
||||
cloud.google.com/go v0.98.0 // indirect
|
||||
cloud.google.com/go/logging v1.4.2 // indirect
|
||||
contrib.go.opencensus.io/exporter/prometheus v0.4.0 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
@@ -63,51 +74,69 @@ require (
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
|
||||
github.com/Azure/go-autorest/logger v0.2.1 // indirect
|
||||
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
|
||||
github.com/BurntSushi/toml v0.3.1 // indirect
|
||||
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.1.1 // indirect
|
||||
github.com/Masterminds/sprig/v3 v3.2.2 // indirect
|
||||
github.com/Microsoft/go-winio v0.5.0 // indirect
|
||||
github.com/Microsoft/hcsshim v0.8.21 // indirect
|
||||
github.com/Masterminds/squirrel v1.5.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.5.2 // indirect
|
||||
github.com/Microsoft/hcsshim v0.9.6 // indirect
|
||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||
github.com/RageCage64/multilinediff v0.2.0 // indirect
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1704 // indirect
|
||||
github.com/armon/go-metrics v0.4.1 // indirect
|
||||
github.com/aws/aws-sdk-go v1.41.7 // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 // indirect
|
||||
github.com/aws/aws-sdk-go v1.43.16 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bmatcuk/doublestar/v4 v4.6.0 // indirect
|
||||
github.com/braydonk/yaml v0.7.0 // indirect
|
||||
github.com/buger/goterm v1.0.4 // indirect
|
||||
github.com/buger/jsonparser v1.1.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.1.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.1.2 // indirect
|
||||
github.com/census-instrumentation/opencensus-proto v0.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect
|
||||
github.com/clbanning/mxj v1.8.4 // indirect
|
||||
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20220520190051-1e77728a1eaa // indirect
|
||||
github.com/containerd/continuity v0.1.0 // indirect
|
||||
github.com/compose-spec/godotenv v1.1.1 // indirect
|
||||
github.com/containerd/cgroups v1.0.4 // indirect
|
||||
github.com/containerd/console v1.0.3 // indirect
|
||||
github.com/containerd/containerd v1.6.14 // indirect
|
||||
github.com/containerd/continuity v0.3.0 // indirect
|
||||
github.com/containerd/typeurl v1.0.2 // indirect
|
||||
github.com/coreos/go-oidc/v3 v3.1.0 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.2.3 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0 // indirect
|
||||
github.com/docker/cli v20.10.7+incompatible // indirect
|
||||
github.com/docker/distribution v2.7.1+incompatible // indirect
|
||||
github.com/docker/docker v20.10.7+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.6.3 // indirect
|
||||
github.com/docker/go-units v0.4.0 // indirect
|
||||
github.com/distribution/distribution/v3 v3.0.0-20221201083218-92d136e113cf // indirect
|
||||
github.com/docker/buildx v0.9.1 // indirect
|
||||
github.com/docker/distribution v2.8.1+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.7.0 // indirect
|
||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
github.com/docker/go-metrics v0.0.1 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0 // indirect
|
||||
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
|
||||
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
|
||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
|
||||
github.com/fatih/color v1.14.1 // indirect
|
||||
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
||||
github.com/fvbommel/sortorder v1.0.1 // indirect
|
||||
github.com/fvbommel/sortorder v1.0.2 // indirect
|
||||
github.com/ghodss/yaml v1.0.0 // indirect
|
||||
github.com/go-errors/errors v1.0.1 // indirect
|
||||
github.com/go-kit/log v0.1.0 // indirect
|
||||
github.com/go-logfmt/logfmt v0.5.0 // indirect
|
||||
github.com/go-logr/logr v0.4.0 // indirect
|
||||
github.com/go-logr/logr v1.2.3 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.19.5 // indirect
|
||||
github.com/go-openapi/swag v0.19.14 // indirect
|
||||
github.com/go-openapi/swag v0.19.15 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
github.com/goccy/go-json v0.4.8 // indirect
|
||||
github.com/gofrs/flock v0.8.0 // indirect
|
||||
github.com/gogo/googleapis v1.4.1 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
@@ -118,23 +147,35 @@ require (
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.1.1 // indirect
|
||||
github.com/googleapis/gnostic v0.5.5 // indirect
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/gosuri/uitable v0.0.4 // indirect
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-hclog v1.5.0 // indirect
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
|
||||
github.com/hashicorp/go-version v1.3.0 // indirect
|
||||
github.com/hashicorp/go-version v1.6.0 // indirect
|
||||
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/hashicorp/serf v0.10.1 // indirect
|
||||
github.com/huandu/xstrings v1.3.2 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/imdario/mergo v0.3.13 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||
github.com/jaguilar/vt100 v0.0.0-20150826170717-2703a27b14ea // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/jmoiron/sqlx v1.3.1 // indirect
|
||||
github.com/jonboulle/clockwork v0.2.2 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/klauspost/compress v1.15.9 // indirect
|
||||
github.com/kr/pretty v0.3.0 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
|
||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
|
||||
github.com/lestrrat-go/backoff/v2 v2.0.7 // indirect
|
||||
github.com/lestrrat-go/blackmagic v1.0.0 // indirect
|
||||
github.com/lestrrat-go/httpcc v1.0.0 // indirect
|
||||
@@ -143,28 +184,41 @@ require (
|
||||
github.com/lestrrat-go/option v1.0.0 // indirect
|
||||
github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f // indirect
|
||||
github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 // indirect
|
||||
github.com/lib/pq v1.10.0 // indirect
|
||||
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
|
||||
github.com/mailru/easyjson v0.7.6 // indirect
|
||||
github.com/magiconair/properties v1.8.5 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.12 // indirect
|
||||
github.com/mattn/go-shellwords v1.0.12 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
||||
github.com/miekg/dns v1.1.43 // indirect
|
||||
github.com/miekg/pkcs11 v1.1.1 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/go-wordwrap v1.0.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/moby/buildkit v0.10.4 // indirect
|
||||
github.com/moby/locker v1.0.1 // indirect
|
||||
github.com/moby/spdystream v0.2.0 // indirect
|
||||
github.com/moby/term v0.0.0-20210610120745-9d4ed1856297 // indirect
|
||||
github.com/moby/sys/mount v0.3.0 // indirect
|
||||
github.com/moby/sys/mountinfo v0.6.0 // indirect
|
||||
github.com/moby/sys/signal v0.7.0 // indirect
|
||||
github.com/moby/sys/symlink v0.2.0 // indirect
|
||||
github.com/moby/term v0.0.0-20221128092401-c43b287e0e0f // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/natefinch/lumberjack v2.0.0+incompatible // indirect
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.1 // indirect
|
||||
github.com/opencontainers/runc v1.0.2 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0-rc2 // indirect
|
||||
github.com/opencontainers/runc v1.1.3 // indirect
|
||||
github.com/openshift/api v0.0.0-20200713203337-b2494ecb17dd // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
github.com/pelletier/go-toml v1.9.4 // indirect
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_golang v1.12.2 // indirect
|
||||
@@ -172,23 +226,37 @@ require (
|
||||
github.com/prometheus/common v0.32.1 // indirect
|
||||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
github.com/prometheus/statsd_exporter v0.21.0 // indirect
|
||||
github.com/russross/blackfriday v1.5.2 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.6.1 // indirect
|
||||
github.com/rubenv/sql-migrate v0.0.0-20210614095031-55d5740dbbcc // indirect
|
||||
github.com/russross/blackfriday v1.6.0 // indirect
|
||||
github.com/sanathkr/go-yaml v0.0.0-20170819195128-ed9d249f429b // indirect
|
||||
github.com/shopspring/decimal v1.2.0 // indirect
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
github.com/spf13/afero v1.2.2 // indirect
|
||||
github.com/spf13/cast v1.3.1 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/theupdateframework/notary v0.7.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/tonistiigi/fsutil v0.0.0-20220930225714-4638ad635be5 // indirect
|
||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
|
||||
github.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca // indirect
|
||||
github.com/yl2chen/cidranger v1.0.2 // indirect
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v0.7.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v0.12.0 // indirect
|
||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
|
||||
go.uber.org/multierr v1.7.0 // indirect
|
||||
go.uber.org/zap v1.21.0 // indirect
|
||||
golang.org/x/crypto v0.11.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
|
||||
golang.org/x/net v0.12.0 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
|
||||
golang.org/x/oauth2 v0.6.0 // indirect
|
||||
golang.org/x/sync v0.2.0 // indirect
|
||||
golang.org/x/sys v0.10.0 // indirect
|
||||
golang.org/x/term v0.10.0 // indirect
|
||||
@@ -197,21 +265,22 @@ require (
|
||||
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
|
||||
gomodules.xyz/jsonpatch/v3 v3.0.1 // indirect
|
||||
gomodules.xyz/orderedmap v0.1.0 // indirect
|
||||
google.golang.org/api v0.59.0 // indirect
|
||||
google.golang.org/api v0.61.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20211020151524-b7c3a969101a // indirect
|
||||
google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect
|
||||
gopkg.in/gcfg.v1 v1.2.3 // indirect
|
||||
gopkg.in/gorp.v1 v1.7.2 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/ini.v1 v1.66.2 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.22.2 // indirect
|
||||
k8s.io/component-base v0.22.2 // indirect
|
||||
k8s.io/klog/v2 v2.10.0 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20211020163157-7327e2aaee2b // indirect
|
||||
k8s.io/apiserver v0.22.5 // indirect
|
||||
k8s.io/component-base v0.22.5 // indirect
|
||||
k8s.io/klog/v2 v2.60.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c // indirect
|
||||
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect
|
||||
oras.land/oras-go v0.4.0 // indirect
|
||||
sigs.k8s.io/gateway-api v0.4.0 // indirect
|
||||
sigs.k8s.io/kustomize/api v0.8.11 // indirect
|
||||
sigs.k8s.io/kustomize/kyaml v0.11.0 // indirect
|
||||
@@ -230,3 +299,60 @@ replace istio.io/pkg => ./external/pkg
|
||||
replace istio.io/client-go => ./external/client-go
|
||||
|
||||
replace istio.io/istio => ./external/istio
|
||||
|
||||
require (
|
||||
github.com/evanphx/json-patch/v5 v5.6.0
|
||||
github.com/google/yamlfmt v0.10.0
|
||||
github.com/kylelemons/godebug v1.1.0
|
||||
github.com/tidwall/gjson v1.17.0
|
||||
helm.sh/helm/v3 v3.7.1
|
||||
k8s.io/apiextensions-apiserver v0.25.4
|
||||
knative.dev/networking v0.0.0-20220302134042-e8b2eb995165
|
||||
knative.dev/pkg v0.0.0-20220301181942-2fdd5f232e77
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/Sirupsen/logrus => github.com/sirupsen/logrus v1.9.3
|
||||
github.com/go-logr/logr => github.com/go-logr/logr v0.4.0
|
||||
|
||||
k8s.io/api => k8s.io/api v0.22.2
|
||||
k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.22.2
|
||||
k8s.io/apimachinery => k8s.io/apimachinery v0.22.2
|
||||
k8s.io/cli-runtime => k8s.io/cli-runtime v0.22.2
|
||||
k8s.io/client-go => k8s.io/client-go v0.22.2
|
||||
k8s.io/code-generator => k8s.io/code-generator v0.22.2
|
||||
k8s.io/component-base => k8s.io/component-base v0.22.2
|
||||
k8s.io/component-helpers => k8s.io/component-helpers v0.22.2
|
||||
k8s.io/klog/v2 => k8s.io/klog/v2 v2.10.0
|
||||
k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e
|
||||
k8s.io/kubectl => k8s.io/kubectl v0.22.2
|
||||
k8s.io/metrics => k8s.io/metrics v0.22.2
|
||||
|
||||
k8s.io/utils => k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect
|
||||
sigs.k8s.io/kustomize/api => sigs.k8s.io/kustomize/api v0.8.11 // indirect
|
||||
sigs.k8s.io/kustomize/kyaml => sigs.k8s.io/kustomize/kyaml v0.11.0 // indirect
|
||||
)
|
||||
|
||||
// for pkg/cmd/hgctl/docker/compose.go
|
||||
// TODO(WeixinX): Wait for the dependency library to upgrade, such as github.com/go-logr/logr from v0.4.0 to v1.2+
|
||||
// replace (
|
||||
// github.com/compose-spec/compose-go => github.com/compose-spec/compose-go v1.8.2
|
||||
// github.com/cucumber/godog => github.com/laurazard/godog v0.0.0-20220922095256-4c4b17abdae7
|
||||
// github.com/docker/buildx => github.com/docker/buildx v0.9.1
|
||||
// github.com/docker/cli => github.com/docker/cli v20.10.3-0.20221013132413-1d6c6e2367e2+incompatible
|
||||
// github.com/docker/compose/v2 => github.com/docker/compose/v2 v2.15.1
|
||||
// github.com/docker/docker => github.com/moby/moby v20.10.3-0.20221021173910-5aac513617f0+incompatible
|
||||
// github.com/moby/buildkit => github.com/moby/buildkit v0.10.1-0.20220816171719-55ba9d14360a
|
||||
// )
|
||||
|
||||
replace (
|
||||
github.com/compose-spec/compose-go => github.com/compose-spec/compose-go v1.0.8
|
||||
github.com/docker/buildx => github.com/docker/buildx v0.5.2-0.20210422185057-908a856079fc
|
||||
github.com/docker/cli => github.com/docker/cli v20.10.7+incompatible
|
||||
github.com/docker/compose/v2 => github.com/docker/compose/v2 v2.2.0
|
||||
github.com/docker/docker => github.com/docker/docker v20.10.3+incompatible
|
||||
github.com/jaguilar/vt100 => github.com/tonistiigi/vt100 v0.0.0-20190402012908-ad4c4a574305
|
||||
github.com/moby/buildkit => github.com/moby/buildkit v0.8.2-0.20210401015549-df49b648c8bf
|
||||
github.com/tonistiigi/fsutil => github.com/tonistiigi/fsutil v0.0.0-20201103201449-0834f99b7b85
|
||||
sigs.k8s.io/gateway-api => github.com/johnlanni/gateway-api v0.0.0-20231031082632-72137664e7c7
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
apiVersion: v2
|
||||
appVersion: 1.1.1
|
||||
appVersion: 1.3.3
|
||||
description: Helm chart for deploying higress gateways
|
||||
icon: https://higress.io/img/higress_logo_small.png
|
||||
home: http://higress.io/
|
||||
@@ -10,4 +10,4 @@ name: higress-core
|
||||
sources:
|
||||
- http://github.com/alibaba/higress
|
||||
type: application
|
||||
version: 1.1.1
|
||||
version: 1.3.3
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
configSources:
|
||||
- address: "xds://127.0.0.1:15051"
|
||||
{{- if .Values.global.enableIstioAPI }}
|
||||
{{- if or .Values.global.enableIstioAPI .Values.global.enableGatewayAPI }}
|
||||
- address: "k8s://"
|
||||
{{- end }}
|
||||
|
||||
|
||||
@@ -117,3 +117,10 @@ rules:
|
||||
- apiGroups: ["config.istio.io", "security.istio.io", "networking.istio.io", "authentication.istio.io", "rbac.istio.io", "telemetry.istio.io", "extensions.istio.io"]
|
||||
verbs: ["get", "watch", "list"]
|
||||
resources: ["*"]
|
||||
# knative KIngress configuration
|
||||
- apiGroups: ["networking.internal.knative.dev"]
|
||||
verbs: ["get","list","watch"]
|
||||
resources: ["ingresses"]
|
||||
- apiGroups: ["networking.internal.knative.dev"]
|
||||
resources: ["ingresses/status"]
|
||||
verbs: ["get","patch","update"]
|
||||
@@ -2,6 +2,7 @@ apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "controller.name" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "controller.labels" . | nindent 4 }}
|
||||
spec:
|
||||
@@ -30,11 +31,7 @@ spec:
|
||||
containers:
|
||||
{{- if not .Values.global.enableHigressIstio }}
|
||||
- name: discovery
|
||||
{{- if contains "/" .Values.pilot.image }}
|
||||
image: "{{ .Values.pilot.image }}"
|
||||
{{- else }}
|
||||
image: "{{ .Values.pilot.hub | default .Values.global.hub }}/{{ .Values.pilot.image | default "pilot" }}:{{ .Values.pilot.tag | default .Values.global.tag }}"
|
||||
{{- end }}
|
||||
image: "{{ .Values.pilot.hub | default .Values.global.hub }}/{{ .Values.pilot.image | default "pilot" }}:{{ .Values.pilot.tag | default .Chart.AppVersion }}"
|
||||
{{- if .Values.global.imagePullPolicy }}
|
||||
imagePullPolicy: {{ .Values.global.imagePullPolicy }}
|
||||
{{- end }}
|
||||
@@ -73,8 +70,14 @@ spec:
|
||||
periodSeconds: 3
|
||||
timeoutSeconds: 5
|
||||
env:
|
||||
- name: ENBALE_SCOPED_RDS
|
||||
value: "{{ .Values.global.enableSRDS }}"
|
||||
- name: ON_DEMAND_RDS
|
||||
value: "{{ .Values.global.onDemandRDS }}"
|
||||
- name: HOST_RDS_MERGE_SUBSET
|
||||
value: "{{ .Values.global.hostRDSMergeSubset }}"
|
||||
- name: PILOT_FILTER_GATEWAY_CLUSTER_CONFIG
|
||||
value: "true"
|
||||
value: "{{ .Values.global.onlyPushRouteCluster }}"
|
||||
- name: HIGRESS_CONTROLLER_SVC
|
||||
value: "127.0.0.1"
|
||||
- name: HIGRESS_CONTROLLER_PORT
|
||||
@@ -126,10 +129,19 @@ spec:
|
||||
value: "{{ .Values.global.istiod.enableAnalysis }}"
|
||||
- name: CLUSTER_ID
|
||||
value: "{{ $.Values.global.multiCluster.clusterName | default `Kubernetes` }}"
|
||||
# HIGRESS_ENABLE_ISTIO_API is only used to restart the controller pod after the config change
|
||||
{{- if .Values.global.enableIstioAPI }}
|
||||
- name: HIGRESS_ENABLE_ISTIO_API
|
||||
value: "true"
|
||||
{{- end }}
|
||||
{{- if .Values.global.enableGatewayAPI }}
|
||||
- name: PILOT_ENABLE_GATEWAY_API
|
||||
value: "true"
|
||||
- name: PILOT_ENABLE_GATEWAY_API_STATUS
|
||||
value: "true"
|
||||
- name: PILOT_ENABLE_GATEWAY_API_DEPLOYMENT_CONTROLLER
|
||||
value: "false"
|
||||
{{- end }}
|
||||
{{- if not .Values.global.enableHigressIstio }}
|
||||
- name: CUSTOM_CA_CERT_NAME
|
||||
value: "higress-ca-root-cert"
|
||||
@@ -174,7 +186,7 @@ spec:
|
||||
- name: {{ .Chart.Name }}
|
||||
securityContext:
|
||||
{{- toYaml .Values.controller.securityContext | nindent 12 }}
|
||||
image: "{{ .Values.hub }}/{{ .Values.controller.image }}:{{ .Values.controller.tag | default .Chart.AppVersion }}"
|
||||
image: "{{ .Values.controller.hub | default .Values.global.hub }}/{{ .Values.controller.image | default "higress" }}:{{ .Values.controller.tag | default .Chart.AppVersion }}"
|
||||
args:
|
||||
- "serve"
|
||||
- --gatewaySelectorKey=higress
|
||||
|
||||
@@ -2,6 +2,7 @@ apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "controller.name" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "controller.labels" . | nindent 4 }}
|
||||
spec:
|
||||
|
||||
@@ -68,7 +68,7 @@ spec:
|
||||
{{- end }}
|
||||
containers:
|
||||
- name: higress-gateway
|
||||
image: "{{ .Values.hub }}/{{ .Values.gateway.image }}:{{ .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:
|
||||
- proxy
|
||||
- router
|
||||
@@ -134,6 +134,8 @@ spec:
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: spec.serviceAccountName
|
||||
- name: PILOT_XDS_SEND_TIMEOUT
|
||||
value: 60s
|
||||
- name: PROXY_XDS_VIA_AGENT
|
||||
value: "true"
|
||||
- name: ENABLE_INGRESS_GATEWAY_SDS
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
revision: ""
|
||||
global:
|
||||
enableSRDS: false
|
||||
onDemandRDS: true
|
||||
hostRDSMergeSubset: true
|
||||
onlyPushRouteCluster: true
|
||||
# IngressClass filters which ingress resources the higress controller watches.
|
||||
# The default ingress class is higress.
|
||||
# There are some special cases for special ingress class.
|
||||
@@ -17,6 +21,7 @@ global:
|
||||
local: false # When deploying to a local cluster (e.g.: kind cluster), set this to true.
|
||||
kind: false # Deprecated. Please use "global.local" instead. Will be removed later.
|
||||
enableIstioAPI: false
|
||||
enableGatewayAPI: false
|
||||
# Deprecated
|
||||
enableHigressIstio: false
|
||||
# Used to locate istiod.
|
||||
@@ -44,8 +49,6 @@ global:
|
||||
# Releases are published to docker hub under 'istio' project.
|
||||
# Dev builds from prow are on gcr.io
|
||||
hub: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress
|
||||
# Default tag for Istio images.
|
||||
tag: 1.1.1
|
||||
|
||||
# Specify image pull policy if default behavior isn't desired.
|
||||
# Default behavior: latest images will be Always else IfNotPresent.
|
||||
@@ -369,7 +372,9 @@ gateway:
|
||||
name: "higress-gateway"
|
||||
replicas: 2
|
||||
image: gateway
|
||||
tag: "1.1.1"
|
||||
|
||||
hub: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress
|
||||
tag: ""
|
||||
# revision declares which revision this gateway is a part of
|
||||
revision: ""
|
||||
|
||||
@@ -457,7 +462,9 @@ controller:
|
||||
name: "higress-controller"
|
||||
replicas: 1
|
||||
image: higress
|
||||
tag: "1.1.1"
|
||||
|
||||
hub: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress
|
||||
tag: ""
|
||||
env: {}
|
||||
|
||||
labels: {}
|
||||
@@ -547,7 +554,7 @@ pilot:
|
||||
rollingMaxUnavailable: 25%
|
||||
|
||||
hub: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress
|
||||
tag: 1.1.1
|
||||
tag: ""
|
||||
|
||||
# Can be a full hub/image:tag
|
||||
image: pilot
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
dependencies:
|
||||
- name: higress-core
|
||||
repository: file://../core
|
||||
version: 1.1.1
|
||||
version: 1.3.3
|
||||
- name: higress-console
|
||||
repository: https://higress.io/helm-charts/
|
||||
version: 1.1.1
|
||||
digest: sha256:dd74a69c4031fa3e7798233602b44f0da3f657cbb40c61754298fbc877be2ae6
|
||||
generated: "2023-08-10T10:54:46.8520756+08:00"
|
||||
version: 1.3.1
|
||||
digest: sha256:585666df5da403450c5e586a71388bc0d029354b1100b20a50616f56711fa171
|
||||
generated: "2024-01-08T21:40:10.446936+08:00"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
apiVersion: v2
|
||||
appVersion: 1.1.1
|
||||
appVersion: 1.3.3
|
||||
description: Helm chart for deploying Higress gateways
|
||||
icon: https://higress.io/img/higress_logo_small.png
|
||||
home: http://higress.io/
|
||||
@@ -12,9 +12,9 @@ sources:
|
||||
dependencies:
|
||||
- name: higress-core
|
||||
repository: "file://../core"
|
||||
version: 1.1.1
|
||||
version: 1.3.3
|
||||
- name: higress-console
|
||||
repository: "https://higress.io/helm-charts/"
|
||||
version: 1.1.1
|
||||
version: 1.3.1
|
||||
type: application
|
||||
version: 1.1.1
|
||||
version: 1.3.3
|
||||
|
||||
@@ -40,6 +40,7 @@ The command removes all the Kubernetes components associated with the chart and
|
||||
| global.disableAlpnH2 | Whether to disable HTTP/2 in ALPN | true |
|
||||
| global.enableStatus | If `true`, Higress Controller will update the `status` field of Ingress resources.<br />When migrating from Nginx Ingress, in order to avoid `status` field of Ingress objects being overwritten, this parameter needs to be set to false, so Higress won't write the entry IP to the `status` field of the corresponding Ingress object. | true |
|
||||
| global.enableIstioAPI | If `true`, Higress Controller will monitor istio resources as well | false |
|
||||
| global.enableGatewayAPI | If `true`, Higress Controller will monitor Gateway API resources as well | false |
|
||||
| global.istioNamespace | The namespace istio is installed to | istio-system |
|
||||
| **Core Paramters** | | |
|
||||
| higress-core.gateway.replicas | Number of Higress Gateway pods | 2 |
|
||||
|
||||
14
istio/1.12/patches/istio/20230922-gateway-class.patch
Normal file
14
istio/1.12/patches/istio/20230922-gateway-class.patch
Normal file
@@ -0,0 +1,14 @@
|
||||
diff -Naur istio/pilot/pkg/config/kube/gateway/conversion.go istio_new/pilot/pkg/config/kube/gateway/conversion.go
|
||||
--- istio/pilot/pkg/config/kube/gateway/conversion.go 2023-09-22 11:06:50.400535200 +0800
|
||||
+++ istio_new/pilot/pkg/config/kube/gateway/conversion.go 2023-09-22 11:07:52.954982700 +0800
|
||||
@@ -37,8 +37,8 @@
|
||||
)
|
||||
|
||||
const (
|
||||
- DefaultClassName = "istio"
|
||||
- ControllerName = "istio.io/gateway-controller"
|
||||
+ DefaultClassName = "higress"
|
||||
+ ControllerName = "higress.io/gateway-controller"
|
||||
)
|
||||
|
||||
// KubernetesResources stores all inputs to our conversion
|
||||
71
istio/1.12/patches/istio/20230925-gateway-httproute.patch
Normal file
71
istio/1.12/patches/istio/20230925-gateway-httproute.patch
Normal file
@@ -0,0 +1,71 @@
|
||||
diff -Naur istio/pilot/pkg/config/kube/gateway/conversion.go istio-new/pilot/pkg/config/kube/gateway/conversion.go
|
||||
--- istio/pilot/pkg/config/kube/gateway/conversion.go 2023-09-25 17:26:32.000000000 +0800
|
||||
+++ istio-new/pilot/pkg/config/kube/gateway/conversion.go 2023-09-25 17:25:27.000000000 +0800
|
||||
@@ -656,6 +656,16 @@
|
||||
Port: &istio.PortSelector{Number: uint32(*to.Port)},
|
||||
}, nil
|
||||
}
|
||||
+ if equal((*string)(to.Group), "networking.higress.io") && nilOrEqual((*string)(to.Kind), "Service") {
|
||||
+ var port *istio.PortSelector
|
||||
+ if to.Port != nil {
|
||||
+ port = &istio.PortSelector{Number: uint32(*to.Port)}
|
||||
+ }
|
||||
+ return &istio.Destination{
|
||||
+ Host: string(to.Name),
|
||||
+ Port: port,
|
||||
+ }, nil
|
||||
+ }
|
||||
return nil, &ConfigError{
|
||||
Reason: InvalidDestination,
|
||||
Message: fmt.Sprintf("referencing unsupported backendRef: group %q kind %q", emptyIfNil((*string)(to.Group)), emptyIfNil((*string)(to.Kind))),
|
||||
@@ -912,7 +922,7 @@
|
||||
ObservedGeneration: obj.Generation,
|
||||
LastTransitionTime: metav1.Now(),
|
||||
Reason: string(k8s.GatewayClassConditionStatusAccepted),
|
||||
- Message: "Handled by Istio controller",
|
||||
+ Message: "Handled by Higress controller",
|
||||
})
|
||||
return gcs
|
||||
})
|
||||
@@ -1371,6 +1381,10 @@
|
||||
return d
|
||||
}
|
||||
|
||||
+func equal(have *string, expected string) bool {
|
||||
+ return have != nil && *have == expected
|
||||
+}
|
||||
+
|
||||
func nilOrEqual(have *string, expected string) bool {
|
||||
return have == nil || *have == expected
|
||||
}
|
||||
diff -Naur istio/pilot/pkg/leaderelection/leaderelection.go istio-new/pilot/pkg/leaderelection/leaderelection.go
|
||||
--- istio/pilot/pkg/leaderelection/leaderelection.go 2023-09-25 17:26:31.000000000 +0800
|
||||
+++ istio-new/pilot/pkg/leaderelection/leaderelection.go 2023-09-25 14:59:39.000000000 +0800
|
||||
@@ -35,20 +35,20 @@
|
||||
|
||||
// Various locks used throughout the code
|
||||
const (
|
||||
- NamespaceController = "istio-namespace-controller-election"
|
||||
- ServiceExportController = "istio-serviceexport-controller-election"
|
||||
+ NamespaceController = "higress-namespace-controller-election"
|
||||
+ ServiceExportController = "higress-serviceexport-controller-election"
|
||||
// This holds the legacy name to not conflict with older control plane deployments which are just
|
||||
// doing the ingress syncing.
|
||||
- IngressController = "istio-leader"
|
||||
+ IngressController = "higress-leader"
|
||||
// GatewayStatusController controls the status of gateway.networking.k8s.io objects. For the v1alpha1
|
||||
// this was formally "istio-gateway-leader"; because they are a different API group we need a different
|
||||
// election to ensure we do not only handle one or the other.
|
||||
- GatewayStatusController = "istio-gateway-status-leader"
|
||||
+ GatewayStatusController = "higress-gateway-status-leader"
|
||||
// GatewayDeploymentController controls the Deployment/Service generation from Gateways. This is
|
||||
// separate from GatewayStatusController to allow running in a separate process (for low priv).
|
||||
- GatewayDeploymentController = "istio-gateway-deployment-leader"
|
||||
- StatusController = "istio-status-leader"
|
||||
- AnalyzeController = "istio-analyze-leader"
|
||||
+ GatewayDeploymentController = "higress-gateway-deployment-leader"
|
||||
+ StatusController = "higress-status-leader"
|
||||
+ AnalyzeController = "higress-analyze-leader"
|
||||
)
|
||||
|
||||
var ClusterScopedNamespaceController = NamespaceController
|
||||
90
istio/1.12/patches/istio/20231008-gateway-fallback.patch
Normal file
90
istio/1.12/patches/istio/20231008-gateway-fallback.patch
Normal file
@@ -0,0 +1,90 @@
|
||||
diff -Naur istio/pilot/pkg/config/kube/gateway/conversion.go istio-new/pilot/pkg/config/kube/gateway/conversion.go
|
||||
--- istio/pilot/pkg/config/kube/gateway/conversion.go 2023-10-08 19:54:47.000000000 +0800
|
||||
+++ istio-new/pilot/pkg/config/kube/gateway/conversion.go 2023-09-27 16:10:42.000000000 +0800
|
||||
@@ -18,6 +18,7 @@
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
+ "strconv"
|
||||
"strings"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
@@ -176,7 +177,9 @@
|
||||
hosts := hostnameToStringList(route.Hostnames)
|
||||
for _, r := range route.Rules {
|
||||
// TODO: implement rewrite, timeout, mirror, corspolicy, retries
|
||||
- vs := &istio.HTTPRoute{}
|
||||
+ vs := &istio.HTTPRoute{
|
||||
+ Name: obj.Name,
|
||||
+ }
|
||||
for _, match := range r.Matches {
|
||||
uri, err := createURIMatch(match)
|
||||
if err != nil {
|
||||
@@ -246,7 +249,9 @@
|
||||
}}
|
||||
}
|
||||
|
||||
- route, err := buildHTTPDestination(r.BackendRefs, obj.Namespace, domain, zero)
|
||||
+ fallbackCluster := obj.Annotations["higress.io/fallback-service"]
|
||||
+
|
||||
+ route, err := buildHTTPDestination(r.BackendRefs, obj.Namespace, domain, zero, fallbackCluster)
|
||||
if err != nil {
|
||||
reportError(err)
|
||||
return nil
|
||||
@@ -581,11 +586,33 @@
|
||||
return r
|
||||
}
|
||||
|
||||
-func buildHTTPDestination(forwardTo []k8s.HTTPBackendRef, ns string, domain string, totalZero bool) ([]*istio.HTTPRouteDestination, *ConfigError) {
|
||||
+func buildHTTPDestination(forwardTo []k8s.HTTPBackendRef, ns string, domain string, totalZero bool, fallbackCluster string) ([]*istio.HTTPRouteDestination, *ConfigError) {
|
||||
if forwardTo == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
+ var fallbackDest *istio.Destination
|
||||
+ if fallbackCluster != "" {
|
||||
+ var port uint64
|
||||
+ host := fallbackCluster
|
||||
+ colon := strings.LastIndex(fallbackCluster, ":")
|
||||
+ if colon != -1 {
|
||||
+ var err error
|
||||
+ port, err = strconv.ParseUint(fallbackCluster[colon+1:], 10, 32)
|
||||
+ if err == nil && port > 0 && port < 65536 {
|
||||
+ host = fallbackCluster[:colon]
|
||||
+ }
|
||||
+ }
|
||||
+ fallbackDest = &istio.Destination{
|
||||
+ Host: host,
|
||||
+ }
|
||||
+ if port > 0 {
|
||||
+ fallbackDest.Port = &istio.PortSelector{
|
||||
+ Number: uint32(port),
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
weights := []int{}
|
||||
action := []k8s.HTTPBackendRef{}
|
||||
for i, w := range forwardTo {
|
||||
@@ -612,6 +639,9 @@
|
||||
Destination: dst,
|
||||
Weight: int32(weights[i]),
|
||||
}
|
||||
+ if fallbackDest != nil {
|
||||
+ rd.FallbackClusters = append(rd.FallbackClusters, fallbackDest)
|
||||
+ }
|
||||
for _, filter := range fwd.Filters {
|
||||
switch filter.Type {
|
||||
case k8s.HTTPRouteFilterRequestHeaderModifier:
|
||||
diff -Naur istio/pilot/pkg/networking/core/v1alpha3/route/route.go istio-new/pilot/pkg/networking/core/v1alpha3/route/route.go
|
||||
--- istio/pilot/pkg/networking/core/v1alpha3/route/route.go 2023-10-08 19:54:46.000000000 +0800
|
||||
+++ istio-new/pilot/pkg/networking/core/v1alpha3/route/route.go 2023-09-27 16:18:16.000000000 +0800
|
||||
@@ -669,7 +669,7 @@
|
||||
}
|
||||
var singleClusterConfig *fallback.ClusterFallbackConfig
|
||||
var weightedClusterConfig *fallback.ClusterFallbackConfig
|
||||
- isSupportFallback := supportFallback(node)
|
||||
+ isSupportFallback := true
|
||||
// Added by ingress
|
||||
if len(in.Route) == 1 {
|
||||
route := in.Route[0]
|
||||
13
istio/1.12/patches/istio/20231024-cds-add-fallback.patch
Normal file
13
istio/1.12/patches/istio/20231024-cds-add-fallback.patch
Normal file
@@ -0,0 +1,13 @@
|
||||
diff -Naur istio/pilot/pkg/model/push_context.go istio-new/pilot/pkg/model/push_context.go
|
||||
--- istio/pilot/pkg/model/push_context.go 2023-10-24 10:55:51.000000000 +0800
|
||||
+++ istio-new/pilot/pkg/model/push_context.go 2023-10-20 17:00:06.000000000 +0800
|
||||
@@ -704,6 +704,9 @@
|
||||
if r.Destination != nil {
|
||||
out = append(out, r.Destination.Host)
|
||||
}
|
||||
+ for _, d := range r.FallbackClusters {
|
||||
+ out = append(out, d.Host)
|
||||
+ }
|
||||
}
|
||||
if h.Mirror != nil {
|
||||
out = append(out, h.Mirror.Host)
|
||||
2202
istio/1.12/patches/istio/20231031-gatewayapi-v1beta1.patch
Normal file
2202
istio/1.12/patches/istio/20231031-gatewayapi-v1beta1.patch
Normal file
File diff suppressed because it is too large
Load Diff
377
istio/1.12/patches/istio/20231103-gatewayapi-sort.patch
Normal file
377
istio/1.12/patches/istio/20231103-gatewayapi-sort.patch
Normal file
@@ -0,0 +1,377 @@
|
||||
diff -Naur istio/pilot/pkg/config/kube/gateway/conversion.go istio-new/pilot/pkg/config/kube/gateway/conversion.go
|
||||
--- istio/pilot/pkg/config/kube/gateway/conversion.go 2023-11-03 17:18:56.000000000 +0800
|
||||
+++ istio-new/pilot/pkg/config/kube/gateway/conversion.go 2023-11-03 17:14:50.000000000 +0800
|
||||
@@ -151,15 +151,113 @@
|
||||
}
|
||||
}
|
||||
|
||||
+ // for gateway routes, build one VS per gateway+host
|
||||
+ gatewayRoutes := make(map[string]map[string]*config.Config)
|
||||
+
|
||||
for _, obj := range r.HTTPRoute {
|
||||
- if vsConfig := buildHTTPVirtualServices(obj, gatewayMap, r.Domain); vsConfig != nil {
|
||||
+ buildHTTPVirtualServices(r, obj, gatewayMap, gatewayRoutes, r.Domain)
|
||||
+ }
|
||||
+ for _, vsByHost := range gatewayRoutes {
|
||||
+ for _, vsConfig := range vsByHost {
|
||||
result = append(result, *vsConfig)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
-func buildHTTPVirtualServices(obj config.Config, gateways map[parentKey]map[gatewayapiV1beta1.SectionName]*parentInfo, domain string) *config.Config {
|
||||
+// getURIRank ranks a URI match type. Exact > Prefix > Regex
|
||||
+func getURIRank(match *istio.HTTPMatchRequest) int {
|
||||
+ if match.Uri == nil {
|
||||
+ return -1
|
||||
+ }
|
||||
+ switch match.Uri.MatchType.(type) {
|
||||
+ case *istio.StringMatch_Exact:
|
||||
+ return 3
|
||||
+ case *istio.StringMatch_Prefix:
|
||||
+ return 2
|
||||
+ case *istio.StringMatch_Regex:
|
||||
+ // TODO optimize in new verison envoy
|
||||
+ if strings.HasSuffix(match.Uri.GetRegex(), prefixMatchRegex) &&
|
||||
+ !strings.ContainsAny(strings.TrimSuffix(match.Uri.GetRegex(), prefixMatchRegex), `\.+*?()|[]{}^$`) {
|
||||
+ return 2
|
||||
+ }
|
||||
+ return 1
|
||||
+ }
|
||||
+ // should not happen
|
||||
+ return -1
|
||||
+}
|
||||
+
|
||||
+func getURILength(match *istio.HTTPMatchRequest) int {
|
||||
+ if match.Uri == nil {
|
||||
+ return 0
|
||||
+ }
|
||||
+ switch match.Uri.MatchType.(type) {
|
||||
+ case *istio.StringMatch_Prefix:
|
||||
+ return len(match.Uri.GetPrefix())
|
||||
+ case *istio.StringMatch_Exact:
|
||||
+ return len(match.Uri.GetExact())
|
||||
+ case *istio.StringMatch_Regex:
|
||||
+ return len(match.Uri.GetRegex())
|
||||
+ }
|
||||
+ // should not happen
|
||||
+ return -1
|
||||
+}
|
||||
+
|
||||
+// sortHTTPRoutes sorts generated vs routes to meet gateway-api requirements
|
||||
+// see https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.HTTPRouteRule
|
||||
+func sortHTTPRoutes(routes []*istio.HTTPRoute) {
|
||||
+ sort.SliceStable(routes, func(i, j int) bool {
|
||||
+ if len(routes[i].Match) == 0 {
|
||||
+ return false
|
||||
+ } else if len(routes[j].Match) == 0 {
|
||||
+ return true
|
||||
+ }
|
||||
+ // Only look at match[0], we always generate only one match
|
||||
+ m1, m2 := routes[i].Match[0], routes[j].Match[0]
|
||||
+ r1, r2 := getURIRank(m1), getURIRank(m2)
|
||||
+ len1, len2 := getURILength(m1), getURILength(m2)
|
||||
+ switch {
|
||||
+ // 1: Exact/Prefix/Regex
|
||||
+ case r1 != r2:
|
||||
+ return r1 > r2
|
||||
+ case len1 != len2:
|
||||
+ return len1 > len2
|
||||
+ // 2: method math
|
||||
+ case (m1.Method == nil) != (m2.Method == nil):
|
||||
+ return m1.Method != nil
|
||||
+ // 3: number of header matches
|
||||
+ case len(m1.Headers) != len(m2.Headers):
|
||||
+ return len(m1.Headers) > len(m2.Headers)
|
||||
+ // 4: number of query matches
|
||||
+ default:
|
||||
+ return len(m1.QueryParams) > len(m2.QueryParams)
|
||||
+ }
|
||||
+ })
|
||||
+}
|
||||
+
|
||||
+func routeMeta(obj config.Config) map[string]string {
|
||||
+ m := parentMeta(obj, nil)
|
||||
+ m[constants.InternalRouteSemantics] = constants.RouteSemanticsGateway
|
||||
+ return m
|
||||
+}
|
||||
+
|
||||
+func filteredReferences(parents []routeParentReference) []routeParentReference {
|
||||
+ ret := make([]routeParentReference, 0, len(parents))
|
||||
+ for _, p := range parents {
|
||||
+ if p.DeniedReason != nil {
|
||||
+ // We should filter this out
|
||||
+ continue
|
||||
+ }
|
||||
+ ret = append(ret, p)
|
||||
+ }
|
||||
+ // To ensure deterministic order, sort them
|
||||
+ sort.Slice(ret, func(i, j int) bool {
|
||||
+ return ret[i].InternalName < ret[j].InternalName
|
||||
+ })
|
||||
+ return ret
|
||||
+}
|
||||
+
|
||||
+func buildHTTPVirtualServices(ctx *KubernetesResources, obj config.Config, gateways map[parentKey]map[gatewayapiV1beta1.SectionName]*parentInfo, gatewayRoutes map[string]map[string]*config.Config, domain string) {
|
||||
route := obj.Spec.(*gatewayapiV1beta1.HTTPRouteSpec)
|
||||
|
||||
parentRefs := extractParentReferenceInfo(gateways, route.ParentRefs, route.Hostnames, gvk.HTTPRoute, obj.Namespace)
|
||||
@@ -172,10 +270,7 @@
|
||||
})
|
||||
}
|
||||
|
||||
- name := fmt.Sprintf("%s-%s", obj.Name, constants.KubernetesGatewayName)
|
||||
-
|
||||
httproutes := []*istio.HTTPRoute{}
|
||||
- hosts := hostnameToStringList(route.Hostnames)
|
||||
for _, r := range route.Rules {
|
||||
// TODO: implement rewrite, timeout, mirror, corspolicy, retries
|
||||
vs := &istio.HTTPRoute{
|
||||
@@ -185,22 +280,22 @@
|
||||
uri, err := createURIMatch(match)
|
||||
if err != nil {
|
||||
reportError(err)
|
||||
- return nil
|
||||
+ return
|
||||
}
|
||||
headers, err := createHeadersMatch(match)
|
||||
if err != nil {
|
||||
reportError(err)
|
||||
- return nil
|
||||
+ return
|
||||
}
|
||||
qp, err := createQueryParamsMatch(match)
|
||||
if err != nil {
|
||||
reportError(err)
|
||||
- return nil
|
||||
+ return
|
||||
}
|
||||
method, err := createMethodMatch(match)
|
||||
if err != nil {
|
||||
reportError(err)
|
||||
- return nil
|
||||
+ return
|
||||
}
|
||||
vs.Match = append(vs.Match, &istio.HTTPMatchRequest{
|
||||
Uri: uri,
|
||||
@@ -219,7 +314,7 @@
|
||||
mirror, err := createMirrorFilter(filter.RequestMirror, obj.Namespace, domain)
|
||||
if err != nil {
|
||||
reportError(err)
|
||||
- return nil
|
||||
+ return
|
||||
}
|
||||
vs.Mirror = mirror
|
||||
default:
|
||||
@@ -227,7 +322,7 @@
|
||||
Reason: InvalidFilter,
|
||||
Message: fmt.Sprintf("unsupported filter type %q", filter.Type),
|
||||
})
|
||||
- return nil
|
||||
+ return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,33 +350,65 @@
|
||||
route, err := buildHTTPDestination(r.BackendRefs, obj.Namespace, domain, zero, fallbackCluster)
|
||||
if err != nil {
|
||||
reportError(err)
|
||||
- return nil
|
||||
+ return
|
||||
}
|
||||
vs.Route = route
|
||||
|
||||
httproutes = append(httproutes, vs)
|
||||
}
|
||||
reportError(nil)
|
||||
- gatewayNames := referencesToInternalNames(parentRefs)
|
||||
- if len(gatewayNames) == 0 {
|
||||
- return nil
|
||||
+
|
||||
+ count := 0
|
||||
+ for _, parent := range filteredReferences(parentRefs) {
|
||||
+ // for gateway routes, build one VS per gateway+host
|
||||
+ routeMap := gatewayRoutes
|
||||
+ routeKey := parent.InternalName
|
||||
+ vsHosts := hostnameToStringList(route.Hostnames)
|
||||
+ routes := httproutes
|
||||
+ if len(routes) == 0 {
|
||||
+ continue
|
||||
+ }
|
||||
+ if _, f := routeMap[routeKey]; !f {
|
||||
+ routeMap[routeKey] = make(map[string]*config.Config)
|
||||
+ }
|
||||
+
|
||||
+ // Create one VS per hostname with a single hostname.
|
||||
+ // This ensures we can treat each hostname independently, as the spec requires
|
||||
+ for _, h := range vsHosts {
|
||||
+ if cfg := routeMap[routeKey][h]; cfg != nil {
|
||||
+ // merge http routes
|
||||
+ vs := cfg.Spec.(*istio.VirtualService)
|
||||
+ vs.Http = append(vs.Http, routes...)
|
||||
+ // append parents
|
||||
+ cfg.Annotations[constants.InternalParentNames] = fmt.Sprintf("%s,%s/%s.%s",
|
||||
+ cfg.Annotations[constants.InternalParentNames], obj.GroupVersionKind.Kind, obj.Name, obj.Namespace)
|
||||
+ } else {
|
||||
+ name := fmt.Sprintf("%s-%d-%s", obj.Name, count, constants.KubernetesGatewayName)
|
||||
+ routeMap[routeKey][h] = &config.Config{
|
||||
+ Meta: config.Meta{
|
||||
+ CreationTimestamp: obj.CreationTimestamp,
|
||||
+ GroupVersionKind: gvk.VirtualService,
|
||||
+ Name: name,
|
||||
+ Annotations: routeMeta(obj),
|
||||
+ Namespace: obj.Namespace,
|
||||
+ Domain: ctx.Domain,
|
||||
+ },
|
||||
+ Spec: &istio.VirtualService{
|
||||
+ Hosts: []string{h},
|
||||
+ Gateways: []string{parent.InternalName},
|
||||
+ Http: routes,
|
||||
+ },
|
||||
+ }
|
||||
+ count++
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
- vsConfig := config.Config{
|
||||
- Meta: config.Meta{
|
||||
- CreationTimestamp: obj.CreationTimestamp,
|
||||
- GroupVersionKind: gvk.VirtualService,
|
||||
- Name: name,
|
||||
- Annotations: parentMeta(obj, nil),
|
||||
- Namespace: obj.Namespace,
|
||||
- Domain: domain,
|
||||
- },
|
||||
- Spec: &istio.VirtualService{
|
||||
- Hosts: hosts,
|
||||
- Gateways: gatewayNames,
|
||||
- Http: httproutes,
|
||||
- },
|
||||
+ for _, vsByHost := range gatewayRoutes {
|
||||
+ for _, cfg := range vsByHost {
|
||||
+ vs := cfg.Spec.(*istio.VirtualService)
|
||||
+ sortHTTPRoutes(vs.Http)
|
||||
+ }
|
||||
}
|
||||
- return &vsConfig
|
||||
}
|
||||
|
||||
func parentMeta(obj config.Config, sectionName *gatewayapiV1beta1.SectionName) map[string]string {
|
||||
@@ -1155,9 +1282,11 @@
|
||||
}
|
||||
gs.Addresses = make([]gatewayapiV1beta1.GatewayAddress, 0, len(addressesToReport))
|
||||
for _, addr := range addressesToReport {
|
||||
+ addrPairs := strings.Split(addr, ":")
|
||||
gs.Addresses = append(gs.Addresses, gatewayapiV1beta1.GatewayAddress{
|
||||
- Type: &addrType,
|
||||
- Value: addr,
|
||||
+ Type: &addrType,
|
||||
+ // strip the port
|
||||
+ Value: addrPairs[0],
|
||||
})
|
||||
}
|
||||
return gs
|
||||
diff -Naur istio/pilot/pkg/model/push_context.go istio-new/pilot/pkg/model/push_context.go
|
||||
--- istio/pilot/pkg/model/push_context.go 2023-11-03 17:18:56.000000000 +0800
|
||||
+++ istio-new/pilot/pkg/model/push_context.go 2023-11-03 17:05:47.000000000 +0800
|
||||
@@ -841,7 +841,19 @@
|
||||
func (ps *PushContext) VirtualServicesForGateway(proxy *Proxy, gateway string) []config.Config {
|
||||
res := ps.virtualServiceIndex.privateByNamespaceAndGateway[proxy.ConfigNamespace][gateway]
|
||||
res = append(res, ps.virtualServiceIndex.exportedToNamespaceByGateway[proxy.ConfigNamespace][gateway]...)
|
||||
- res = append(res, ps.virtualServiceIndex.publicByGateway[gateway]...)
|
||||
+
|
||||
+ // Favor same-namespace Gateway routes, to give the "consumer override" preference.
|
||||
+ // We do 2 iterations here to avoid extra allocations.
|
||||
+ for _, vs := range ps.virtualServiceIndex.publicByGateway[gateway] {
|
||||
+ if UseGatewaySemantics(vs) && vs.Namespace == proxy.ConfigNamespace {
|
||||
+ res = append(res, vs)
|
||||
+ }
|
||||
+ }
|
||||
+ for _, vs := range ps.virtualServiceIndex.publicByGateway[gateway] {
|
||||
+ if !(UseGatewaySemantics(vs) && vs.Namespace == proxy.ConfigNamespace) {
|
||||
+ res = append(res, vs)
|
||||
+ }
|
||||
+ }
|
||||
return res
|
||||
}
|
||||
|
||||
diff -Naur istio/pilot/pkg/model/virtualservice.go istio-new/pilot/pkg/model/virtualservice.go
|
||||
--- istio/pilot/pkg/model/virtualservice.go 2023-11-03 17:18:55.000000000 +0800
|
||||
+++ istio-new/pilot/pkg/model/virtualservice.go 2023-11-03 15:19:08.000000000 +0800
|
||||
@@ -76,6 +76,11 @@
|
||||
}
|
||||
|
||||
func resolveVirtualServiceShortnames(rule *networking.VirtualService, meta config.Meta) {
|
||||
+ // Kubernetes Gateway API semantics support shortnames
|
||||
+ // if UseGatewaySemantics(config.Config{Meta: meta}) {
|
||||
+ // return
|
||||
+ // }
|
||||
+
|
||||
// resolve top level hosts
|
||||
for i, h := range rule.Hosts {
|
||||
rule.Hosts[i] = string(ResolveShortnameToFQDN(h, meta))
|
||||
@@ -524,3 +529,10 @@
|
||||
}
|
||||
return false
|
||||
}
|
||||
+
|
||||
+// UseGatewaySemantics determines which logic we should use for VirtualService
|
||||
+// This allows gateway-api and VS to both be represented by VirtualService, but have different
|
||||
+// semantics.
|
||||
+func UseGatewaySemantics(cfg config.Config) bool {
|
||||
+ return cfg.Annotations[constants.InternalRouteSemantics] == constants.RouteSemanticsGateway
|
||||
+}
|
||||
diff -Naur istio/pilot/pkg/networking/core/v1alpha3/route/route.go istio-new/pilot/pkg/networking/core/v1alpha3/route/route.go
|
||||
--- istio/pilot/pkg/networking/core/v1alpha3/route/route.go 2023-11-03 17:18:56.000000000 +0800
|
||||
+++ istio-new/pilot/pkg/networking/core/v1alpha3/route/route.go 2023-11-03 17:05:55.000000000 +0800
|
||||
@@ -408,7 +408,6 @@
|
||||
break
|
||||
}
|
||||
}
|
||||
-
|
||||
if len(out) == 0 {
|
||||
return nil, fmt.Errorf("no routes matched")
|
||||
}
|
||||
@@ -493,6 +492,14 @@
|
||||
},
|
||||
}
|
||||
|
||||
+ if model.UseGatewaySemantics(virtualService) {
|
||||
+ if uri, isPrefixReplace := cutPrefix(redirect.Uri, "%PREFIX()%"); isPrefixReplace {
|
||||
+ action.Redirect.PathRewriteSpecifier = &route.RedirectAction_PrefixRewrite{
|
||||
+ PrefixRewrite: uri,
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
if redirect.Scheme != "" {
|
||||
action.Redirect.SchemeRewriteSpecifier = &route.RedirectAction_SchemeRedirect{SchemeRedirect: redirect.Scheme}
|
||||
}
|
||||
@@ -1616,3 +1623,10 @@
|
||||
isSupport = curVersion.GreaterThan(notSupportFallback)
|
||||
return
|
||||
}
|
||||
+
|
||||
+func cutPrefix(s, prefix string) (after string, found bool) {
|
||||
+ if !strings.HasPrefix(s, prefix) {
|
||||
+ return s, false
|
||||
+ }
|
||||
+ return s[len(prefix):], true
|
||||
+}
|
||||
diff -Naur istio/pkg/config/constants/constants.go istio-new/pkg/config/constants/constants.go
|
||||
--- istio/pkg/config/constants/constants.go 2023-11-03 17:18:54.000000000 +0800
|
||||
+++ istio-new/pkg/config/constants/constants.go 2023-11-03 14:29:27.000000000 +0800
|
||||
@@ -15,6 +15,12 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
+ InternalParentNames = "internal.istio.io/parents"
|
||||
+
|
||||
+ InternalRouteSemantics = "internal.istio.io/route-semantics"
|
||||
+
|
||||
+ RouteSemanticsGateway = "gateway"
|
||||
+
|
||||
// UnspecifiedIP constant for empty IP address
|
||||
UnspecifiedIP = "0.0.0.0"
|
||||
|
||||
50
istio/1.12/patches/istio/20231104-gatewayapi-catchall.patch
Normal file
50
istio/1.12/patches/istio/20231104-gatewayapi-catchall.patch
Normal file
@@ -0,0 +1,50 @@
|
||||
diff -Naur istio/pilot/pkg/config/kube/gateway/conversion.go istio-new/pilot/pkg/config/kube/gateway/conversion.go
|
||||
--- istio/pilot/pkg/config/kube/gateway/conversion.go 2023-11-03 20:09:38.000000000 +0800
|
||||
+++ istio-new/pilot/pkg/config/kube/gateway/conversion.go 2023-11-03 20:02:26.000000000 +0800
|
||||
@@ -165,6 +165,34 @@
|
||||
return result
|
||||
}
|
||||
|
||||
+// isCatchAll returns true if HTTPMatchRequest is a catchall match otherwise
|
||||
+// false. Note - this may not be exactly "catch all" as we don't know the full
|
||||
+// class of possible inputs As such, this is used only for optimization.
|
||||
+func isCatchAllMatch(m *istio.HTTPMatchRequest) bool {
|
||||
+ catchall := false
|
||||
+ if m.Uri != nil {
|
||||
+ switch m := m.Uri.MatchType.(type) {
|
||||
+ case *istio.StringMatch_Prefix:
|
||||
+ catchall = m.Prefix == "/"
|
||||
+ case *istio.StringMatch_Regex:
|
||||
+ catchall = m.Regex == "*"
|
||||
+ }
|
||||
+ }
|
||||
+ // A Match is catch all if and only if it has no match set
|
||||
+ // and URI has a prefix / or regex *.
|
||||
+ return catchall &&
|
||||
+ len(m.Headers) == 0 &&
|
||||
+ len(m.QueryParams) == 0 &&
|
||||
+ len(m.SourceLabels) == 0 &&
|
||||
+ len(m.WithoutHeaders) == 0 &&
|
||||
+ len(m.Gateways) == 0 &&
|
||||
+ m.Method == nil &&
|
||||
+ m.Scheme == nil &&
|
||||
+ m.Port == 0 &&
|
||||
+ m.Authority == nil &&
|
||||
+ m.SourceNamespace == ""
|
||||
+}
|
||||
+
|
||||
// getURIRank ranks a URI match type. Exact > Prefix > Regex
|
||||
func getURIRank(match *istio.HTTPMatchRequest) int {
|
||||
if match.Uri == nil {
|
||||
@@ -212,6 +240,11 @@
|
||||
} else if len(routes[j].Match) == 0 {
|
||||
return true
|
||||
}
|
||||
+ if isCatchAllMatch(routes[i].Match[0]) {
|
||||
+ return false
|
||||
+ } else if isCatchAllMatch(routes[j].Match[0]) {
|
||||
+ return true
|
||||
+ }
|
||||
// Only look at match[0], we always generate only one match
|
||||
m1, m2 := routes[i].Match[0], routes[j].Match[0]
|
||||
r1, r2 := getURIRank(m1), getURIRank(m2)
|
||||
62
istio/1.12/patches/istio/20231115-optimize-xds-push.patch
Normal file
62
istio/1.12/patches/istio/20231115-optimize-xds-push.patch
Normal file
@@ -0,0 +1,62 @@
|
||||
diff -Naur istio/pilot/pkg/xds/ads.go istio-new/pilot/pkg/xds/ads.go
|
||||
--- istio/pilot/pkg/xds/ads.go 2023-11-15 20:25:18.000000000 +0800
|
||||
+++ istio-new/pilot/pkg/xds/ads.go 2023-11-15 20:24:20.000000000 +0800
|
||||
@@ -318,6 +318,27 @@
|
||||
<-con.initialized
|
||||
|
||||
for {
|
||||
+ // Go select{} statements are not ordered; the same channel can be chosen many times.
|
||||
+ // For requests, these are higher priority (client may be blocked on startup until these are done)
|
||||
+ // and often very cheap to handle (simple ACK), so we check it first.
|
||||
+ select {
|
||||
+ case req, ok := <-con.reqChan:
|
||||
+ if ok {
|
||||
+ if err := s.processRequest(req, con); err != nil {
|
||||
+ return err
|
||||
+ }
|
||||
+ } else {
|
||||
+ // Remote side closed connection or error processing the request.
|
||||
+ return <-con.errorChan
|
||||
+ }
|
||||
+ case <-con.stop:
|
||||
+ return nil
|
||||
+ default:
|
||||
+ }
|
||||
+ // If there wasn't already a request, poll for requests and pushes. Note: if we have a huge
|
||||
+ // amount of incoming requests, we may still send some pushes, as we do not `continue` above;
|
||||
+ // however, requests will be handled ~2x as much as pushes. This ensures a wave of requests
|
||||
+ // cannot completely starve pushes. However, this scenario is unlikely.
|
||||
select {
|
||||
case req, ok := <-con.reqChan:
|
||||
if ok {
|
||||
diff -Naur istio/pilot/pkg/xds/delta.go istio-new/pilot/pkg/xds/delta.go
|
||||
--- istio/pilot/pkg/xds/delta.go 2023-11-15 20:25:18.000000000 +0800
|
||||
+++ istio-new/pilot/pkg/xds/delta.go 2023-11-15 20:24:44.000000000 +0800
|
||||
@@ -102,6 +102,27 @@
|
||||
<-con.initialized
|
||||
|
||||
for {
|
||||
+ // Go select{} statements are not ordered; the same channel can be chosen many times.
|
||||
+ // For requests, these are higher priority (client may be blocked on startup until these are done)
|
||||
+ // and often very cheap to handle (simple ACK), so we check it first.
|
||||
+ select {
|
||||
+ case req, ok := <-con.deltaReqChan:
|
||||
+ if ok {
|
||||
+ if err := s.processDeltaRequest(req, con); err != nil {
|
||||
+ return err
|
||||
+ }
|
||||
+ } else {
|
||||
+ // Remote side closed connection or error processing the request.
|
||||
+ return <-con.errorChan
|
||||
+ }
|
||||
+ case <-con.stop:
|
||||
+ return nil
|
||||
+ default:
|
||||
+ }
|
||||
+ // If there wasn't already a request, poll for requests and pushes. Note: if we have a huge
|
||||
+ // amount of incoming requests, we may still send some pushes, as we do not `continue` above;
|
||||
+ // however, requests will be handled ~2x as much as pushes. This ensures a wave of requests
|
||||
+ // cannot completely starve pushes. However, this scenario is unlikely.
|
||||
select {
|
||||
case req, ok := <-con.deltaReqChan:
|
||||
if ok {
|
||||
633
istio/1.12/patches/istio/20240104-enhance-srds.patch
Normal file
633
istio/1.12/patches/istio/20240104-enhance-srds.patch
Normal file
@@ -0,0 +1,633 @@
|
||||
diff -Naur istio/pilot/pkg/features/pilot.go istio-new/pilot/pkg/features/pilot.go
|
||||
--- istio/pilot/pkg/features/pilot.go 2024-01-05 17:58:08.000000000 +0800
|
||||
+++ istio-new/pilot/pkg/features/pilot.go 2024-01-04 21:20:00.000000000 +0800
|
||||
@@ -569,6 +569,12 @@
|
||||
// Added by ingress
|
||||
CustomCACertConfigMapName = env.RegisterStringVar("CUSTOM_CA_CERT_NAME", "",
|
||||
"Defines the configmap's name of istio's root ca certificate").Get()
|
||||
+ HostRDSMergeSubset = env.RegisterBoolVar("HOST_RDS_MERGE_SUBSET", true,
|
||||
+ "If enabled, if host A is a subset of B, then we merge B's routes into A's hostRDS").Get()
|
||||
+ EnableScopedRDS = env.RegisterBoolVar("ENBALE_SCOPED_RDS", true,
|
||||
+ "If enabled, each host in virtualservice will have an independent RDS, which is used with SRDS").Get()
|
||||
+ OnDemandRDS = env.RegisterBoolVar("ON_DEMAND_RDS", false,
|
||||
+ "If enabled, the on demand filter will be added to the HCM filters").Get()
|
||||
// End added by ingress
|
||||
)
|
||||
|
||||
diff -Naur istio/pilot/pkg/networking/core/configgen.go istio-new/pilot/pkg/networking/core/configgen.go
|
||||
--- istio/pilot/pkg/networking/core/configgen.go 2024-01-05 17:58:02.000000000 +0800
|
||||
+++ istio-new/pilot/pkg/networking/core/configgen.go 2024-01-04 21:20:00.000000000 +0800
|
||||
@@ -17,6 +17,7 @@
|
||||
import (
|
||||
core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
|
||||
listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
|
||||
+ route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
|
||||
discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
|
||||
|
||||
meshconfig "istio.io/api/mesh/v1alpha1"
|
||||
@@ -44,6 +45,10 @@
|
||||
// BuildHTTPRoutes returns the list of HTTP routes for the given proxy. This is the RDS output
|
||||
BuildHTTPRoutes(node *model.Proxy, req *model.PushRequest, routeNames []string) ([]*discovery.Resource, model.XdsLogDetails)
|
||||
|
||||
+ // Added by ingress
|
||||
+ BuildScopedRoutes(node *model.Proxy, push *model.PushContext) []*route.ScopedRouteConfiguration
|
||||
+ // End added by ingress
|
||||
+
|
||||
// BuildNameTable returns list of hostnames and the associated IPs
|
||||
BuildNameTable(node *model.Proxy, push *model.PushContext) *dnsProto.NameTable
|
||||
|
||||
diff -Naur istio/pilot/pkg/networking/core/v1alpha3/gateway.go istio-new/pilot/pkg/networking/core/v1alpha3/gateway.go
|
||||
--- istio/pilot/pkg/networking/core/v1alpha3/gateway.go 2024-01-05 17:58:07.000000000 +0800
|
||||
+++ istio-new/pilot/pkg/networking/core/v1alpha3/gateway.go 2024-01-05 11:19:54.000000000 +0800
|
||||
@@ -41,7 +41,9 @@
|
||||
"istio.io/istio/pilot/pkg/networking/plugin"
|
||||
"istio.io/istio/pilot/pkg/networking/util"
|
||||
authn_model "istio.io/istio/pilot/pkg/security/model"
|
||||
+ "istio.io/istio/pilot/pkg/util/sets"
|
||||
"istio.io/istio/pkg/config"
|
||||
+ "istio.io/istio/pkg/config/constants"
|
||||
"istio.io/istio/pkg/config/gateway"
|
||||
"istio.io/istio/pkg/config/host"
|
||||
"istio.io/istio/pkg/config/protocol"
|
||||
@@ -104,10 +106,15 @@
|
||||
// We can also have QUIC on a given port along with HTTPS/TLS on a given port. It does not
|
||||
// cause port-conflict as they use different transport protocols
|
||||
opts := &buildListenerOpts{
|
||||
- push: builder.push,
|
||||
- proxy: builder.node,
|
||||
- bind: bind,
|
||||
- port: &model.Port{Port: int(port.Number)},
|
||||
+ push: builder.push,
|
||||
+ proxy: builder.node,
|
||||
+ bind: bind,
|
||||
+ port: &model.Port{
|
||||
+ Port: int(port.Number),
|
||||
+ // Added by ingress
|
||||
+ Protocol: protocol.Parse(port.Protocol),
|
||||
+ // End added by ingress
|
||||
+ },
|
||||
bindToPort: true,
|
||||
class: istionetworking.ListenerClassGateway,
|
||||
transport: transport,
|
||||
@@ -340,6 +347,269 @@
|
||||
return nameToServiceMap
|
||||
}
|
||||
|
||||
+// Added by ingress
|
||||
+func (configgen *ConfigGeneratorImpl) BuildScopedRoutes(node *model.Proxy, push *model.PushContext) []*route.ScopedRouteConfiguration {
|
||||
+ if node.MergedGateway == nil {
|
||||
+ log.Warnf("buildScopedRoutes: no gateways for router %v", node.ID)
|
||||
+ return nil
|
||||
+ }
|
||||
+ merged := node.MergedGateway
|
||||
+ var out []*route.ScopedRouteConfiguration
|
||||
+ gatewayVirtualServices := make(map[string][]config.Config)
|
||||
+ serverIterator := func(listenerPort int, mergedServers map[model.ServerPort]*model.MergedServers) sets.Set {
|
||||
+ hostSet := sets.NewSet()
|
||||
+ for port, servers := range mergedServers {
|
||||
+ if port.Number != uint32(listenerPort) {
|
||||
+ continue
|
||||
+ }
|
||||
+ for _, server := range servers.Servers {
|
||||
+ gatewayName := merged.GatewayNameForServer[server]
|
||||
+
|
||||
+ var virtualServices []config.Config
|
||||
+ var exists bool
|
||||
+
|
||||
+ if virtualServices, exists = gatewayVirtualServices[gatewayName]; !exists {
|
||||
+ virtualServices = push.VirtualServicesForGateway(node, gatewayName)
|
||||
+ gatewayVirtualServices[gatewayName] = virtualServices
|
||||
+ }
|
||||
+ for _, virtualService := range virtualServices {
|
||||
+ for _, host := range virtualService.Spec.(*networking.VirtualService).Hosts {
|
||||
+ hostSet.Insert(host)
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ return hostSet
|
||||
+ }
|
||||
+ buildPortHostScopedRoute := func(listenerPort model.ServerPort) {
|
||||
+ p := protocol.Parse(listenerPort.Protocol)
|
||||
+ if !p.IsHTTP() && p != protocol.HTTPS {
|
||||
+ return
|
||||
+ }
|
||||
+ port := strconv.Itoa(int(listenerPort.Number))
|
||||
+ hostSet := serverIterator(int(listenerPort.Number), merged.MergedServers).
|
||||
+ Union(serverIterator(int(listenerPort.Number), merged.MergedQUICTransportServers))
|
||||
+ for host, _ := range hostSet {
|
||||
+ portKey := &route.ScopedRouteConfiguration_Key_Fragment{
|
||||
+ Type: &route.ScopedRouteConfiguration_Key_Fragment_StringKey{
|
||||
+ StringKey: port,
|
||||
+ },
|
||||
+ }
|
||||
+ hostKey := &route.ScopedRouteConfiguration_Key_Fragment{
|
||||
+ Type: &route.ScopedRouteConfiguration_Key_Fragment_StringKey{
|
||||
+ StringKey: host,
|
||||
+ },
|
||||
+ }
|
||||
+ name := strings.Join([]string{port, host}, ".")
|
||||
+ out = append(out, &route.ScopedRouteConfiguration{
|
||||
+ OnDemand: features.OnDemandRDS,
|
||||
+ Name: name,
|
||||
+ RouteConfigurationName: constants.HigressHostRDSNamePrefix + name,
|
||||
+ Key: &route.ScopedRouteConfiguration_Key{
|
||||
+ Fragments: []*route.ScopedRouteConfiguration_Key_Fragment{portKey, hostKey},
|
||||
+ },
|
||||
+ })
|
||||
+ }
|
||||
+ }
|
||||
+ for _, port := range merged.ServerPorts {
|
||||
+ buildPortHostScopedRoute(port)
|
||||
+ }
|
||||
+ return out
|
||||
+}
|
||||
+
|
||||
+type virtualServiceContext struct {
|
||||
+ virtualService config.Config
|
||||
+ server *networking.Server
|
||||
+ gatewayName string
|
||||
+}
|
||||
+
|
||||
+func (configgen *ConfigGeneratorImpl) buildHostRDSConfig(node *model.Proxy, push *model.PushContext,
|
||||
+ routeName string) *route.RouteConfiguration {
|
||||
+ var (
|
||||
+ hostRDSPort string
|
||||
+ hostRDSHost string
|
||||
+ )
|
||||
+ portAndHost := strings.SplitN(strings.TrimPrefix(routeName, constants.HigressHostRDSNamePrefix), ".", 2)
|
||||
+ if len(portAndHost) != 2 {
|
||||
+ log.Errorf("Invalid route %s when using Higress hostRDS", routeName)
|
||||
+ return nil
|
||||
+ }
|
||||
+ hostRDSPort = portAndHost[0]
|
||||
+ hostRDSHost = portAndHost[1]
|
||||
+ merged := node.MergedGateway
|
||||
+ log.Debugf("buildGatewayRoutes: gateways after merging: %v", merged)
|
||||
+ rdsPort, err := strconv.Atoi(hostRDSPort)
|
||||
+ if err != nil {
|
||||
+ log.Errorf("Invalid port %s of route %s when using Higress hostRDS", hostRDSPort, routeName)
|
||||
+ return nil
|
||||
+ }
|
||||
+ listenerPort := uint32(rdsPort)
|
||||
+ globalHTTPFilters := mseingress.ExtractGlobalHTTPFilters(node, push)
|
||||
+
|
||||
+ isH3DiscoveryNeeded := false
|
||||
+
|
||||
+ // When this is true, we add alt-svc header to the response to tell the client
|
||||
+ // that HTTP/3 over QUIC is available on the same port for this host. This is
|
||||
+ // very important for discovering HTTP/3 services
|
||||
+ for port, servers := range merged.MergedQUICTransportServers {
|
||||
+ if port.Number == listenerPort && len(servers.Servers) > 0 {
|
||||
+ isH3DiscoveryNeeded = true
|
||||
+ break
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ gatewayRoutes := make(map[string]map[string][]*route.Route)
|
||||
+ gatewayVirtualServices := make(map[string][]config.Config)
|
||||
+ var selectedVirtualServices []virtualServiceContext
|
||||
+ var vHost *route.VirtualHost
|
||||
+ serverIterator := func(mergedServers map[model.ServerPort]*model.MergedServers) {
|
||||
+ for port, servers := range mergedServers {
|
||||
+ if port.Number != listenerPort {
|
||||
+ continue
|
||||
+ }
|
||||
+ for _, server := range servers.Servers {
|
||||
+ gatewayName := merged.GatewayNameForServer[server]
|
||||
+
|
||||
+ var virtualServices []config.Config
|
||||
+ var exists bool
|
||||
+
|
||||
+ if virtualServices, exists = gatewayVirtualServices[gatewayName]; !exists {
|
||||
+ virtualServices = push.VirtualServicesForGateway(node, gatewayName)
|
||||
+ gatewayVirtualServices[gatewayName] = virtualServices
|
||||
+ }
|
||||
+ for _, virtualService := range virtualServices {
|
||||
+ hostMatch := false
|
||||
+ var selectHost string
|
||||
+ virtualServiceHosts := host.NewNames(virtualService.Spec.(*networking.VirtualService).Hosts)
|
||||
+ for _, hostname := range virtualServiceHosts {
|
||||
+ // exact match
|
||||
+ if hostname == host.Name(hostRDSHost) {
|
||||
+ hostMatch = true
|
||||
+ selectHost = hostRDSHost
|
||||
+ break
|
||||
+ }
|
||||
+ if features.HostRDSMergeSubset {
|
||||
+ // subset match
|
||||
+ if host.Name(hostRDSHost).SubsetOf(hostname) {
|
||||
+ hostMatch = true
|
||||
+ selectHost = string(hostname)
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ if !hostMatch {
|
||||
+ continue
|
||||
+ }
|
||||
+ copiedVS := virtualService.DeepCopy()
|
||||
+ copiedVS.Spec.(*networking.VirtualService).Hosts = []string{selectHost}
|
||||
+ selectedVirtualServices = append(selectedVirtualServices, virtualServiceContext{
|
||||
+ virtualService: copiedVS,
|
||||
+ server: server,
|
||||
+ gatewayName: gatewayName,
|
||||
+ })
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ serverIterator(merged.MergedServers)
|
||||
+ serverIterator(merged.MergedQUICTransportServers)
|
||||
+ // Sort by subset
|
||||
+ // before: ["*.abc.com", "*.com", "www.abc.com"]
|
||||
+ // after: ["www.abc.com", "*.abc.com", "*.com"]
|
||||
+ sort.SliceStable(selectedVirtualServices, func(i, j int) bool {
|
||||
+ return host.Name(selectedVirtualServices[i].virtualService.Spec.(*networking.VirtualService).Hosts[0]).SubsetOf(
|
||||
+ host.Name(selectedVirtualServices[j].virtualService.Spec.(*networking.VirtualService).Hosts[0]))
|
||||
+ })
|
||||
+ port := int(listenerPort)
|
||||
+ for _, ctx := range selectedVirtualServices {
|
||||
+ virtualService := ctx.virtualService
|
||||
+ server := ctx.server
|
||||
+ gatewayName := ctx.gatewayName
|
||||
+ // Make sure we can obtain services which are visible to this virtualService as much as possible.
|
||||
+ nameToServiceMap := buildNameToServiceMapForHTTPRoutes(node, push, virtualService)
|
||||
+
|
||||
+ var routes []*route.Route
|
||||
+ var exists bool
|
||||
+ var err error
|
||||
+ if _, exists = gatewayRoutes[gatewayName]; !exists {
|
||||
+ gatewayRoutes[gatewayName] = make(map[string][]*route.Route)
|
||||
+ }
|
||||
+
|
||||
+ vskey := virtualService.Name + "/" + virtualService.Namespace
|
||||
+
|
||||
+ if routes, exists = gatewayRoutes[gatewayName][vskey]; !exists {
|
||||
+ hashByDestination := istio_route.GetConsistentHashForVirtualService(push, node, virtualService, nameToServiceMap)
|
||||
+ routes, err = istio_route.BuildHTTPRoutesForVirtualServiceWithHTTPFilters(node, virtualService, nameToServiceMap,
|
||||
+ hashByDestination, port, map[string]bool{gatewayName: true}, isH3DiscoveryNeeded, push.Mesh, globalHTTPFilters)
|
||||
+ if err != nil {
|
||||
+ log.Debugf("%s omitting routes for virtual service %v/%v due to error: %v", node.ID, virtualService.Namespace, virtualService.Name, err)
|
||||
+ continue
|
||||
+ }
|
||||
+ gatewayRoutes[gatewayName][vskey] = routes
|
||||
+ }
|
||||
+
|
||||
+ if vHost != nil {
|
||||
+ vHost.Routes = append(vHost.Routes, routes...)
|
||||
+ if server.Tls != nil && server.Tls.HttpsRedirect {
|
||||
+ vHost.RequireTls = route.VirtualHost_ALL
|
||||
+ }
|
||||
+ } else {
|
||||
+ vHost = &route.VirtualHost{
|
||||
+ Name: util.DomainName(hostRDSHost, port),
|
||||
+ Domains: buildGatewayVirtualHostDomains(hostRDSHost, port),
|
||||
+ Routes: routes,
|
||||
+ IncludeRequestAttemptCount: true,
|
||||
+ TypedPerFilterConfig: mseingress.ConstructTypedPerFilterConfigForVHost(globalHTTPFilters, virtualService),
|
||||
+ }
|
||||
+ if server.Tls != nil && server.Tls.HttpsRedirect {
|
||||
+ vHost.RequireTls = route.VirtualHost_ALL
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // check all hostname if is not exist with HttpsRedirect set to true
|
||||
+ // create VirtualHost to redirect
|
||||
+ for _, hostname := range server.Hosts {
|
||||
+ if !server.GetTls().GetHttpsRedirect() {
|
||||
+ continue
|
||||
+ }
|
||||
+ if vHost != nil && host.Name(hostname) == host.Name(hostRDSHost) {
|
||||
+ vHost.RequireTls = route.VirtualHost_ALL
|
||||
+ continue
|
||||
+ }
|
||||
+ vHost = &route.VirtualHost{
|
||||
+ Name: util.DomainName(hostname, port),
|
||||
+ Domains: buildGatewayVirtualHostDomains(hostname, port),
|
||||
+ IncludeRequestAttemptCount: true,
|
||||
+ RequireTls: route.VirtualHost_ALL,
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ }
|
||||
+ var virtualHosts []*route.VirtualHost
|
||||
+ if vHost == nil {
|
||||
+ log.Warnf("constructed http route config for route %s on port %d with no vhosts; Setting up a default 404 vhost", routeName, port)
|
||||
+ virtualHosts = []*route.VirtualHost{{
|
||||
+ Name: util.DomainName("blackhole", port),
|
||||
+ Domains: []string{"*"},
|
||||
+ // Empty route list will cause Envoy to 404 NR any requests
|
||||
+ Routes: []*route.Route{},
|
||||
+ }}
|
||||
+ } else {
|
||||
+ vHost.Routes = istio_route.CombineVHostRoutes(vHost.Routes)
|
||||
+ virtualHosts = append(virtualHosts, vHost)
|
||||
+ }
|
||||
+
|
||||
+ routeCfg := &route.RouteConfiguration{
|
||||
+ // Retain the routeName as its used by EnvoyFilter patching logic
|
||||
+ Name: routeName,
|
||||
+ VirtualHosts: virtualHosts,
|
||||
+ ValidateClusters: proto.BoolFalse,
|
||||
+ }
|
||||
+
|
||||
+ return routeCfg
|
||||
+}
|
||||
+
|
||||
+// End added by ingress
|
||||
+
|
||||
func (configgen *ConfigGeneratorImpl) buildGatewayHTTPRouteConfig(node *model.Proxy, push *model.PushContext,
|
||||
routeName string) *route.RouteConfiguration {
|
||||
if node.MergedGateway == nil {
|
||||
@@ -351,6 +621,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
+ // Added by ingress
|
||||
+ if strings.HasPrefix(routeName, constants.HigressHostRDSNamePrefix) {
|
||||
+ return configgen.buildHostRDSConfig(node, push, routeName)
|
||||
+ }
|
||||
+ // End added by ingress
|
||||
+
|
||||
merged := node.MergedGateway
|
||||
log.Debugf("buildGatewayRoutes: gateways after merging: %v", merged)
|
||||
|
||||
@@ -670,7 +946,9 @@
|
||||
// TLS mode | Mesh-wide SDS | Ingress SDS | Resulting Configuration
|
||||
// SIMPLE/MUTUAL | ENABLED | ENABLED | support SDS at ingress gateway to terminate SSL communication outside the mesh
|
||||
// ISTIO_MUTUAL | ENABLED | DISABLED | support SDS at gateway to terminate workload mTLS, with internal workloads
|
||||
-// | for egress or with another trusted cluster for ingress)
|
||||
+//
|
||||
+// | for egress or with another trusted cluster for ingress)
|
||||
+//
|
||||
// ISTIO_MUTUAL | DISABLED | DISABLED | use file-mounted secret paths to terminate workload mTLS from gateway
|
||||
//
|
||||
// Note that ISTIO_MUTUAL TLS mode and ingressSds should not be used simultaneously on the same ingress gateway.
|
||||
diff -Naur istio/pilot/pkg/networking/core/v1alpha3/listener.go istio-new/pilot/pkg/networking/core/v1alpha3/listener.go
|
||||
--- istio/pilot/pkg/networking/core/v1alpha3/listener.go 2024-01-05 17:58:07.000000000 +0800
|
||||
+++ istio-new/pilot/pkg/networking/core/v1alpha3/listener.go 2024-01-05 17:31:10.000000000 +0800
|
||||
@@ -1279,8 +1279,48 @@
|
||||
|
||||
notimeout := durationpb.New(0 * time.Second)
|
||||
connectionManager.StreamIdleTimeout = notimeout
|
||||
-
|
||||
- if httpOpts.rds != "" {
|
||||
+ // Added by ingress
|
||||
+ enableSRDS := false
|
||||
+ if features.EnableScopedRDS &&
|
||||
+ (listenerOpts.port.Protocol.IsHTTP() || (listenerOpts.port.Protocol == protocol.HTTPS)) {
|
||||
+ enableSRDS = true
|
||||
+ portFragment := &hcm.ScopedRoutes_ScopeKeyBuilder_FragmentBuilder{
|
||||
+ Type: &hcm.ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractor_{
|
||||
+ LocalPortValueExtractor: &hcm.ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_LocalPortValueExtractor{},
|
||||
+ }}
|
||||
+ hostFragment := &hcm.ScopedRoutes_ScopeKeyBuilder_FragmentBuilder{
|
||||
+ Type: &hcm.ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractor_{
|
||||
+ HostValueExtractor: &hcm.ScopedRoutes_ScopeKeyBuilder_FragmentBuilder_HostValueExtractor{},
|
||||
+ }}
|
||||
+ scopedRoutes := &hcm.HttpConnectionManager_ScopedRoutes{
|
||||
+ ScopedRoutes: &hcm.ScopedRoutes{
|
||||
+ Name: constants.DefaultScopedRouteName,
|
||||
+ ScopeKeyBuilder: &hcm.ScopedRoutes_ScopeKeyBuilder{
|
||||
+ Fragments: []*hcm.ScopedRoutes_ScopeKeyBuilder_FragmentBuilder{portFragment, hostFragment},
|
||||
+ },
|
||||
+ RdsConfigSource: &core.ConfigSource{
|
||||
+ ConfigSourceSpecifier: &core.ConfigSource_Ads{
|
||||
+ Ads: &core.AggregatedConfigSource{},
|
||||
+ },
|
||||
+ InitialFetchTimeout: durationpb.New(0),
|
||||
+ ResourceApiVersion: core.ApiVersion_V3,
|
||||
+ },
|
||||
+ ConfigSpecifier: &hcm.ScopedRoutes_ScopedRds{
|
||||
+ ScopedRds: &hcm.ScopedRds{
|
||||
+ ScopedRdsConfigSource: &core.ConfigSource{
|
||||
+ ConfigSourceSpecifier: &core.ConfigSource_Ads{
|
||||
+ Ads: &core.AggregatedConfigSource{},
|
||||
+ },
|
||||
+ InitialFetchTimeout: durationpb.New(0),
|
||||
+ ResourceApiVersion: core.ApiVersion_V3,
|
||||
+ },
|
||||
+ },
|
||||
+ },
|
||||
+ },
|
||||
+ }
|
||||
+ connectionManager.RouteSpecifier = scopedRoutes
|
||||
+ } else if httpOpts.rds != "" {
|
||||
+ // End added by ingress
|
||||
rds := &hcm.HttpConnectionManager_Rds{
|
||||
Rds: &hcm.Rds{
|
||||
ConfigSource: &core.ConfigSource{
|
||||
@@ -1304,8 +1344,15 @@
|
||||
|
||||
filters := make([]*hcm.HttpFilter, len(httpFilters))
|
||||
copy(filters, httpFilters)
|
||||
- // Make sure cors filter always in the first.
|
||||
- filters = append([]*hcm.HttpFilter{xdsfilters.Cors}, filters...)
|
||||
+ // Added by ingress
|
||||
+ // Now only support onDemandRDS when enable SRDS
|
||||
+ if features.OnDemandRDS && enableSRDS {
|
||||
+ filters = append([]*hcm.HttpFilter{xdsfilters.OnDemand, xdsfilters.Cors}, filters...)
|
||||
+ } else {
|
||||
+ // End added by ingress
|
||||
+ // Make sure cors filter always in the first.
|
||||
+ filters = append([]*hcm.HttpFilter{xdsfilters.Cors}, filters...)
|
||||
+ }
|
||||
|
||||
if features.MetadataExchange {
|
||||
filters = append(filters, xdsfilters.HTTPMx)
|
||||
diff -Naur istio/pilot/pkg/xds/ads.go istio-new/pilot/pkg/xds/ads.go
|
||||
--- istio/pilot/pkg/xds/ads.go 2024-01-05 17:58:08.000000000 +0800
|
||||
+++ istio-new/pilot/pkg/xds/ads.go 2024-01-05 17:31:44.000000000 +0800
|
||||
@@ -797,15 +797,18 @@
|
||||
|
||||
// PushOrder defines the order that updates will be pushed in. Any types not listed here will be pushed in random
|
||||
// order after the types listed here
|
||||
-var PushOrder = []string{v3.ClusterType, v3.EndpointType, v3.ListenerType, v3.RouteType, v3.SecretType}
|
||||
+var PushOrder = []string{v3.ClusterType, v3.EndpointType, v3.ListenerType, v3.ScopedRouteType, v3.RouteType, v3.SecretType}
|
||||
|
||||
// KnownOrderedTypeUrls has typeUrls for which we know the order of push.
|
||||
var KnownOrderedTypeUrls = map[string]struct{}{
|
||||
v3.ClusterType: {},
|
||||
v3.EndpointType: {},
|
||||
v3.ListenerType: {},
|
||||
- v3.RouteType: {},
|
||||
- v3.SecretType: {},
|
||||
+ // Added by ingress
|
||||
+ v3.ScopedRouteType: {},
|
||||
+ // End added by ingress
|
||||
+ v3.RouteType: {},
|
||||
+ v3.SecretType: {},
|
||||
}
|
||||
|
||||
// orderWatchedResources orders the resources in accordance with known push order.
|
||||
diff -Naur istio/pilot/pkg/xds/discovery.go istio-new/pilot/pkg/xds/discovery.go
|
||||
--- istio/pilot/pkg/xds/discovery.go 2024-01-05 17:58:07.000000000 +0800
|
||||
+++ istio-new/pilot/pkg/xds/discovery.go 2024-01-04 21:20:00.000000000 +0800
|
||||
@@ -589,6 +589,9 @@
|
||||
s.Generators[v3.ClusterType] = &CdsGenerator{Server: s}
|
||||
s.Generators[v3.ListenerType] = &LdsGenerator{Server: s}
|
||||
s.Generators[v3.RouteType] = &RdsGenerator{Server: s}
|
||||
+ // Added by ingress
|
||||
+ s.Generators[v3.ScopedRouteType] = &SrdsGenerator{Server: s}
|
||||
+ // End added by ingress
|
||||
s.Generators[v3.EndpointType] = edsGen
|
||||
s.Generators[v3.NameTableType] = &NdsGenerator{Server: s}
|
||||
s.Generators[v3.ExtensionConfigurationType] = &EcdsGenerator{Server: s}
|
||||
diff -Naur istio/pilot/pkg/xds/filters/filters.go istio-new/pilot/pkg/xds/filters/filters.go
|
||||
--- istio/pilot/pkg/xds/filters/filters.go 2024-01-05 17:58:03.000000000 +0800
|
||||
+++ istio-new/pilot/pkg/xds/filters/filters.go 2024-01-04 21:20:00.000000000 +0800
|
||||
@@ -21,6 +21,7 @@
|
||||
fault "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/fault/v3"
|
||||
grpcstats "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/grpc_stats/v3"
|
||||
grpcweb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/grpc_web/v3"
|
||||
+ ondemand "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/on_demand/v3"
|
||||
router "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v3"
|
||||
httpwasm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/wasm/v3"
|
||||
httpinspector "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/http_inspector/v3"
|
||||
@@ -54,6 +55,14 @@
|
||||
// Define static filters to be reused across the codebase. This avoids duplicate marshaling/unmarshaling
|
||||
// This should not be used for filters that will be mutated
|
||||
var (
|
||||
+ // Added by ingress
|
||||
+ OnDemand = &hcm.HttpFilter{
|
||||
+ Name: "envoy.filters.http.on_demand.v3.OnDemand",
|
||||
+ ConfigType: &hcm.HttpFilter_TypedConfig{
|
||||
+ TypedConfig: util.MessageToAny(&ondemand.OnDemand{}),
|
||||
+ },
|
||||
+ }
|
||||
+ // End added by ingress
|
||||
Cors = &hcm.HttpFilter{
|
||||
Name: wellknown.CORS,
|
||||
ConfigType: &hcm.HttpFilter_TypedConfig{
|
||||
diff -Naur istio/pilot/pkg/xds/srds.go istio-new/pilot/pkg/xds/srds.go
|
||||
--- istio/pilot/pkg/xds/srds.go 1970-01-01 08:00:00.000000000 +0800
|
||||
+++ istio-new/pilot/pkg/xds/srds.go 2024-01-05 13:45:49.000000000 +0800
|
||||
@@ -0,0 +1,79 @@
|
||||
+// Copyright Istio 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.
|
||||
+
|
||||
+package xds
|
||||
+
|
||||
+import (
|
||||
+ discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
|
||||
+ "istio.io/istio/pilot/pkg/features"
|
||||
+ "istio.io/istio/pilot/pkg/model"
|
||||
+ "istio.io/istio/pilot/pkg/networking/util"
|
||||
+ "istio.io/istio/pkg/config"
|
||||
+ "istio.io/istio/pkg/config/schema/gvk"
|
||||
+)
|
||||
+
|
||||
+type SrdsGenerator struct {
|
||||
+ Server *DiscoveryServer
|
||||
+}
|
||||
+
|
||||
+var _ model.XdsResourceGenerator = &SrdsGenerator{}
|
||||
+
|
||||
+// Map of all configs that do not impact SRDS
|
||||
+var skippedSrdsConfigs = map[config.GroupVersionKind]struct{}{
|
||||
+ gvk.WorkloadEntry: {},
|
||||
+ gvk.WorkloadGroup: {},
|
||||
+ gvk.RequestAuthentication: {},
|
||||
+ gvk.PeerAuthentication: {},
|
||||
+ gvk.Secret: {},
|
||||
+}
|
||||
+
|
||||
+func srdsNeedsPush(req *model.PushRequest) bool {
|
||||
+ if !features.EnableScopedRDS {
|
||||
+ return false
|
||||
+ }
|
||||
+ if req == nil {
|
||||
+ return true
|
||||
+ }
|
||||
+ if !req.Full {
|
||||
+ // SRDS only handles full push
|
||||
+ return false
|
||||
+ }
|
||||
+ // If none set, we will always push
|
||||
+ if len(req.ConfigsUpdated) == 0 {
|
||||
+ return true
|
||||
+ }
|
||||
+ for config := range req.ConfigsUpdated {
|
||||
+ if _, f := skippedSrdsConfigs[config.Kind]; !f {
|
||||
+ return true
|
||||
+ }
|
||||
+ }
|
||||
+ return false
|
||||
+}
|
||||
+
|
||||
+func (s SrdsGenerator) Generate(proxy *model.Proxy, push *model.PushContext, w *model.WatchedResource,
|
||||
+ req *model.PushRequest) (model.Resources, model.XdsLogDetails, error) {
|
||||
+ if !srdsNeedsPush(req) {
|
||||
+ return nil, model.DefaultXdsLogDetails, nil
|
||||
+ }
|
||||
+
|
||||
+ scopedRoutes := s.Server.ConfigGenerator.BuildScopedRoutes(proxy, push)
|
||||
+ resources := model.Resources{}
|
||||
+ for _, sr := range scopedRoutes {
|
||||
+ resources = append(resources, &discovery.Resource{
|
||||
+ Name: sr.Name,
|
||||
+ Resource: util.MessageToAny(sr),
|
||||
+ })
|
||||
+ }
|
||||
+ return resources, model.DefaultXdsLogDetails, nil
|
||||
+}
|
||||
diff -Naur istio/pilot/pkg/xds/v3/model.go istio-new/pilot/pkg/xds/v3/model.go
|
||||
--- istio/pilot/pkg/xds/v3/model.go 2024-01-05 17:58:03.000000000 +0800
|
||||
+++ istio-new/pilot/pkg/xds/v3/model.go 2024-01-05 16:55:49.000000000 +0800
|
||||
@@ -31,6 +31,10 @@
|
||||
SecretType = resource.SecretType
|
||||
ExtensionConfigurationType = resource.ExtensionConfigType
|
||||
|
||||
+ // Added by ingress
|
||||
+ ScopedRouteType = apiTypePrefix + "envoy.config.route.v3.ScopedRouteConfiguration"
|
||||
+ // End added by ingress
|
||||
+
|
||||
NameTableType = apiTypePrefix + "istio.networking.nds.v1.NameTable"
|
||||
HealthInfoType = apiTypePrefix + "istio.v1.HealthInformation"
|
||||
ProxyConfigType = apiTypePrefix + "istio.mesh.v1alpha1.ProxyConfig"
|
||||
@@ -61,6 +65,10 @@
|
||||
return "PCDS"
|
||||
case ExtensionConfigurationType:
|
||||
return "ECDS"
|
||||
+ // Added by ingress
|
||||
+ case ScopedRouteType:
|
||||
+ return "SRDS"
|
||||
+ // End added by ingress
|
||||
default:
|
||||
return typeURL
|
||||
}
|
||||
@@ -87,6 +95,10 @@
|
||||
return "ecds"
|
||||
case BootstrapType:
|
||||
return "bds"
|
||||
+ // Added by ingress
|
||||
+ case ScopedRouteType:
|
||||
+ return "srds"
|
||||
+ // End added by ingress
|
||||
default:
|
||||
return typeURL
|
||||
}
|
||||
diff -Naur istio/pkg/config/constants/constants.go istio-new/pkg/config/constants/constants.go
|
||||
--- istio/pkg/config/constants/constants.go 2024-01-05 17:58:08.000000000 +0800
|
||||
+++ istio-new/pkg/config/constants/constants.go 2024-01-04 21:20:00.000000000 +0800
|
||||
@@ -143,4 +143,9 @@
|
||||
// CertProviderNone does not create any certificates for the control plane. It is assumed that some external
|
||||
// load balancer, such as an Istio Gateway, is terminating the TLS.
|
||||
CertProviderNone = "none"
|
||||
+
|
||||
+ // Added by ingress
|
||||
+ HigressHostRDSNamePrefix = "higress-rds-"
|
||||
+ DefaultScopedRouteName = "scoped-route"
|
||||
+ // End added by ingress
|
||||
)
|
||||
@@ -20,6 +20,10 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/alibaba/higress/pkg/ingress/kube/common"
|
||||
"github.com/alibaba/higress/pkg/ingress/mcp"
|
||||
"github.com/alibaba/higress/pkg/ingress/translation"
|
||||
higresskube "github.com/alibaba/higress/pkg/kube"
|
||||
prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/reflection"
|
||||
@@ -46,11 +50,6 @@ import (
|
||||
"istio.io/pkg/log"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
ingressconfig "github.com/alibaba/higress/pkg/ingress/config"
|
||||
"github.com/alibaba/higress/pkg/ingress/kube/common"
|
||||
"github.com/alibaba/higress/pkg/ingress/mcp"
|
||||
higresskube "github.com/alibaba/higress/pkg/kube"
|
||||
)
|
||||
|
||||
type XdsOptions struct {
|
||||
@@ -225,9 +224,12 @@ func (s *Server) initConfigController() error {
|
||||
if options.ClusterId == "Kubernetes" {
|
||||
options.ClusterId = ""
|
||||
}
|
||||
ingressConfig := ingressconfig.NewIngressConfig(s.kubeClient, s.xdsServer, ns, options.ClusterId)
|
||||
ingressController := ingressConfig.AddLocalCluster(options)
|
||||
|
||||
ingressConfig := translation.NewIngressTranslation(s.kubeClient, s.xdsServer, ns, options.ClusterId)
|
||||
ingressController, kingressController := ingressConfig.AddLocalCluster(options)
|
||||
|
||||
s.configStores = append(s.configStores, ingressConfig)
|
||||
|
||||
// Wrap the config controller with a cache.
|
||||
aggregateConfigController, err := configaggregate.MakeCache(s.configStores)
|
||||
if err != nil {
|
||||
@@ -242,7 +244,7 @@ func (s *Server) initConfigController() error {
|
||||
|
||||
// Defer starting the controller until after the service is created.
|
||||
s.server.RunComponent(func(stop <-chan struct{}) error {
|
||||
if err := ingressConfig.InitializeCluster(ingressController, stop); err != nil {
|
||||
if err := ingressConfig.InitializeCluster(ingressController, kingressController, stop); err != nil {
|
||||
return err
|
||||
}
|
||||
go s.configController.Run(stop)
|
||||
|
||||
338
pkg/cmd/hgctl/code_debug.go
Normal file
338
pkg/cmd/hgctl/code_debug.go
Normal file
@@ -0,0 +1,338 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package hgctl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/helm"
|
||||
"github.com/spf13/cobra"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultIp = "127.0.0.1"
|
||||
DefaultPort = ":15051"
|
||||
)
|
||||
|
||||
func newCodeDebugCmd() *cobra.Command {
|
||||
codeDebugCmd := &cobra.Command{
|
||||
Use: "code-debug",
|
||||
Short: "Start or stop code debug",
|
||||
}
|
||||
|
||||
codeDebugCmd.AddCommand(getStartCodeDebugCmd())
|
||||
codeDebugCmd.AddCommand(getStopCodeDebugCmd())
|
||||
|
||||
return codeDebugCmd
|
||||
}
|
||||
|
||||
func getStartCodeDebugCmd() *cobra.Command {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
fmt.Printf("fail to get user home dir: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
kubeConfigDir := homeDir + "/.kube/config"
|
||||
|
||||
startCodeDebugCmd := &cobra.Command{
|
||||
Use: "start",
|
||||
Aliases: []string{"start"},
|
||||
Short: "Start code debug",
|
||||
Example: "hgctl code-debug start",
|
||||
RunE: func(c *cobra.Command, args []string) error {
|
||||
writer := c.OutOrStdout()
|
||||
|
||||
// wait for user to confirm
|
||||
if !promptCodeDebug(writer, "local grpc address") {
|
||||
return nil
|
||||
}
|
||||
|
||||
// check profile type is local or not
|
||||
fmt.Fprintf(writer, "Checking profile type...\n")
|
||||
profiles, err := getAllProfiles()
|
||||
if err != nil {
|
||||
return fmt.Errorf("fail to get all profiles: %v", err)
|
||||
}
|
||||
if len(profiles) == 0 {
|
||||
fmt.Fprintf(writer, "Higress hasn't been installed yet!\n")
|
||||
return nil
|
||||
}
|
||||
for _, profile := range profiles {
|
||||
if profile.Install != helm.InstallLocalK8s {
|
||||
fmt.Fprintf(writer, "\nHigress needs to be installed locally!\n")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// get kubernetes clientSet
|
||||
fmt.Fprintf(writer, "Getting kubernetes clientset...\n")
|
||||
config, err := clientcmd.BuildConfigFromFlags("", kubeConfigDir)
|
||||
if err != nil {
|
||||
fmt.Fprintf(writer, "fail to build config from kubeconfig: %v", err)
|
||||
return nil
|
||||
}
|
||||
clientSet, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
fmt.Fprintf(writer, "fail to create kubernetes clientset: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// get non-loopback IPv4 address
|
||||
fmt.Fprintf(writer, "Getting non-loopback IPv4 address...\n")
|
||||
ip, err := getNonLoopbackIPv4()
|
||||
if err != nil {
|
||||
fmt.Fprintf(writer, "fail to get non-loopback IPv4 address: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// update the xds address in higress-config ConfigMap
|
||||
// and trigger rollout for higress-controller and higress-gateway deployments
|
||||
fmt.Fprintf(writer, "Updating xds address in higress-config ConfigMap "+
|
||||
"and triggering rollout for higress-controller and higress-gateway deployments...\n")
|
||||
err = updateXdsIpAndRollout(clientSet, ip, DefaultPort)
|
||||
if err != nil {
|
||||
fmt.Fprintf(writer, "fail to update xds address in higress-config ConfigMap: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Fprintf(writer, "Code debug started!\n")
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
startCodeDebugCmd.PersistentFlags().StringVar(&kubeConfigDir, "kubeconfig", kubeConfigDir,
|
||||
"Use a Kubernetes configuration file instead of in-cluster configuration")
|
||||
|
||||
return startCodeDebugCmd
|
||||
}
|
||||
|
||||
func getStopCodeDebugCmd() *cobra.Command {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
fmt.Printf("fail to get user home dir: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
kubeConfigDir := homeDir + "/.kube/config"
|
||||
|
||||
stopCodeDebugCmd := &cobra.Command{
|
||||
Use: "stop",
|
||||
Aliases: []string{"stop"},
|
||||
Short: "Stop code debug",
|
||||
Example: "hgctl code-debug stop",
|
||||
RunE: func(c *cobra.Command, args []string) error {
|
||||
// wait for user to confirm
|
||||
writer := c.OutOrStdout()
|
||||
if !promptCodeDebug(writer, "default grpc address") {
|
||||
return nil
|
||||
}
|
||||
|
||||
// check profile type is local or not
|
||||
fmt.Fprintf(writer, "Checking profile type...\n")
|
||||
profiles, err := getAllProfiles()
|
||||
if err != nil {
|
||||
return fmt.Errorf("fail to get all profiles: %v", err)
|
||||
}
|
||||
if len(profiles) == 0 {
|
||||
fmt.Fprintf(writer, "Higress hasn't been installed yet!\n")
|
||||
return nil
|
||||
}
|
||||
for _, profile := range profiles {
|
||||
if profile.Install != helm.InstallLocalK8s {
|
||||
fmt.Fprintf(writer, "\nHigress needs to be installed locally!\n")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// get kubernetes clientSet
|
||||
fmt.Fprintf(writer, "Getting kubernetes clientset...\n")
|
||||
config, err := clientcmd.BuildConfigFromFlags("", kubeConfigDir)
|
||||
if err != nil {
|
||||
fmt.Fprintf(writer, "fail to build config from kubeconfig: %v", err)
|
||||
return nil
|
||||
}
|
||||
clientSet, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
fmt.Fprintf(writer, "fail to create kubernetes clientset: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// recover the xds address in higress-config ConfigMap
|
||||
// and trigger rollout for higress-controller and higress-gateway deployments
|
||||
fmt.Fprintf(writer, "Recovering xds address in higress-config ConfigMap "+
|
||||
"and triggering rollout for higress-controller and higress-gateway deployments...\n")
|
||||
err = updateXdsIpAndRollout(clientSet, DefaultIp, DefaultPort)
|
||||
if err != nil {
|
||||
fmt.Fprintf(writer, "fail to recover xds address in higress-config ConfigMap: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Fprintf(writer, "Code debug stopped!\n")
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
stopCodeDebugCmd.PersistentFlags().StringVar(&kubeConfigDir, "kubeconfig", kubeConfigDir,
|
||||
"Use a Kubernetes configuration file instead of in-cluster configuration")
|
||||
|
||||
return stopCodeDebugCmd
|
||||
}
|
||||
|
||||
// getNonLoopbackIPv4 returns the first non-loopback IPv4 address of the host.
|
||||
func getNonLoopbackIPv4() (string, error) {
|
||||
// get all network interfaces
|
||||
interfaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// traverse all network interfaces
|
||||
for _, i := range interfaces {
|
||||
// exclude loopback interface and virtual interface
|
||||
if i.Flags&net.FlagLoopback == 0 && i.Flags&net.FlagUp != 0 {
|
||||
// get all addresses of the interface
|
||||
addrs, err := i.Addrs()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// traverse all addresses of the interface
|
||||
for _, addr := range addrs {
|
||||
// check the type of the address is IP address
|
||||
if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
|
||||
// check the IP address is IPv4 address
|
||||
if ipnet.IP.To4() != nil {
|
||||
return ipnet.IP.String(), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("Non-loopback IPv4 address not found")
|
||||
}
|
||||
|
||||
// updateXdsIpAndRollout updates the xds address in higress-config ConfigMap
|
||||
// and triggers rollout for higress-controller and higress-gateway deployments
|
||||
// also can recover the xds address in higress-config ConfigMap
|
||||
func updateXdsIpAndRollout(c *kubernetes.Clientset, ip string, port string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Get higress-config ConfigMap
|
||||
cm, err := c.CoreV1().ConfigMaps("higress-system").Get(ctx, "higress-config", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Update mesh field in higress-config ConfigMap
|
||||
if _, ok := cm.Data["mesh"]; !ok {
|
||||
return fmt.Errorf("mesh not found in configmap higress-config")
|
||||
}
|
||||
mesh := cm.Data["mesh"]
|
||||
newMesh, err := replaceXDSAddress(mesh, ip, port)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cm.Data["mesh"] = newMesh
|
||||
|
||||
// Update higress-config ConfigMap
|
||||
_, err = c.CoreV1().ConfigMaps("higress-system").Update(ctx, cm, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Trigger rollout for higress-controller deployment
|
||||
err = triggerRollout(c, "higress-controller")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Trigger rollout for higress-gateway deployment
|
||||
err = triggerRollout(c, "higress-gateway")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// triggerRollout triggers rollout for the specified deployment
|
||||
func triggerRollout(clientset *kubernetes.Clientset, deploymentName string) error {
|
||||
deploymentsClient := clientset.AppsV1().Deployments("higress-system")
|
||||
|
||||
// Get the deployment
|
||||
deployment, err := deploymentsClient.Get(context.TODO(), deploymentName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Increment the deployment's revision to trigger a rollout
|
||||
deployment.Spec.Template.ObjectMeta.Labels["version"] = time.Now().Format("20060102150405")
|
||||
|
||||
// Update the deployment
|
||||
_, err = deploymentsClient.Update(context.TODO(), deployment, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// replaceXDSAddress replaces the xds address in the config string with new IP and Port
|
||||
func replaceXDSAddress(configString, newIP, newPort string) (string, error) {
|
||||
// define the regular expression to match xds address
|
||||
xdsRegex := regexp.MustCompile(`xds://[0-9.:]+`)
|
||||
|
||||
// find the first match
|
||||
match := xdsRegex.FindString(configString)
|
||||
if match == "" {
|
||||
// if no match, return error
|
||||
return "", fmt.Errorf("no xds address found in config string")
|
||||
}
|
||||
|
||||
// replace xds address with new IP and Port
|
||||
newXDSAddress := fmt.Sprintf("xds://%s%s", newIP, newPort)
|
||||
result := xdsRegex.ReplaceAllString(configString, newXDSAddress)
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// promptCodeDebug prompts user to confirm code debug
|
||||
func promptCodeDebug(writer io.Writer, t string) bool {
|
||||
answer := ""
|
||||
for {
|
||||
fmt.Fprintf(writer, "This will start set xds address to %s in higress-config ConfigMap "+
|
||||
"and trigger rollout for higress-controller and higress-gateway deployments. \nProceed? (y/N)", t)
|
||||
fmt.Scanln(&answer)
|
||||
if answer == "y" {
|
||||
return true
|
||||
}
|
||||
if answer == "N" {
|
||||
fmt.Fprintf(writer, "Cancelled.\n")
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
21
pkg/cmd/hgctl/common.go
Normal file
21
pkg/cmd/hgctl/common.go
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package hgctl
|
||||
|
||||
const (
|
||||
yamlOutput = "yaml"
|
||||
jsonOutput = "json"
|
||||
flagsOutput = "flags"
|
||||
)
|
||||
232
pkg/cmd/hgctl/completion.go
Normal file
232
pkg/cmd/hgctl/completion.go
Normal file
@@ -0,0 +1,232 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package hgctl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const completionDesc = `
|
||||
Generate autocompletion scripts for hgctl for the specified shell.
|
||||
`
|
||||
|
||||
const bashCompDesc = `
|
||||
Generate the autocompletion script for the bash shell.
|
||||
|
||||
This script depends on the 'bash-completion' package.
|
||||
If it is not installed already, you can install it via your OS's package manager.
|
||||
|
||||
To load completions in your current shell session:
|
||||
|
||||
source <(hgctl completion bash)
|
||||
|
||||
To load completions for every new session, execute once:
|
||||
|
||||
#### Linux:
|
||||
|
||||
hgctl completion bash > /etc/bash_completion.d/hgctl
|
||||
|
||||
#### macOS:
|
||||
|
||||
hgctl completion bash > $(brew --prefix)/etc/bash_completion.d/hgctl
|
||||
|
||||
You will need to start a new shell for this setup to take effect.
|
||||
`
|
||||
|
||||
const zshCompDesc = `
|
||||
Generate the autocompletion script for the zsh shell.
|
||||
|
||||
If shell completion is not already enabled in your environment you will need
|
||||
to enable it. You can execute the following once:
|
||||
|
||||
echo "autoload -U compinit; compinit" >> ~/.zshrc
|
||||
|
||||
To load completions in your current shell session:
|
||||
|
||||
source <(hgctl completion zsh); compdef _hgctl hgctl
|
||||
|
||||
To load completions for every new session, execute once:
|
||||
|
||||
#### Linux:
|
||||
|
||||
hgctl completion zsh > "${fpath[1]}/_hgctl"
|
||||
|
||||
#### macOS:
|
||||
|
||||
hgctl completion zsh > $(brew --prefix)/share/zsh/site-functions/_hgctl
|
||||
|
||||
You will need to start a new shell for this setup to take effect.
|
||||
`
|
||||
|
||||
const fishCompDesc = `
|
||||
Generate the autocompletion script for the fish shell.
|
||||
|
||||
To load completions in your current shell session:
|
||||
|
||||
hgctl completion fish | source
|
||||
|
||||
To load completions for every new session, execute once:
|
||||
|
||||
hgctl completion fish > ~/.config/fish/completions/hgctl.fish
|
||||
|
||||
You will need to start a new shell for this setup to take effect.
|
||||
`
|
||||
|
||||
const powershellCompDesc = `
|
||||
Generate the autocompletion script for powershell.
|
||||
|
||||
To load completions in your current shell session:
|
||||
|
||||
hgctl completion powershell | Out-String | Invoke-Expression
|
||||
|
||||
To load completions for every new session, add the output of the above command
|
||||
to your powershell profile.
|
||||
`
|
||||
|
||||
const (
|
||||
noDescFlagName = "no-descriptions"
|
||||
noDescFlagText = "disable completion descriptions"
|
||||
)
|
||||
|
||||
var disableCompDescriptions bool
|
||||
|
||||
// newCompletionCmd creates a new completion command for hgctl
|
||||
func newCompletionCmd(out io.Writer) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "completion",
|
||||
Short: "generate autocompletion scripts for the specified shell",
|
||||
Long: completionDesc,
|
||||
Args: cobra.NoArgs,
|
||||
}
|
||||
|
||||
bash := &cobra.Command{
|
||||
Use: "bash",
|
||||
Short: "generate autocompletion script for bash",
|
||||
Long: bashCompDesc,
|
||||
Args: cobra.NoArgs,
|
||||
ValidArgsFunction: noCompletions,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runCompletionBash(out, cmd)
|
||||
},
|
||||
}
|
||||
bash.Flags().BoolVar(&disableCompDescriptions, noDescFlagName, false, noDescFlagText)
|
||||
|
||||
zsh := &cobra.Command{
|
||||
Use: "zsh",
|
||||
Short: "generate autocompletion script for zsh",
|
||||
Long: zshCompDesc,
|
||||
Args: cobra.NoArgs,
|
||||
ValidArgsFunction: noCompletions,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runCompletionZsh(out, cmd)
|
||||
},
|
||||
}
|
||||
zsh.Flags().BoolVar(&disableCompDescriptions, noDescFlagName, false, noDescFlagText)
|
||||
|
||||
fish := &cobra.Command{
|
||||
Use: "fish",
|
||||
Short: "generate autocompletion script for fish",
|
||||
Long: fishCompDesc,
|
||||
Args: cobra.NoArgs,
|
||||
ValidArgsFunction: noCompletions,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runCompletionFish(out, cmd)
|
||||
},
|
||||
}
|
||||
fish.Flags().BoolVar(&disableCompDescriptions, noDescFlagName, false, noDescFlagText)
|
||||
|
||||
powershell := &cobra.Command{
|
||||
Use: "powershell",
|
||||
Short: "generate autocompletion script for powershell",
|
||||
Long: powershellCompDesc,
|
||||
Args: cobra.NoArgs,
|
||||
ValidArgsFunction: noCompletions,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runCompletionPowershell(out, cmd)
|
||||
},
|
||||
}
|
||||
powershell.Flags().BoolVar(&disableCompDescriptions, noDescFlagName, false, noDescFlagText)
|
||||
|
||||
cmd.AddCommand(bash, zsh, fish, powershell)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runCompletionBash(out io.Writer, cmd *cobra.Command) error {
|
||||
err := cmd.Root().GenBashCompletionV2(out, !disableCompDescriptions)
|
||||
|
||||
// In case the user renamed the hgctl binary, we hook the new binary name to the completion function
|
||||
if binary := filepath.Base(os.Args[0]); binary != "hgctl" {
|
||||
renamedBinaryHook := `
|
||||
# Hook the command used to generate the completion script
|
||||
# to the hgctl completion function to handle the case where
|
||||
# the user renamed the hgctl binary
|
||||
if [[ $(type -t compopt) = "builtin" ]]; then
|
||||
complete -o default -F __start_hgctl %[1]s
|
||||
else
|
||||
complete -o default -o nospace -F __start_hgctl %[1]s
|
||||
fi
|
||||
`
|
||||
fmt.Fprintf(out, renamedBinaryHook, binary)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func runCompletionZsh(out io.Writer, cmd *cobra.Command) error {
|
||||
var err error
|
||||
if disableCompDescriptions {
|
||||
err = cmd.Root().GenZshCompletionNoDesc(out)
|
||||
} else {
|
||||
err = cmd.Root().GenZshCompletion(out)
|
||||
}
|
||||
|
||||
// In case the user renamed the hgctl binary, we hook the new binary name to the completion function
|
||||
if binary := filepath.Base(os.Args[0]); binary != "hgctl" {
|
||||
renamedBinaryHook := `
|
||||
# Hook the command used to generate the completion script
|
||||
# to the hgctl completion function to handle the case where
|
||||
# the user renamed the hgctl binary
|
||||
compdef _hgctl %[1]s
|
||||
`
|
||||
fmt.Fprintf(out, renamedBinaryHook, binary)
|
||||
}
|
||||
|
||||
// Cobra doesn't source zsh completion file, explicitly doing it here
|
||||
fmt.Fprintf(out, "compdef _hgctl hgctl")
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func runCompletionFish(out io.Writer, cmd *cobra.Command) error {
|
||||
return cmd.Root().GenFishCompletion(out, !disableCompDescriptions)
|
||||
}
|
||||
|
||||
func runCompletionPowershell(out io.Writer, cmd *cobra.Command) error {
|
||||
if disableCompDescriptions {
|
||||
return cmd.Root().GenPowerShellCompletion(out)
|
||||
}
|
||||
return cmd.Root().GenPowerShellCompletionWithDesc(out)
|
||||
}
|
||||
|
||||
// Function to disable file completion
|
||||
func noCompletions(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package hgctl
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/kubernetes"
|
||||
"github.com/alibaba/higress/pkg/cmd/options"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
var (
|
||||
output string
|
||||
podName string
|
||||
podNamespace string
|
||||
)
|
||||
|
||||
const (
|
||||
adminPort = 15000
|
||||
containerName = "envoy"
|
||||
)
|
||||
|
||||
func retrieveConfigDump(args []string, includeEds bool) ([]byte, error) {
|
||||
if len(args) != 0 {
|
||||
podName = args[0]
|
||||
}
|
||||
|
||||
if podNamespace == "" {
|
||||
return nil, fmt.Errorf("pod namespace is required")
|
||||
}
|
||||
|
||||
if podName == "" || len(args) == 0 {
|
||||
c, err := kubernetes.NewCLIClient(options.DefaultConfigFlags.ToRawKubeConfigLoader())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build kubernetes client: %w", err)
|
||||
}
|
||||
podList, err := c.PodsForSelector(podNamespace, "app=higress-gateway")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(podList.Items) == 0 {
|
||||
return nil, fmt.Errorf("higress gateway pod is not existed in namespace %s", podNamespace)
|
||||
}
|
||||
|
||||
podName = podList.Items[0].GetName()
|
||||
}
|
||||
|
||||
fw, err := portForwarder(types.NamespacedName{
|
||||
Namespace: podNamespace,
|
||||
Name: podName,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := fw.Start(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer fw.Stop()
|
||||
|
||||
configDump, err := fetchGatewayConfig(fw, includeEds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return configDump, nil
|
||||
}
|
||||
|
||||
func portForwarder(nn types.NamespacedName) (kubernetes.PortForwarder, error) {
|
||||
c, err := kubernetes.NewCLIClient(options.DefaultConfigFlags.ToRawKubeConfigLoader())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("build CLI client fail: %w", err)
|
||||
}
|
||||
|
||||
pod, err := c.Pod(nn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get pod %s fail: %w", nn, err)
|
||||
}
|
||||
if pod.Status.Phase != "Running" {
|
||||
return nil, fmt.Errorf("pod %s is not running", nn)
|
||||
}
|
||||
|
||||
fw, err := kubernetes.NewLocalPortForwarder(c, nn, 0, adminPort)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return fw, nil
|
||||
}
|
||||
|
||||
func formatGatewayConfig(configDump any, output string) ([]byte, error) {
|
||||
out, err := json.MarshalIndent(configDump, "", " ")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if output == "yaml" {
|
||||
out, err = yaml.JSONToYAML(out)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func fetchGatewayConfig(fw kubernetes.PortForwarder, includeEds bool) ([]byte, error) {
|
||||
out, err := configDumpRequest(fw.Address(), includeEds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func configDumpRequest(address string, includeEds bool) ([]byte, error) {
|
||||
url := fmt.Sprintf("http://%s/config_dump", address)
|
||||
if includeEds {
|
||||
url = fmt.Sprintf("%s?include_eds", url)
|
||||
}
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
return io.ReadAll(resp.Body)
|
||||
}
|
||||
@@ -17,6 +17,7 @@ package hgctl
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/alibaba/higress/cmd/hgctl/config"
|
||||
"github.com/spf13/cobra"
|
||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||
)
|
||||
@@ -45,21 +46,20 @@ func bootstrapConfigCmd() *cobra.Command {
|
||||
}
|
||||
|
||||
func runBootstrapConfig(c *cobra.Command, args []string) error {
|
||||
configDump, err := retrieveConfigDump(args, false)
|
||||
if len(args) != 0 {
|
||||
podName = args[0]
|
||||
}
|
||||
envoyConfig, err := config.GetEnvoyConfig(&config.GetEnvoyConfigOptions{
|
||||
PodName: podName,
|
||||
PodNamespace: podNamespace,
|
||||
BindAddress: bindAddress,
|
||||
Output: output,
|
||||
EnvoyConfigType: config.BootstrapEnvoyConfigType,
|
||||
IncludeEds: true,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bootstrap, err := GetXDSResource(BootstrapEnvoyConfigType, configDump)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
out, err := formatGatewayConfig(bootstrap, output)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = fmt.Fprintln(c.OutOrStdout(), string(out))
|
||||
_, err = fmt.Fprintln(c.OutOrStdout(), string(envoyConfig))
|
||||
return err
|
||||
}
|
||||
@@ -11,11 +11,13 @@
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package hgctl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/alibaba/higress/cmd/hgctl/config"
|
||||
"github.com/spf13/cobra"
|
||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||
)
|
||||
@@ -44,21 +46,20 @@ func clusterConfigCmd() *cobra.Command {
|
||||
}
|
||||
|
||||
func runClusterConfig(c *cobra.Command, args []string) error {
|
||||
configDump, err := retrieveConfigDump(args, false)
|
||||
if len(args) != 0 {
|
||||
podName = args[0]
|
||||
}
|
||||
envoyConfig, err := config.GetEnvoyConfig(&config.GetEnvoyConfigOptions{
|
||||
PodName: podName,
|
||||
PodNamespace: podNamespace,
|
||||
BindAddress: bindAddress,
|
||||
Output: output,
|
||||
EnvoyConfigType: config.ClusterEnvoyConfigType,
|
||||
IncludeEds: true,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cluster, err := GetXDSResource(ClusterEnvoyConfigType, configDump)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
out, err := formatGatewayConfig(cluster, output)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = fmt.Fprintln(c.OutOrStdout(), string(out))
|
||||
_, err = fmt.Fprintln(c.OutOrStdout(), string(envoyConfig))
|
||||
return err
|
||||
}
|
||||
@@ -17,11 +17,23 @@ package hgctl
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/alibaba/higress/cmd/hgctl/config"
|
||||
"github.com/alibaba/higress/pkg/cmd/options"
|
||||
"github.com/spf13/cobra"
|
||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||
)
|
||||
|
||||
var (
|
||||
output string
|
||||
podName string
|
||||
podNamespace string
|
||||
)
|
||||
|
||||
const (
|
||||
defaultProxyAdminPort = 15000
|
||||
containerName = "envoy"
|
||||
)
|
||||
|
||||
func newConfigCommand() *cobra.Command {
|
||||
cfgCommand := &cobra.Command{
|
||||
Use: "gateway-config",
|
||||
@@ -69,11 +81,20 @@ func allConfigCmd() *cobra.Command {
|
||||
}
|
||||
|
||||
func runAllConfig(c *cobra.Command, args []string) error {
|
||||
configDump, err := retrieveConfigDump(args, true)
|
||||
if len(args) != 0 {
|
||||
podName = args[0]
|
||||
}
|
||||
envoyConfig, err := config.GetEnvoyConfig(&config.GetEnvoyConfigOptions{
|
||||
PodName: podName,
|
||||
PodNamespace: podNamespace,
|
||||
BindAddress: bindAddress,
|
||||
Output: output,
|
||||
EnvoyConfigType: config.AllEnvoyConfigType,
|
||||
IncludeEds: true,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = fmt.Fprintln(c.OutOrStdout(), string(configDump))
|
||||
_, err = fmt.Fprintln(c.OutOrStdout(), string(envoyConfig))
|
||||
return err
|
||||
}
|
||||
@@ -17,6 +17,7 @@ package hgctl
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/alibaba/higress/cmd/hgctl/config"
|
||||
"github.com/spf13/cobra"
|
||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||
)
|
||||
@@ -45,21 +46,20 @@ func endpointConfigCmd() *cobra.Command {
|
||||
}
|
||||
|
||||
func runEndpointConfig(c *cobra.Command, args []string) error {
|
||||
configDump, err := retrieveConfigDump(args, true)
|
||||
if len(args) != 0 {
|
||||
podName = args[0]
|
||||
}
|
||||
envoyConfig, err := config.GetEnvoyConfig(&config.GetEnvoyConfigOptions{
|
||||
PodName: podName,
|
||||
PodNamespace: podNamespace,
|
||||
BindAddress: bindAddress,
|
||||
Output: output,
|
||||
EnvoyConfigType: config.EndpointEnvoyConfigType,
|
||||
IncludeEds: true,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
endpoint, err := GetXDSResource(EndpointEnvoyConfigType, configDump)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
out, err := formatGatewayConfig(endpoint, output)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = fmt.Fprintln(c.OutOrStdout(), string(out))
|
||||
_, err = fmt.Fprintln(c.OutOrStdout(), string(envoyConfig))
|
||||
return err
|
||||
}
|
||||
@@ -17,6 +17,7 @@ package hgctl
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/alibaba/higress/cmd/hgctl/config"
|
||||
"github.com/spf13/cobra"
|
||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||
)
|
||||
@@ -45,21 +46,20 @@ func listenerConfigCmd() *cobra.Command {
|
||||
}
|
||||
|
||||
func runListenerConfig(c *cobra.Command, args []string) error {
|
||||
configDump, err := retrieveConfigDump(args, false)
|
||||
if len(args) != 0 {
|
||||
podName = args[0]
|
||||
}
|
||||
envoyConfig, err := config.GetEnvoyConfig(&config.GetEnvoyConfigOptions{
|
||||
PodName: podName,
|
||||
PodNamespace: podNamespace,
|
||||
BindAddress: bindAddress,
|
||||
Output: output,
|
||||
EnvoyConfigType: config.ListenerEnvoyConfigType,
|
||||
IncludeEds: true,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
listener, err := GetXDSResource(ListenerEnvoyConfigType, configDump)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
out, err := formatGatewayConfig(listener, output)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = fmt.Fprintln(c.OutOrStdout(), string(out))
|
||||
_, err = fmt.Fprintln(c.OutOrStdout(), string(envoyConfig))
|
||||
return err
|
||||
}
|
||||
@@ -17,6 +17,7 @@ package hgctl
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/alibaba/higress/cmd/hgctl/config"
|
||||
"github.com/spf13/cobra"
|
||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||
)
|
||||
@@ -45,21 +46,20 @@ func routeConfigCmd() *cobra.Command {
|
||||
}
|
||||
|
||||
func runRouteConfig(c *cobra.Command, args []string) error {
|
||||
configDump, err := retrieveConfigDump(args, false)
|
||||
if len(args) != 0 {
|
||||
podName = args[0]
|
||||
}
|
||||
envoyConfig, err := config.GetEnvoyConfig(&config.GetEnvoyConfigOptions{
|
||||
PodName: podName,
|
||||
PodNamespace: podNamespace,
|
||||
BindAddress: bindAddress,
|
||||
Output: output,
|
||||
EnvoyConfigType: config.RouteEnvoyConfigType,
|
||||
IncludeEds: true,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
route, err := GetXDSResource(RouteEnvoyConfigType, configDump)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
out, err := formatGatewayConfig(route, output)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = fmt.Fprintln(c.OutOrStdout(), string(out))
|
||||
_, err = fmt.Fprintln(c.OutOrStdout(), string(envoyConfig))
|
||||
return err
|
||||
}
|
||||
439
pkg/cmd/hgctl/dashboard.go
Normal file
439
pkg/cmd/hgctl/dashboard.go
Normal file
@@ -0,0 +1,439 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package hgctl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/kubernetes"
|
||||
"github.com/alibaba/higress/pkg/cmd/options"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/flags"
|
||||
types2 "github.com/docker/docker/api/types"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
var (
|
||||
listenPort = 0
|
||||
promPort = 0
|
||||
grafanaPort = 0
|
||||
consolePort = 0
|
||||
controllerPort = 0
|
||||
|
||||
bindAddress = "localhost"
|
||||
|
||||
// open browser or not, default is true
|
||||
browser = true
|
||||
|
||||
// label selector
|
||||
labelSelector = ""
|
||||
|
||||
addonNamespace = ""
|
||||
|
||||
envoyDashNs = ""
|
||||
|
||||
proxyAdminPort int
|
||||
|
||||
docker = false
|
||||
)
|
||||
|
||||
const (
|
||||
defaultPrometheusPort = 9090
|
||||
defaultGrafanaPort = 3000
|
||||
defaultConsolePort = 8080
|
||||
defaultControllerPort = 8888
|
||||
)
|
||||
|
||||
func newDashboardCmd() *cobra.Command {
|
||||
dashboardCmd := &cobra.Command{
|
||||
Use: "dashboard",
|
||||
Aliases: []string{"dash", "d"},
|
||||
Short: "Access to Higress web UIs",
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 0 {
|
||||
return fmt.Errorf("unknown dashboard %q", args[0])
|
||||
}
|
||||
return nil
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cmd.HelpFunc()(cmd, args)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
dashboardCmd.PersistentFlags().IntVarP(&listenPort, "port", "p", 0, "Local port to listen to")
|
||||
dashboardCmd.PersistentFlags().BoolVar(&browser, "browser", true,
|
||||
"When --browser is supplied as false, hgctl dashboard will not open the browser. "+
|
||||
"Default is true which means hgctl dashboard will always open a browser to view the dashboard.")
|
||||
dashboardCmd.PersistentFlags().StringVarP(&addonNamespace, "namespace", "n", "higress-system",
|
||||
"Namespace where the addon is running, if not specified, higress-system would be used")
|
||||
dashboardCmd.PersistentFlags().StringVarP(&bindAddress, "listen", "l", "localhost", "The address to bind to")
|
||||
|
||||
prom := promDashCmd()
|
||||
prom.PersistentFlags().IntVar(&promPort, "ui-port", defaultPrometheusPort, "The component dashboard UI port.")
|
||||
dashboardCmd.AddCommand(prom)
|
||||
|
||||
graf := grafanaDashCmd()
|
||||
graf.PersistentFlags().IntVar(&grafanaPort, "ui-port", defaultGrafanaPort, "The component dashboard UI port.")
|
||||
dashboardCmd.AddCommand(graf)
|
||||
|
||||
envoy := envoyDashCmd()
|
||||
envoy.PersistentFlags().StringVarP(&labelSelector, "selector", "s", "app=higress-gateway", "Label selector")
|
||||
envoy.PersistentFlags().StringVarP(&envoyDashNs, "namespace", "n", "",
|
||||
"Namespace where the addon is running, if not specified, higress-system would be used")
|
||||
envoy.PersistentFlags().IntVar(&proxyAdminPort, "ui-port", defaultProxyAdminPort, "The component dashboard UI port.")
|
||||
dashboardCmd.AddCommand(envoy)
|
||||
|
||||
consoleCmd := consoleDashCmd()
|
||||
consoleCmd.PersistentFlags().IntVar(&consolePort, "ui-port", defaultConsolePort, "The component dashboard UI port.")
|
||||
consoleCmd.PersistentFlags().BoolVar(&docker, "docker", false, "Search higress console from docker")
|
||||
dashboardCmd.AddCommand(consoleCmd)
|
||||
|
||||
controllerDebugCmd := controllerDebugCmd()
|
||||
controllerDebugCmd.PersistentFlags().IntVar(&controllerPort, "ui-port", defaultControllerPort, "The component dashboard UI port.")
|
||||
dashboardCmd.AddCommand(controllerDebugCmd)
|
||||
flags := dashboardCmd.PersistentFlags()
|
||||
options.AddKubeConfigFlags(flags)
|
||||
return dashboardCmd
|
||||
}
|
||||
|
||||
// port-forward to Higress System Prometheus; open browser
|
||||
func promDashCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "prometheus",
|
||||
Short: "Open Prometheus web UI",
|
||||
Long: `Open Higress's Prometheus dashboard`,
|
||||
Example: ` hgctl dashboard prometheus
|
||||
|
||||
# with short syntax
|
||||
hgctl dash prometheus
|
||||
hgctl d prometheus`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
client, err := kubernetes.NewCLIClient(options.DefaultConfigFlags.ToRawKubeConfigLoader())
|
||||
if err != nil {
|
||||
return fmt.Errorf("build CLI client fail: %w", err)
|
||||
}
|
||||
|
||||
pl, err := client.PodsForSelector(addonNamespace, "app=higress-console-prometheus")
|
||||
if err != nil {
|
||||
return fmt.Errorf("not able to locate Prometheus pod: %v", err)
|
||||
}
|
||||
|
||||
if len(pl.Items) < 1 {
|
||||
return errors.New("no Prometheus pods found")
|
||||
}
|
||||
|
||||
// only use the first pod in the list
|
||||
return portForward(pl.Items[0].Name, addonNamespace, "Prometheus",
|
||||
"http://%s", bindAddress, promPort, client, cmd.OutOrStdout(), browser)
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// port-forward to Higress System Console; open browser
|
||||
func consoleDashCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "console",
|
||||
Short: "Open Console web UI",
|
||||
Long: `Open Higress Console`,
|
||||
Example: ` hgctl dashboard console
|
||||
|
||||
# with short syntax
|
||||
hgctl dash console
|
||||
hgctl d console`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if docker {
|
||||
return accessDocker(cmd)
|
||||
}
|
||||
client, err := kubernetes.NewCLIClient(options.DefaultConfigFlags.ToRawKubeConfigLoader())
|
||||
if err != nil {
|
||||
fmt.Printf("build kubernetes CLI client fail: %v\ntry to access docker container\n", err)
|
||||
return accessDocker(cmd)
|
||||
}
|
||||
pl, err := client.PodsForSelector(addonNamespace, "app.kubernetes.io/name=higress-console")
|
||||
if err != nil {
|
||||
fmt.Printf("build kubernetes CLI client fail: %v\ntry to access docker container\n", err)
|
||||
return accessDocker(cmd)
|
||||
}
|
||||
|
||||
if len(pl.Items) < 1 {
|
||||
fmt.Printf("no higress console pods found\ntry to access docker container\n")
|
||||
return accessDocker(cmd)
|
||||
}
|
||||
|
||||
// only use the first pod in the list
|
||||
return portForward(pl.Items[0].Name, addonNamespace, "Console",
|
||||
"http://%s", bindAddress, consolePort, client, cmd.OutOrStdout(), browser)
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// accessDocker access docker container
|
||||
func accessDocker(cmd *cobra.Command) error {
|
||||
dockerCli, err := command.NewDockerCli(command.WithCombinedStreams(os.Stdout))
|
||||
if err != nil {
|
||||
return fmt.Errorf("build docker CLI client fail: %w", err)
|
||||
}
|
||||
err = dockerCli.Initialize(flags.NewClientOptions())
|
||||
if err != nil {
|
||||
return fmt.Errorf("docker client initialize fail: %w", err)
|
||||
}
|
||||
apiClient := dockerCli.Client()
|
||||
list, err := apiClient.ContainerList(context.Background(), types2.ContainerListOptions{})
|
||||
for _, container := range list {
|
||||
for i, name := range container.Names {
|
||||
if strings.Contains(name, "higress-console") {
|
||||
port := container.Ports[i].PublicPort
|
||||
// not support define ip address
|
||||
url := fmt.Sprintf("http://localhost:%d", port)
|
||||
openBrowser(url, cmd.OutOrStdout(), browser)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors.New("no higress console container found")
|
||||
}
|
||||
|
||||
// port-forward to Higress System Grafana; open browser
|
||||
func grafanaDashCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "grafana",
|
||||
Short: "Open Grafana web UI",
|
||||
Long: `Open Higress's Grafana dashboard`,
|
||||
Example: ` hgctl dashboard grafana
|
||||
|
||||
# with short syntax
|
||||
hgctl dash grafana
|
||||
hgctl d grafana`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
client, err := kubernetes.NewCLIClient(options.DefaultConfigFlags.ToRawKubeConfigLoader())
|
||||
if err != nil {
|
||||
return fmt.Errorf("build CLI client fail: %w", err)
|
||||
}
|
||||
pl, err := client.PodsForSelector(addonNamespace, "app=higress-console-grafana")
|
||||
if err != nil {
|
||||
return fmt.Errorf("not able to locate Grafana pod: %v", err)
|
||||
}
|
||||
|
||||
if len(pl.Items) < 1 {
|
||||
return errors.New("no Grafana pods found")
|
||||
}
|
||||
|
||||
// only use the first pod in the list
|
||||
return portForward(pl.Items[0].Name, addonNamespace, "Grafana",
|
||||
"http://%s", bindAddress, grafanaPort, client, cmd.OutOrStdout(), browser)
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// port-forward to sidecar Envoy admin port; open browser
|
||||
func envoyDashCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "envoy [<type>/]<name>[.<namespace>]",
|
||||
Short: "Open Envoy admin web UI",
|
||||
Long: `Open the Envoy admin dashboard for a higress gateway`,
|
||||
Example: ` # Open Envoy dashboard for the higress-gateway-56f9b9797-b9nnc
|
||||
hgctl dashboard envoy higress-gateway-56f9b9797-b9nnc
|
||||
|
||||
# with short syntax
|
||||
hgctl dash envoy
|
||||
hgctl d envoy
|
||||
`,
|
||||
RunE: func(c *cobra.Command, args []string) error {
|
||||
kubeClient, err := kubernetes.NewCLIClient(options.DefaultConfigFlags.ToRawKubeConfigLoader())
|
||||
if err != nil {
|
||||
return fmt.Errorf("build CLI client fail: %w", err)
|
||||
}
|
||||
|
||||
if labelSelector == "" && len(args) < 1 {
|
||||
c.Println(c.UsageString())
|
||||
return fmt.Errorf("specify a pod or --selector")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create k8s client: %v", err)
|
||||
}
|
||||
|
||||
var podName, ns string
|
||||
if labelSelector != "" {
|
||||
pl, err := kubeClient.PodsForSelector(envoyDashNs, labelSelector)
|
||||
if err != nil {
|
||||
return fmt.Errorf("not able to locate pod with selector %s: %v", labelSelector, err)
|
||||
}
|
||||
|
||||
if len(pl.Items) < 1 {
|
||||
return errors.New("no pods found")
|
||||
}
|
||||
// only use the first pod in the list
|
||||
podName = pl.Items[0].Name
|
||||
ns = pl.Items[0].Namespace
|
||||
} else if len(args) > 0 {
|
||||
po, err := kubeClient.Pod(types.NamespacedName{Name: args[0], Namespace: envoyDashNs})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
podName = po.Name
|
||||
ns = po.Namespace
|
||||
}
|
||||
|
||||
return portForward(podName, ns, fmt.Sprintf("Envoy sidecar %s", podName),
|
||||
"http://%s", bindAddress, proxyAdminPort, kubeClient, c.OutOrStdout(), browser)
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// port-forward to Higress System Console; open browser
|
||||
func controllerDebugCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "controller",
|
||||
Short: "Open Controller debug web UI",
|
||||
Long: `Open Higress Controller`,
|
||||
Example: ` hgctl dashboard controller
|
||||
|
||||
# with short syntax
|
||||
hgctl dash controller
|
||||
hgctl d controller`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
client, err := kubernetes.NewCLIClient(options.DefaultConfigFlags.ToRawKubeConfigLoader())
|
||||
if err != nil {
|
||||
return fmt.Errorf("build CLI client fail: %w", err)
|
||||
}
|
||||
|
||||
pl, err := client.PodsForSelector(addonNamespace, "app=higress-controller")
|
||||
if err != nil {
|
||||
return fmt.Errorf("not able to locate controller pod: %v", err)
|
||||
}
|
||||
|
||||
if len(pl.Items) < 1 {
|
||||
return errors.New("no higress controller pods found")
|
||||
}
|
||||
|
||||
// only use the first pod in the list
|
||||
return portForward(pl.Items[0].Name, addonNamespace, "Controller",
|
||||
"http://%s/debug", bindAddress, controllerPort, client, cmd.OutOrStdout(), browser)
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// portForward first tries to forward localhost:remotePort to podName:remotePort, falls back to dynamic local port
|
||||
func portForward(podName, namespace, flavor, urlFormat, localAddress string, remotePort int,
|
||||
client kubernetes.CLIClient, writer io.Writer, browser bool,
|
||||
) error {
|
||||
// port preference:
|
||||
// - If --listenPort is specified, use it
|
||||
// - without --listenPort, prefer the remotePort but fall back to a random port
|
||||
var portPrefs []int
|
||||
if listenPort != 0 {
|
||||
portPrefs = []int{listenPort}
|
||||
} else {
|
||||
portPrefs = []int{remotePort}
|
||||
}
|
||||
|
||||
var err error
|
||||
for _, localPort := range portPrefs {
|
||||
var fw kubernetes.PortForwarder
|
||||
fw, err = kubernetes.NewLocalPortForwarder(client, types.NamespacedName{Namespace: namespace, Name: podName}, localPort, remotePort, bindAddress)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not build port forwarder for %s: %v", flavor, err)
|
||||
}
|
||||
|
||||
if err := fw.Start(); err != nil {
|
||||
fw.Stop()
|
||||
// Try the next port
|
||||
continue
|
||||
}
|
||||
|
||||
// Close the port forwarder when the command is terminated.
|
||||
ClosePortForwarderOnInterrupt(fw)
|
||||
|
||||
openBrowser(fmt.Sprintf(urlFormat, fw.Address()), writer, browser)
|
||||
|
||||
// Wait for stop
|
||||
fw.WaitForStop()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failure running port forward process: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ClosePortForwarderOnInterrupt(fw kubernetes.PortForwarder) {
|
||||
go func() {
|
||||
signals := make(chan os.Signal, 1)
|
||||
signal.Notify(signals, os.Interrupt)
|
||||
defer signal.Stop(signals)
|
||||
<-signals
|
||||
fw.Stop()
|
||||
}()
|
||||
}
|
||||
|
||||
func openBrowser(url string, writer io.Writer, browser bool) {
|
||||
fmt.Fprintf(writer, "%s\n", url)
|
||||
|
||||
if !browser {
|
||||
fmt.Fprint(writer, "skipping opening a browser")
|
||||
return
|
||||
}
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
openCommand(writer, "xdg-open", url)
|
||||
case "windows":
|
||||
openCommand(writer, "rundll32", "url.dll,FileProtocolHandler", url)
|
||||
case "darwin":
|
||||
openCommand(writer, "open", url)
|
||||
default:
|
||||
fmt.Fprintf(writer, "Unsupported platform %q; open %s in your browser.\n", runtime.GOOS, url)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func openCommand(writer io.Writer, command string, args ...string) {
|
||||
_, err := exec.LookPath(command)
|
||||
if err != nil {
|
||||
if errors.Is(err, exec.ErrNotFound) {
|
||||
fmt.Fprintf(writer, "Could not open your browser. Please open it maually.\n")
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(writer, "Failed to open browser; open %s in your browser.\nError: %s\n", args[0], err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
err = exec.Command(command, args...).Start()
|
||||
if err != nil {
|
||||
fmt.Fprintf(writer, "Failed to open browser; open %s in your browser.\nError: %s\n", args[0], err.Error())
|
||||
}
|
||||
}
|
||||
111
pkg/cmd/hgctl/docker/compose.go
Normal file
111
pkg/cmd/hgctl/docker/compose.go
Normal file
@@ -0,0 +1,111 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/compose-spec/compose-go/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/flags"
|
||||
"github.com/docker/compose/v2/cmd/formatter"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/compose"
|
||||
)
|
||||
|
||||
type Compose struct {
|
||||
client *api.ServiceProxy
|
||||
w io.Writer
|
||||
}
|
||||
|
||||
func NewCompose(w io.Writer) (*Compose, error) {
|
||||
c := &Compose{w: w}
|
||||
|
||||
dockerCli, err := command.NewDockerCli(
|
||||
command.WithCombinedStreams(c.w),
|
||||
// command.WithDefaultContextStoreConfig(), Deprecated, set during NewDockerCli
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
opts := flags.NewClientOptions()
|
||||
err = dockerCli.Initialize(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.client = api.NewServiceProxy().WithService(compose.NewComposeService(dockerCli.Client(), dockerCli.ConfigFile()))
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c Compose) Up(ctx context.Context, name string, configs []string, source string, detach bool) error {
|
||||
pOpts, err := cli.NewProjectOptions(
|
||||
configs,
|
||||
cli.WithWorkingDirectory(source),
|
||||
cli.WithDefaultConfigPath,
|
||||
cli.WithName(name),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
project, err := cli.ProjectFromOptions(pOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, s := range project.Services {
|
||||
// TODO(WeixinX): Change from `Label` to `CustomLabels` after upgrading the dependency library github.com/compose-spec/compose-go
|
||||
s.Labels = map[string]string{
|
||||
api.ProjectLabel: project.Name,
|
||||
api.ServiceLabel: s.Name,
|
||||
api.VersionLabel: api.ComposeVersion,
|
||||
api.WorkingDirLabel: project.WorkingDir,
|
||||
api.ConfigFilesLabel: strings.Join(project.ComposeFiles, ","),
|
||||
api.OneoffLabel: "False",
|
||||
}
|
||||
project.Services[i] = s
|
||||
}
|
||||
project.WithoutUnnecessaryResources()
|
||||
|
||||
// for log
|
||||
var consumer api.LogConsumer
|
||||
if !detach {
|
||||
// TODO(WeixinX): Change to `formatter.NewLogConsumer(ctx, c.w, c.w, true, true, false)` after upgrading the dependency library github.com/compose-spec/compose-go
|
||||
consumer = formatter.NewLogConsumer(ctx, c.w, true, true)
|
||||
}
|
||||
attachTo := make([]string, 0)
|
||||
for _, svc := range project.Services {
|
||||
attachTo = append(attachTo, svc.Name)
|
||||
}
|
||||
|
||||
return c.client.Up(ctx, project, api.UpOptions{
|
||||
Start: api.StartOptions{
|
||||
Attach: consumer,
|
||||
AttachTo: attachTo,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (c Compose) List(ctx context.Context) ([]api.Stack, error) {
|
||||
return c.client.List(ctx, api.ListOptions{})
|
||||
}
|
||||
|
||||
func (c Compose) Down(ctx context.Context, name string) error {
|
||||
return c.client.Down(ctx, name, api.DownOptions{})
|
||||
}
|
||||
365
pkg/cmd/hgctl/helm/common.go
Normal file
365
pkg/cmd/hgctl/helm/common.go
Normal file
@@ -0,0 +1,365 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package helm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/helm/tpath"
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/util"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
// GetProfileFromFlags get profile name from flags.
|
||||
func GetProfileFromFlags(setFlags []string) (string, error) {
|
||||
profileName := DefaultProfileName
|
||||
// The profile coming from --set flag has the highest precedence.
|
||||
psf := GetValueForSetFlag(setFlags, "profile")
|
||||
if psf != "" {
|
||||
profileName = psf
|
||||
}
|
||||
return profileName, nil
|
||||
}
|
||||
|
||||
func GetValuesOverylayFromFiles(inFilenames []string) (string, error) {
|
||||
// Convert layeredYamls under values node in profile file to support helm values
|
||||
overLayYamls := ""
|
||||
// Get Overlays from files
|
||||
if len(inFilenames) > 0 {
|
||||
layeredYamls, err := ReadLayeredYAMLs(inFilenames)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
vals := make(map[string]any)
|
||||
if err := yaml.Unmarshal([]byte(layeredYamls), &vals); err != nil {
|
||||
return "", fmt.Errorf("%s:\n\nYAML:\n%s", err, layeredYamls)
|
||||
}
|
||||
values := make(map[string]any)
|
||||
values["values"] = vals
|
||||
out, err := yaml.Marshal(values)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
overLayYamls = string(out)
|
||||
}
|
||||
|
||||
return overLayYamls, nil
|
||||
}
|
||||
|
||||
func GetUninstallProfileName() string {
|
||||
return DefaultUninstallProfileName
|
||||
}
|
||||
|
||||
func ReadLayeredYAMLs(filenames []string) (string, error) {
|
||||
return readLayeredYAMLs(filenames, os.Stdin)
|
||||
}
|
||||
|
||||
func readLayeredYAMLs(filenames []string, stdinReader io.Reader) (string, error) {
|
||||
var ly string
|
||||
var stdin bool
|
||||
for _, fn := range filenames {
|
||||
var b []byte
|
||||
var err error
|
||||
if fn == "-" {
|
||||
if stdin {
|
||||
continue
|
||||
}
|
||||
stdin = true
|
||||
b, err = io.ReadAll(stdinReader)
|
||||
} else {
|
||||
b, err = os.ReadFile(strings.TrimSpace(fn))
|
||||
}
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ly, err = util.OverlayYAML(ly, string(b))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return ly, nil
|
||||
}
|
||||
|
||||
// GetValueForSetFlag parses the passed set flags which have format key=value and if any set the given path,
|
||||
// returns the corresponding value, otherwise returns the empty string. setFlags must have valid format.
|
||||
func GetValueForSetFlag(setFlags []string, path string) string {
|
||||
ret := ""
|
||||
for _, sf := range setFlags {
|
||||
p, v := getPV(sf)
|
||||
if p == path {
|
||||
ret = v
|
||||
}
|
||||
// if set multiple times, return last set value
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// getPV returns the path and value components for the given set flag string, which must be in path=value format.
|
||||
func getPV(setFlag string) (path string, value string) {
|
||||
pv := strings.Split(setFlag, "=")
|
||||
if len(pv) != 2 {
|
||||
return setFlag, ""
|
||||
}
|
||||
path, value = strings.TrimSpace(pv[0]), strings.TrimSpace(pv[1])
|
||||
return
|
||||
}
|
||||
|
||||
func GenerateConfig(inFilenames []string, setFlags []string) (string, *Profile, string, error) {
|
||||
if err := validateSetFlags(setFlags); err != nil {
|
||||
return "", nil, "", err
|
||||
}
|
||||
|
||||
profileName, err := GetProfileFromFlags(setFlags)
|
||||
if err != nil {
|
||||
return "", nil, "", err
|
||||
}
|
||||
|
||||
valuesOverlay, err := GetValuesOverylayFromFiles(inFilenames)
|
||||
if err != nil {
|
||||
return "", nil, "", err
|
||||
}
|
||||
|
||||
profileString, profile, err := GenProfile(profileName, valuesOverlay, setFlags)
|
||||
|
||||
if err != nil {
|
||||
return "", nil, "", err
|
||||
}
|
||||
|
||||
return profileString, profile, profileName, nil
|
||||
}
|
||||
|
||||
// validateSetFlags validates that setFlags all have path=value format.
|
||||
func validateSetFlags(setFlags []string) error {
|
||||
for _, sf := range setFlags {
|
||||
pv := strings.Split(sf, "=")
|
||||
if len(pv) != 2 {
|
||||
return fmt.Errorf("set flag %s has incorrect format, must be path=value", sf)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func overlaySetFlagValues(iopYAML string, setFlags []string) (string, error) {
|
||||
iop := make(map[string]any)
|
||||
if err := yaml.Unmarshal([]byte(iopYAML), &iop); err != nil {
|
||||
return "", err
|
||||
}
|
||||
// Unmarshal returns nil for empty manifests but we need something to insert into.
|
||||
if iop == nil {
|
||||
iop = make(map[string]any)
|
||||
}
|
||||
|
||||
for _, sf := range setFlags {
|
||||
p, v := getPV(sf)
|
||||
inc, _, err := tpath.GetPathContext(iop, util.PathFromString(p), true)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// input value type is always string, transform it to correct type before setting.
|
||||
if err := tpath.WritePathContext(inc, util.ParseValue(v), false); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
out, err := yaml.Marshal(iop)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(out), nil
|
||||
}
|
||||
|
||||
// getInstallPackagePath returns the installPackagePath in the given IstioOperator YAML string.
|
||||
func getInstallPackagePath(profileYAML string) (string, error) {
|
||||
profile, err := UnmarshalProfile(profileYAML)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if profile == nil {
|
||||
return "", nil
|
||||
}
|
||||
return profile.InstallPackagePath, nil
|
||||
}
|
||||
|
||||
// GetProfileYAML returns the YAML for the given profile name, using the given profileOrPath string, which may be either
|
||||
// a profile label or a file path.
|
||||
func GetProfileYAML(installPackagePath, profileOrPath string) (string, error) {
|
||||
if profileOrPath == "" {
|
||||
profileOrPath = DefaultProfileFilename
|
||||
}
|
||||
profiles, err := readProfiles(installPackagePath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read profiles: %v", err)
|
||||
}
|
||||
// If charts are a file path and profile is a name like default, transform it to the file path.
|
||||
if profiles[profileOrPath] && installPackagePath != "" {
|
||||
profileOrPath = filepath.Join(installPackagePath, "profiles", profileOrPath+".yaml")
|
||||
}
|
||||
// This contains the IstioOperator CR.
|
||||
baseCRYAML, err := ReadProfileYAML(profileOrPath, installPackagePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
//if !IsDefaultProfile(profileOrPath) {
|
||||
// // Profile definitions are relative to the default profileOrPath, so read that first.
|
||||
// dfn := DefaultFilenameForProfile(profileOrPath)
|
||||
// defaultYAML, err := ReadProfileYAML(dfn, installPackagePath)
|
||||
// if err != nil {
|
||||
// return "", err
|
||||
// }
|
||||
// baseCRYAML, err = util.OverlayYAML(defaultYAML, baseCRYAML)
|
||||
// if err != nil {
|
||||
// return "", err
|
||||
// }
|
||||
//}
|
||||
return baseCRYAML, nil
|
||||
}
|
||||
|
||||
// IsDefaultProfile reports whether the given profile is the default profile.
|
||||
func IsDefaultProfile(profile string) bool {
|
||||
return profile == "" || profile == DefaultProfileName || filepath.Base(profile) == DefaultProfileFilename
|
||||
}
|
||||
|
||||
// DefaultFilenameForProfile returns the profile name of the default profile for the given profile.
|
||||
func DefaultFilenameForProfile(profile string) string {
|
||||
switch {
|
||||
case util.IsFilePath(profile):
|
||||
return filepath.Join(filepath.Dir(profile), DefaultProfileFilename)
|
||||
default:
|
||||
return DefaultProfileName
|
||||
}
|
||||
}
|
||||
|
||||
// ReadProfileYAML reads the YAML values associated with the given profile. It uses an appropriate reader for the
|
||||
// profile format (compiled-in, file, HTTP, etc.).
|
||||
func ReadProfileYAML(profile, manifestsPath string) (string, error) {
|
||||
var err error
|
||||
var globalValues string
|
||||
|
||||
// Get global values from profile.
|
||||
switch {
|
||||
case util.IsFilePath(profile):
|
||||
if globalValues, err = readFile(profile); err != nil {
|
||||
return "", err
|
||||
}
|
||||
default:
|
||||
if globalValues, err = LoadValues(profile, manifestsPath); err != nil {
|
||||
return "", fmt.Errorf("failed to read profile %v from %v: %v", profile, manifestsPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
return globalValues, nil
|
||||
}
|
||||
|
||||
func readFile(path string) (string, error) {
|
||||
b, err := os.ReadFile(path)
|
||||
return string(b), err
|
||||
}
|
||||
|
||||
// UnmarshalProfile unmarshals a string containing Profile as YAML.
|
||||
func UnmarshalProfile(profileYAML string) (*Profile, error) {
|
||||
profile := &Profile{}
|
||||
if err := yaml.Unmarshal([]byte(profileYAML), profile); err != nil {
|
||||
return nil, fmt.Errorf("%s:\n\nYAML:\n%s", err, profileYAML)
|
||||
}
|
||||
return profile, nil
|
||||
}
|
||||
|
||||
// GenProfile generates an Profile from the given profile name or path, and overlay YAMLs from user
|
||||
// files and the --set flag. If successful, it returns an Profile string and struct.
|
||||
func GenProfile(profileOrPath, fileOverlayYAML string, setFlags []string) (string, *Profile, error) {
|
||||
installPackagePath, err := getInstallPackagePath(fileOverlayYAML)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if sfp := GetValueForSetFlag(setFlags, "installPackagePath"); sfp != "" {
|
||||
// set flag installPackagePath has the highest precedence, if set.
|
||||
installPackagePath = sfp
|
||||
}
|
||||
|
||||
// To generate the base profileOrPath for overlaying with user values, we need the installPackagePath where the profiles
|
||||
// can be found, and the selected profileOrPath. Both of these can come from either the user overlay file or --set flag.
|
||||
outYAML, err := GetProfileYAML(installPackagePath, profileOrPath)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
// Combine file and --set overlays and translate any K8s settings in values to Profile format
|
||||
overlayYAML, err := overlaySetFlagValues(fileOverlayYAML, setFlags)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
// Merge user file and --set flags.
|
||||
outYAML, err = util.OverlayYAML(outYAML, overlayYAML)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("could not overlay user config over base: %s", err)
|
||||
}
|
||||
|
||||
finalProfile, err := UnmarshalProfile(outYAML)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
if len(installPackagePath) > 0 {
|
||||
finalProfile.InstallPackagePath = installPackagePath
|
||||
}
|
||||
|
||||
if finalProfile.Profile == "" {
|
||||
finalProfile.Profile = DefaultProfileName
|
||||
}
|
||||
return util.ToYAML(finalProfile), finalProfile, nil
|
||||
}
|
||||
|
||||
func GenProfileFromProfileContent(profileContent, fileOverlayYAML string, setFlags []string) (string, *Profile, error) {
|
||||
installPackagePath, err := getInstallPackagePath(fileOverlayYAML)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if sfp := GetValueForSetFlag(setFlags, "installPackagePath"); sfp != "" {
|
||||
// set flag installPackagePath has the highest precedence, if set.
|
||||
installPackagePath = sfp
|
||||
}
|
||||
|
||||
// Combine file and --set overlays and translate any K8s settings in values to Profile format
|
||||
overlayYAML, err := overlaySetFlagValues(fileOverlayYAML, setFlags)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
// Merge user file and --set flags.
|
||||
outYAML, err := util.OverlayYAML(profileContent, overlayYAML)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("could not overlay user config over base: %s", err)
|
||||
}
|
||||
|
||||
finalProfile, err := UnmarshalProfile(outYAML)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
if len(installPackagePath) > 0 {
|
||||
finalProfile.InstallPackagePath = installPackagePath
|
||||
}
|
||||
|
||||
if finalProfile.Profile == "" {
|
||||
finalProfile.Profile = DefaultProfileName
|
||||
}
|
||||
return util.ToYAML(finalProfile), finalProfile, nil
|
||||
}
|
||||
63
pkg/cmd/hgctl/helm/name/name.go
Normal file
63
pkg/cmd/hgctl/helm/name/name.go
Normal file
@@ -0,0 +1,63 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package name
|
||||
|
||||
// Kubernetes Kind strings.
|
||||
const (
|
||||
CRDStr = "CustomResourceDefinition"
|
||||
ClusterRoleStr = "ClusterRole"
|
||||
ClusterRoleBindingStr = "ClusterRoleBinding"
|
||||
CMStr = "ConfigMap"
|
||||
DaemonSetStr = "DaemonSet"
|
||||
DeploymentStr = "Deployment"
|
||||
EndpointStr = "Endpoints"
|
||||
HPAStr = "HorizontalPodAutoscaler"
|
||||
IngressStr = "Ingress"
|
||||
IstioOperator = "IstioOperator"
|
||||
MutatingWebhookConfigurationStr = "MutatingWebhookConfiguration"
|
||||
NamespaceStr = "Namespace"
|
||||
PVCStr = "PersistentVolumeClaim"
|
||||
PodStr = "Pod"
|
||||
PDBStr = "PodDisruptionBudget"
|
||||
ReplicationControllerStr = "ReplicationController"
|
||||
ReplicaSetStr = "ReplicaSet"
|
||||
RoleStr = "Role"
|
||||
RoleBindingStr = "RoleBinding"
|
||||
SAStr = "ServiceAccount"
|
||||
ServiceStr = "Service"
|
||||
SecretStr = "Secret"
|
||||
StatefulSetStr = "StatefulSet"
|
||||
ValidatingWebhookConfigurationStr = "ValidatingWebhookConfiguration"
|
||||
)
|
||||
|
||||
// Istio Kind strings
|
||||
const (
|
||||
EnvoyFilterStr = "EnvoyFilter"
|
||||
GatewayStr = "Gateway"
|
||||
DestinationRuleStr = "DestinationRule"
|
||||
MeshPolicyStr = "MeshPolicy"
|
||||
PeerAuthenticationStr = "PeerAuthentication"
|
||||
VirtualServiceStr = "VirtualService"
|
||||
IstioOperatorStr = "IstioOperator"
|
||||
)
|
||||
|
||||
// Istio API Group Names
|
||||
const (
|
||||
AuthenticationAPIGroupName = "authentication.istio.io"
|
||||
ConfigAPIGroupName = "config.istio.io"
|
||||
NetworkingAPIGroupName = "networking.istio.io"
|
||||
OperatorAPIGroupName = "operator.istio.io"
|
||||
SecurityAPIGroupName = "security.istio.io"
|
||||
)
|
||||
573
pkg/cmd/hgctl/helm/object/objects.go
Normal file
573
pkg/cmd/hgctl/helm/object/objects.go
Normal file
@@ -0,0 +1,573 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package object
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
names "github.com/alibaba/higress/pkg/cmd/hgctl/helm/name"
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/helm/tpath"
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/util"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
k8syaml "k8s.io/apimachinery/pkg/util/yaml"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
const (
|
||||
// YAMLSeparator is a separator for multi-document YAML files.
|
||||
YAMLSeparator = "\n---\n"
|
||||
)
|
||||
|
||||
// K8sObject is an in-memory representation of a k8s object, used for moving between different representations
|
||||
// (Unstructured, JSON, YAML) with cached rendering.
|
||||
type K8sObject struct {
|
||||
object *unstructured.Unstructured
|
||||
|
||||
Group string
|
||||
Kind string
|
||||
Name string
|
||||
Namespace string
|
||||
|
||||
json []byte
|
||||
yaml []byte
|
||||
}
|
||||
|
||||
// NewK8sObject creates a new K8sObject and returns a ptr to it.
|
||||
func NewK8sObject(u *unstructured.Unstructured, json, yaml []byte) *K8sObject {
|
||||
o := &K8sObject{
|
||||
object: u,
|
||||
json: json,
|
||||
yaml: yaml,
|
||||
}
|
||||
|
||||
gvk := u.GetObjectKind().GroupVersionKind()
|
||||
o.Group = gvk.Group
|
||||
o.Kind = gvk.Kind
|
||||
o.Name = u.GetName()
|
||||
o.Namespace = u.GetNamespace()
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
// Hash returns a unique, insecure hash based on kind, namespace and name.
|
||||
func Hash(kind, namespace, name string) string {
|
||||
switch kind {
|
||||
case names.ClusterRoleStr, names.ClusterRoleBindingStr, names.MeshPolicyStr:
|
||||
namespace = ""
|
||||
}
|
||||
return strings.Join([]string{kind, namespace, name}, ":")
|
||||
}
|
||||
|
||||
// FromHash parses kind, namespace and name from a hash.
|
||||
func FromHash(hash string) (kind, namespace, name string) {
|
||||
hv := strings.Split(hash, ":")
|
||||
if len(hv) != 3 {
|
||||
return "Bad hash string: " + hash, "", ""
|
||||
}
|
||||
kind, namespace, name = hv[0], hv[1], hv[2]
|
||||
return
|
||||
}
|
||||
|
||||
// HashNameKind returns a unique, insecure hash based on kind and name.
|
||||
func HashNameKind(kind, name string) string {
|
||||
return strings.Join([]string{kind, name}, ":")
|
||||
}
|
||||
|
||||
// ParseJSONToK8sObject parses JSON to an K8sObject.
|
||||
func ParseJSONToK8sObject(json []byte) (*K8sObject, error) {
|
||||
o, _, err := unstructured.UnstructuredJSONScheme.Decode(json, nil, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing json into unstructured object: %v", err)
|
||||
}
|
||||
|
||||
u, ok := o.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("parsed unexpected type %T", o)
|
||||
}
|
||||
|
||||
return NewK8sObject(u, json, nil), nil
|
||||
}
|
||||
|
||||
// ParseYAMLToK8sObject parses YAML to an Object.
|
||||
func ParseYAMLToK8sObject(yaml []byte) (*K8sObject, error) {
|
||||
r := bytes.NewReader(yaml)
|
||||
decoder := k8syaml.NewYAMLOrJSONDecoder(r, 1024)
|
||||
|
||||
out := &unstructured.Unstructured{}
|
||||
err := decoder.Decode(out)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding object %v: %v", string(yaml), err)
|
||||
}
|
||||
return NewK8sObject(out, nil, yaml), nil
|
||||
}
|
||||
|
||||
// UnstructuredObject exposes the raw object, primarily for testing
|
||||
func (o *K8sObject) UnstructuredObject() *unstructured.Unstructured {
|
||||
return o.object
|
||||
}
|
||||
|
||||
// ResolveK8sConflict - This method resolves k8s object possible
|
||||
// conflicting settings. Which K8sObjects may need such method
|
||||
// depends on the type of the K8sObject.
|
||||
func (o *K8sObject) ResolveK8sConflict() *K8sObject {
|
||||
if o.Kind == names.PDBStr {
|
||||
return resolvePDBConflict(o)
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
// Unstructured exposes the raw object content, primarily for testing
|
||||
func (o *K8sObject) Unstructured() map[string]any {
|
||||
return o.UnstructuredObject().UnstructuredContent()
|
||||
}
|
||||
|
||||
// Container returns a container subtree for Deployment objects if one is found, or nil otherwise.
|
||||
func (o *K8sObject) Container(name string) map[string]any {
|
||||
u := o.Unstructured()
|
||||
path := fmt.Sprintf("spec.template.spec.containers.[name:%s]", name)
|
||||
node, f, err := tpath.GetPathContext(u, util.PathFromString(path), false)
|
||||
if err == nil && f {
|
||||
// Must be the type from the schema.
|
||||
return node.Node.(map[string]any)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GroupVersionKind returns the GroupVersionKind for the K8sObject
|
||||
func (o *K8sObject) GroupVersionKind() schema.GroupVersionKind {
|
||||
return o.object.GroupVersionKind()
|
||||
}
|
||||
|
||||
// Version returns the APIVersion of the K8sObject
|
||||
func (o *K8sObject) Version() string {
|
||||
return o.object.GetAPIVersion()
|
||||
}
|
||||
|
||||
// Hash returns a unique hash for the K8sObject
|
||||
func (o *K8sObject) Hash() string {
|
||||
return Hash(o.Kind, o.Namespace, o.Name)
|
||||
}
|
||||
|
||||
// HashNameKind returns a hash for the K8sObject based on the name and kind only.
|
||||
func (o *K8sObject) HashNameKind() string {
|
||||
return HashNameKind(o.Kind, o.Name)
|
||||
}
|
||||
|
||||
// JSON returns a JSON representation of the K8sObject, using an internal cache.
|
||||
func (o *K8sObject) JSON() ([]byte, error) {
|
||||
if o.json != nil {
|
||||
return o.json, nil
|
||||
}
|
||||
|
||||
b, err := o.object.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// YAML returns a YAML representation of the K8sObject, using an internal cache.
|
||||
func (o *K8sObject) YAML() ([]byte, error) {
|
||||
if o == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if o.yaml != nil {
|
||||
return o.yaml, nil
|
||||
}
|
||||
oj, err := o.JSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
o.json = oj
|
||||
y, err := yaml.JSONToYAML(oj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
o.yaml = y
|
||||
return y, nil
|
||||
}
|
||||
|
||||
// YAMLDebugString returns a YAML representation of the K8sObject, or an error string if the K8sObject cannot be rendered to YAML.
|
||||
func (o *K8sObject) YAMLDebugString() string {
|
||||
y, err := o.YAML()
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return string(y)
|
||||
}
|
||||
|
||||
// K8sObjects holds a collection of k8s objects, so that we can filter / sequence them
|
||||
type K8sObjects []*K8sObject
|
||||
|
||||
// String implements the Stringer interface.
|
||||
func (os K8sObjects) String() string {
|
||||
var out []string
|
||||
for _, oo := range os {
|
||||
out = append(out, oo.YAMLDebugString())
|
||||
}
|
||||
return strings.Join(out, YAMLSeparator)
|
||||
}
|
||||
|
||||
// Keys returns a slice with the keys of os.
|
||||
func (os K8sObjects) Keys() []string {
|
||||
var out []string
|
||||
for _, oo := range os {
|
||||
out = append(out, oo.Hash())
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// UnstructuredItems returns the list of items of unstructured.Unstructured.
|
||||
func (os K8sObjects) UnstructuredItems() []unstructured.Unstructured {
|
||||
var usList []unstructured.Unstructured
|
||||
for _, obj := range os {
|
||||
usList = append(usList, *obj.UnstructuredObject())
|
||||
}
|
||||
return usList
|
||||
}
|
||||
|
||||
// ParseK8sObjectsFromYAMLManifest returns a K8sObjects representation of manifest.
|
||||
func ParseK8sObjectsFromYAMLManifest(manifest string) (K8sObjects, error) {
|
||||
return ParseK8sObjectsFromYAMLManifestFailOption(manifest, true)
|
||||
}
|
||||
|
||||
// ParseK8sObjectsFromYAMLManifestFailOption returns a K8sObjects representation of manifest. Continues parsing when a bad object
|
||||
// is found if failOnError is set to false.
|
||||
func ParseK8sObjectsFromYAMLManifestFailOption(manifest string, failOnError bool) (K8sObjects, error) {
|
||||
var b bytes.Buffer
|
||||
|
||||
var yamls []string
|
||||
scanner := bufio.NewScanner(strings.NewReader(manifest))
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if strings.HasPrefix(line, "---") {
|
||||
// yaml separator
|
||||
yamls = append(yamls, b.String())
|
||||
b.Reset()
|
||||
} else {
|
||||
if _, err := b.WriteString(line); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := b.WriteString("\n"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
yamls = append(yamls, b.String())
|
||||
|
||||
var objects K8sObjects
|
||||
|
||||
for _, yaml := range yamls {
|
||||
yaml = removeNonYAMLLines(yaml)
|
||||
if yaml == "" {
|
||||
continue
|
||||
}
|
||||
o, err := ParseYAMLToK8sObject([]byte(yaml))
|
||||
if err != nil {
|
||||
e := fmt.Errorf("failed to parse YAML to a k8s object: %s", err)
|
||||
if failOnError {
|
||||
return nil, e
|
||||
}
|
||||
continue
|
||||
}
|
||||
if o.Valid() {
|
||||
objects = append(objects, o)
|
||||
}
|
||||
}
|
||||
|
||||
return objects, nil
|
||||
}
|
||||
|
||||
func removeNonYAMLLines(yms string) string {
|
||||
var b strings.Builder
|
||||
for _, s := range strings.Split(yms, "\n") {
|
||||
if strings.HasPrefix(s, "#") {
|
||||
continue
|
||||
}
|
||||
b.WriteString(s)
|
||||
b.WriteString("\n")
|
||||
}
|
||||
|
||||
// helm charts sometimes emits blank objects with just a "disabled" comment.
|
||||
return strings.TrimSpace(b.String())
|
||||
}
|
||||
|
||||
// YAMLManifest returns a YAML representation of K8sObjects os.
|
||||
func (os K8sObjects) YAMLManifest() (string, error) {
|
||||
var b bytes.Buffer
|
||||
|
||||
for i, item := range os {
|
||||
if i != 0 {
|
||||
if _, err := b.WriteString("\n\n"); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
ym, err := item.YAML()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error building yaml: %v", err)
|
||||
}
|
||||
if _, err := b.Write(ym); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if _, err := b.Write([]byte(YAMLSeparator)); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return b.String(), nil
|
||||
}
|
||||
|
||||
// Sort will order the items in K8sObjects in order of score, group, kind, name. The intent is to
|
||||
// have a deterministic ordering in which K8sObjects are applied.
|
||||
func (os K8sObjects) Sort(score func(o *K8sObject) int) {
|
||||
sort.Slice(os, func(i, j int) bool {
|
||||
iScore := score(os[i])
|
||||
jScore := score(os[j])
|
||||
return iScore < jScore ||
|
||||
(iScore == jScore &&
|
||||
os[i].Group < os[j].Group) ||
|
||||
(iScore == jScore &&
|
||||
os[i].Group == os[j].Group &&
|
||||
os[i].Kind < os[j].Kind) ||
|
||||
(iScore == jScore &&
|
||||
os[i].Group == os[j].Group &&
|
||||
os[i].Kind == os[j].Kind &&
|
||||
os[i].Name < os[j].Name)
|
||||
})
|
||||
}
|
||||
|
||||
// ToMap returns a map of K8sObject hash to K8sObject.
|
||||
func (os K8sObjects) ToMap() map[string]*K8sObject {
|
||||
ret := make(map[string]*K8sObject)
|
||||
for _, oo := range os {
|
||||
if oo.Valid() {
|
||||
ret[oo.Hash()] = oo
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// ToNameKindMap returns a map of K8sObject name/kind hash to K8sObject.
|
||||
func (os K8sObjects) ToNameKindMap() map[string]*K8sObject {
|
||||
ret := make(map[string]*K8sObject)
|
||||
for _, oo := range os {
|
||||
if oo.Valid() {
|
||||
ret[oo.HashNameKind()] = oo
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Valid checks returns true if Kind of K8sObject is not empty.
|
||||
func (o *K8sObject) Valid() bool {
|
||||
return o.Kind != ""
|
||||
}
|
||||
|
||||
// FullName returns namespace/name of K8s object
|
||||
func (o *K8sObject) FullName() string {
|
||||
return fmt.Sprintf("%s/%s", o.Namespace, o.Name)
|
||||
}
|
||||
|
||||
// Equal returns true if o and other are both valid and equal to each other.
|
||||
func (o *K8sObject) Equal(other *K8sObject) bool {
|
||||
if o == nil {
|
||||
return other == nil
|
||||
}
|
||||
if other == nil {
|
||||
return o == nil
|
||||
}
|
||||
|
||||
ay, err := o.YAML()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
by, err := other.YAML()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return util.IsYAMLEqual(string(ay), string(by))
|
||||
}
|
||||
|
||||
func istioCustomResources(group string) bool {
|
||||
switch group {
|
||||
case names.ConfigAPIGroupName,
|
||||
names.SecurityAPIGroupName,
|
||||
names.AuthenticationAPIGroupName,
|
||||
names.NetworkingAPIGroupName:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// DefaultObjectOrder is default sorting function used to sort k8s objects.
|
||||
func DefaultObjectOrder() func(o *K8sObject) int {
|
||||
return func(o *K8sObject) int {
|
||||
gk := o.Group + "/" + o.Kind
|
||||
switch {
|
||||
// Create CRDs asap - both because they are slow and because we will likely create instances of them soon
|
||||
case gk == "apiextensions.k8s.io/CustomResourceDefinition":
|
||||
return -1000
|
||||
|
||||
// We need to create ServiceAccounts, Roles before we bind them with a RoleBinding
|
||||
case gk == "/ServiceAccount" || gk == "rbac.authorization.k8s.io/ClusterRole":
|
||||
return 1
|
||||
case gk == "rbac.authorization.k8s.io/ClusterRoleBinding":
|
||||
return 2
|
||||
|
||||
// validatingwebhookconfiguration is configured to FAIL-OPEN in the default install. For the
|
||||
// re-install case we want to apply the validatingwebhookconfiguration first to reset any
|
||||
// orphaned validatingwebhookconfiguration that is FAIL-CLOSE.
|
||||
case gk == "admissionregistration.k8s.io/ValidatingWebhookConfiguration":
|
||||
return 3
|
||||
|
||||
case istioCustomResources(o.Group):
|
||||
return 4
|
||||
|
||||
// Pods might need configmap or secrets - avoid backoff by creating them first
|
||||
case gk == "/ConfigMap" || gk == "/Secrets":
|
||||
return 100
|
||||
|
||||
// Create the pods after we've created other things they might be waiting for
|
||||
case gk == "extensions/Deployment" || gk == "app/Deployment":
|
||||
return 1000
|
||||
|
||||
// Autoscalers typically act on a deployment
|
||||
case gk == "autoscaling/HorizontalPodAutoscaler":
|
||||
return 1001
|
||||
|
||||
// Create services late - after pods have been started
|
||||
case gk == "/Service":
|
||||
return 10000
|
||||
|
||||
default:
|
||||
return 1000
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ObjectsNotInLists(objects K8sObjects, lists ...K8sObjects) K8sObjects {
|
||||
var ret K8sObjects
|
||||
|
||||
filterMap := make(map[*K8sObject]bool)
|
||||
for _, list := range lists {
|
||||
for _, object := range list {
|
||||
filterMap[object] = true
|
||||
}
|
||||
}
|
||||
|
||||
for _, o := range objects {
|
||||
if !filterMap[o] {
|
||||
ret = append(ret, o)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// KindObjects returns the subset of objs with the given kind.
|
||||
func KindObjects(objs K8sObjects, kind string) K8sObjects {
|
||||
var ret K8sObjects
|
||||
for _, o := range objs {
|
||||
if o.Kind == kind {
|
||||
ret = append(ret, o)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
//// ParseK8SYAMLToIstioOperator parses a IstioOperator CustomResource YAML string and unmarshals in into
|
||||
//// an IstioOperatorSpec object. It returns the object and an API group/version with it.
|
||||
//func ParseK8SYAMLToIstioOperator(yml string) (*v1alpha1.HigressOperator, *schema.GroupVersionKind, error) {
|
||||
// o, err := ParseYAMLToK8sObject([]byte(yml))
|
||||
// if err != nil {
|
||||
// return nil, nil, err
|
||||
// }
|
||||
// iop := &v1alpha1.HigressOperator{}
|
||||
// if err := yaml.UnmarshalStrict([]byte(yml), iop); err != nil {
|
||||
// return nil, nil, err
|
||||
// }
|
||||
// gvk := o.GroupVersionKind()
|
||||
// //v1alpha1.SetNamespace(iop.Spec, o.Namespace)
|
||||
// return iop, &gvk, nil
|
||||
//}
|
||||
|
||||
// AllObjectHashes returns a map with object hashes of all the objects contained in cmm as the keys.
|
||||
func AllObjectHashes(m string) map[string]bool {
|
||||
ret := make(map[string]bool)
|
||||
objs, err := ParseK8sObjectsFromYAMLManifest(m)
|
||||
if err != nil {
|
||||
}
|
||||
for _, o := range objs {
|
||||
ret[o.Hash()] = true
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// resolvePDBConflict When user uses both minAvailable and
|
||||
// maxUnavailable to configure istio instances, these two
|
||||
// parameters are mutually exclusive, care must be taken
|
||||
// to resolve the issue
|
||||
func resolvePDBConflict(o *K8sObject) *K8sObject {
|
||||
if o.json == nil {
|
||||
return o
|
||||
}
|
||||
if o.object.Object["spec"] == nil {
|
||||
return o
|
||||
}
|
||||
spec := o.object.Object["spec"].(map[string]any)
|
||||
isDefault := func(item any) bool {
|
||||
var ii intstr.IntOrString
|
||||
switch item := item.(type) {
|
||||
case int:
|
||||
ii = intstr.FromInt(item)
|
||||
case int64:
|
||||
ii = intstr.FromInt(int(item))
|
||||
case string:
|
||||
ii = intstr.FromString(item)
|
||||
default:
|
||||
ii = intstr.FromInt(0)
|
||||
}
|
||||
intVal, err := intstr.GetScaledValueFromIntOrPercent(&ii, 100, false)
|
||||
if err != nil || intVal == 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
if spec["maxUnavailable"] != nil && spec["minAvailable"] != nil {
|
||||
// When both maxUnavailable and minAvailable present and
|
||||
// neither has value 0, this is considered a conflict,
|
||||
// then maxUnavailale will take precedence.
|
||||
if !isDefault(spec["maxUnavailable"]) && !isDefault(spec["minAvailable"]) {
|
||||
delete(spec, "minAvailable")
|
||||
// Make sure that the json and yaml representation of the object
|
||||
// is consistent with the changed object
|
||||
o.json = nil
|
||||
o.json, _ = o.JSON()
|
||||
if o.yaml != nil {
|
||||
o.yaml = nil
|
||||
o.yaml, _ = o.YAML()
|
||||
}
|
||||
}
|
||||
}
|
||||
return o
|
||||
}
|
||||
713
pkg/cmd/hgctl/helm/object/objects_test.go
Normal file
713
pkg/cmd/hgctl/helm/object/objects_test.go
Normal file
@@ -0,0 +1,713 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package object
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/util"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
func TestHash(t *testing.T) {
|
||||
hashTests := []struct {
|
||||
desc string
|
||||
kind string
|
||||
namespace string
|
||||
name string
|
||||
want string
|
||||
}{
|
||||
{"CalculateHashForObjectWithNormalCharacter", "Service", "default", "ingressgateway", "Service:default:ingressgateway"},
|
||||
{"CalculateHashForObjectWithDash", "Deployment", "istio-system", "istio-pilot", "Deployment:istio-system:istio-pilot"},
|
||||
{"CalculateHashForObjectWithDot", "ConfigMap", "istio-system", "my.config", "ConfigMap:istio-system:my.config"},
|
||||
}
|
||||
|
||||
for _, tt := range hashTests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
got := Hash(tt.kind, tt.namespace, tt.name)
|
||||
if got != tt.want {
|
||||
t.Errorf("Hash(%s): got %s for kind %s, namespace %s, name %s, want %s", tt.desc, got, tt.kind, tt.namespace, tt.name, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromHash(t *testing.T) {
|
||||
hashTests := []struct {
|
||||
desc string
|
||||
hash string
|
||||
kind string
|
||||
namespace string
|
||||
name string
|
||||
}{
|
||||
{"ParseHashWithNormalCharacter", "Service:default:ingressgateway", "Service", "default", "ingressgateway"},
|
||||
{"ParseHashForObjectWithDash", "Deployment:istio-system:istio-pilot", "Deployment", "istio-system", "istio-pilot"},
|
||||
{"ParseHashForObjectWithDot", "ConfigMap:istio-system:my.config", "ConfigMap", "istio-system", "my.config"},
|
||||
{"InvalidHash", "test", "Bad hash string: test", "", ""},
|
||||
}
|
||||
|
||||
for _, tt := range hashTests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
k, ns, name := FromHash(tt.hash)
|
||||
if k != tt.kind || ns != tt.namespace || name != tt.name {
|
||||
t.Errorf("FromHash(%s): got kind %s, namespace %s, name %s, want kind %s, namespace %s, name %s", tt.desc, k, ns, name, tt.kind, tt.namespace, tt.name)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHashNameKind(t *testing.T) {
|
||||
hashNameKindTests := []struct {
|
||||
desc string
|
||||
kind string
|
||||
name string
|
||||
want string
|
||||
}{
|
||||
{"CalculateHashNameKindForObjectWithNormalCharacter", "Service", "ingressgateway", "Service:ingressgateway"},
|
||||
{"CalculateHashNameKindForObjectWithDash", "Deployment", "istio-pilot", "Deployment:istio-pilot"},
|
||||
{"CalculateHashNameKindForObjectWithDot", "ConfigMap", "my.config", "ConfigMap:my.config"},
|
||||
}
|
||||
|
||||
for _, tt := range hashNameKindTests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
got := HashNameKind(tt.kind, tt.name)
|
||||
if got != tt.want {
|
||||
t.Errorf("HashNameKind(%s): got %s for kind %s, name %s, want %s", tt.desc, got, tt.kind, tt.name, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseJSONToK8sObject(t *testing.T) {
|
||||
testDeploymentJSON := `{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": {
|
||||
"name": "istio-citadel",
|
||||
"namespace": "istio-system",
|
||||
"labels": {
|
||||
"istio": "citadel"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"replicas": 1,
|
||||
"selector": {
|
||||
"matchLabels": {
|
||||
"istio": "citadel"
|
||||
}
|
||||
},
|
||||
"template": {
|
||||
"metadata": {
|
||||
"labels": {
|
||||
"istio": "citadel"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"containers": [
|
||||
{
|
||||
"name": "citadel",
|
||||
"image": "docker.io/istio/citadel:1.1.8",
|
||||
"args": [
|
||||
"--append-dns-names=true",
|
||||
"--grpc-port=8060",
|
||||
"--grpc-hostname=citadel",
|
||||
"--citadel-storage-namespace=istio-system",
|
||||
"--custom-dns-names=istio-pilot-service-account.istio-system:istio-pilot.istio-system",
|
||||
"--monitoring-port=15014",
|
||||
"--self-signed-ca=true"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
||||
testPodJSON := `{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Pod",
|
||||
"metadata": {
|
||||
"name": "istio-galley-75bcd59768-hpt5t",
|
||||
"namespace": "istio-system",
|
||||
"labels": {
|
||||
"istio": "galley"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"containers": [
|
||||
{
|
||||
"name": "galley",
|
||||
"image": "docker.io/istio/galley:1.1.8",
|
||||
"command": [
|
||||
"/usr/local/bin/galley",
|
||||
"server",
|
||||
"--meshConfigFile=/etc/mesh-config/mesh",
|
||||
"--livenessProbeInterval=1s",
|
||||
"--livenessProbePath=/healthliveness",
|
||||
"--readinessProbePath=/healthready",
|
||||
"--readinessProbeInterval=1s",
|
||||
"--deployment-namespace=istio-system",
|
||||
"--insecure=true",
|
||||
"--validation-webhook-config-file",
|
||||
"/etc/config/validatingwebhookconfiguration.yaml",
|
||||
"--monitoringPort=15014",
|
||||
"--log_output_level=default:info"
|
||||
],
|
||||
"ports": [
|
||||
{
|
||||
"containerPort": 443,
|
||||
"protocol": "TCP"
|
||||
},
|
||||
{
|
||||
"containerPort": 15014,
|
||||
"protocol": "TCP"
|
||||
},
|
||||
{
|
||||
"containerPort": 9901,
|
||||
"protocol": "TCP"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}`
|
||||
testServiceJSON := `{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Service",
|
||||
"metadata": {
|
||||
"labels": {
|
||||
"app": "pilot"
|
||||
},
|
||||
"name": "istio-pilot",
|
||||
"namespace": "istio-system"
|
||||
},
|
||||
"spec": {
|
||||
"clusterIP": "10.102.230.31",
|
||||
"ports": [
|
||||
{
|
||||
"name": "grpc-xds",
|
||||
"port": 15010,
|
||||
"protocol": "TCP",
|
||||
"targetPort": 15010
|
||||
},
|
||||
{
|
||||
"name": "https-xds",
|
||||
"port": 15011,
|
||||
"protocol": "TCP",
|
||||
"targetPort": 15011
|
||||
},
|
||||
{
|
||||
"name": "http-legacy-discovery",
|
||||
"port": 8080,
|
||||
"protocol": "TCP",
|
||||
"targetPort": 8080
|
||||
},
|
||||
{
|
||||
"name": "http-monitoring",
|
||||
"port": 15014,
|
||||
"protocol": "TCP",
|
||||
"targetPort": 15014
|
||||
}
|
||||
],
|
||||
"selector": {
|
||||
"istio": "pilot"
|
||||
},
|
||||
"sessionAffinity": "None",
|
||||
"type": "ClusterIP"
|
||||
}
|
||||
}`
|
||||
|
||||
testInvalidJSON := `invalid json`
|
||||
|
||||
parseJSONToK8sObjectTests := []struct {
|
||||
desc string
|
||||
objString string
|
||||
wantGroup string
|
||||
wantKind string
|
||||
wantName string
|
||||
wantNamespace string
|
||||
wantErr bool
|
||||
}{
|
||||
{"ParseJsonToK8sDeployment", testDeploymentJSON, "apps", "Deployment", "istio-citadel", "istio-system", false},
|
||||
{"ParseJsonToK8sPod", testPodJSON, "", "Pod", "istio-galley-75bcd59768-hpt5t", "istio-system", false},
|
||||
{"ParseJsonToK8sService", testServiceJSON, "", "Service", "istio-pilot", "istio-system", false},
|
||||
{"ParseJsonError", testInvalidJSON, "", "", "", "", true},
|
||||
}
|
||||
|
||||
for _, tt := range parseJSONToK8sObjectTests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
k8sObj, err := ParseJSONToK8sObject([]byte(tt.objString))
|
||||
if err == nil {
|
||||
if tt.wantErr {
|
||||
t.Errorf("ParseJsonToK8sObject(%s): should be error", tt.desc)
|
||||
}
|
||||
k8sObjStr := k8sObj.YAMLDebugString()
|
||||
if k8sObj.Group != tt.wantGroup {
|
||||
t.Errorf("ParseJsonToK8sObject(%s): got group %s for k8s object %s, want %s", tt.desc, k8sObj.Group, k8sObjStr, tt.wantGroup)
|
||||
}
|
||||
if k8sObj.Kind != tt.wantKind {
|
||||
t.Errorf("ParseJsonToK8sObject(%s): got kind %s for k8s object %s, want %s", tt.desc, k8sObj.Kind, k8sObjStr, tt.wantKind)
|
||||
}
|
||||
if k8sObj.Name != tt.wantName {
|
||||
t.Errorf("ParseJsonToK8sObject(%s): got name %s for k8s object %s, want %s", tt.desc, k8sObj.Name, k8sObjStr, tt.wantName)
|
||||
}
|
||||
if k8sObj.Namespace != tt.wantNamespace {
|
||||
t.Errorf("ParseJsonToK8sObject(%s): got group %s for k8s object %s, want %s", tt.desc, k8sObj.Namespace, k8sObjStr, tt.wantNamespace)
|
||||
}
|
||||
} else if !tt.wantErr {
|
||||
t.Errorf("ParseJsonToK8sObject(%s): got unexpected error: %v", tt.desc, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseYAMLToK8sObject(t *testing.T) {
|
||||
testDeploymentYaml := `apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: istio-citadel
|
||||
namespace: istio-system
|
||||
labels:
|
||||
istio: citadel
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
istio: citadel
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
istio: citadel
|
||||
spec:
|
||||
containers:
|
||||
- name: citadel
|
||||
image: docker.io/istio/citadel:1.1.8
|
||||
args:
|
||||
- "--append-dns-names=true"
|
||||
- "--grpc-port=8060"
|
||||
- "--grpc-hostname=citadel"
|
||||
- "--citadel-storage-namespace=istio-system"
|
||||
- "--custom-dns-names=istio-pilot-service-account.istio-system:istio-pilot.istio-system"
|
||||
- "--monitoring-port=15014"
|
||||
- "--self-signed-ca=true"`
|
||||
|
||||
testPodYaml := `apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: istio-galley-75bcd59768-hpt5t
|
||||
namespace: istio-system
|
||||
labels:
|
||||
istio: galley
|
||||
spec:
|
||||
containers:
|
||||
- name: galley
|
||||
image: docker.io/istio/galley:1.1.8
|
||||
command:
|
||||
- "/usr/local/bin/galley"
|
||||
- server
|
||||
- "--meshConfigFile=/etc/mesh-config/mesh"
|
||||
- "--livenessProbeInterval=1s"
|
||||
- "--livenessProbePath=/healthliveness"
|
||||
- "--readinessProbePath=/healthready"
|
||||
- "--readinessProbeInterval=1s"
|
||||
- "--deployment-namespace=istio-system"
|
||||
- "--insecure=true"
|
||||
- "--validation-webhook-config-file"
|
||||
- "/etc/config/validatingwebhookconfiguration.yaml"
|
||||
- "--monitoringPort=15014"
|
||||
- "--log_output_level=default:info"
|
||||
ports:
|
||||
- containerPort: 443
|
||||
protocol: TCP
|
||||
- containerPort: 15014
|
||||
protocol: TCP
|
||||
- containerPort: 9901
|
||||
protocol: TCP`
|
||||
|
||||
testServiceYaml := `apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
app: pilot
|
||||
name: istio-pilot
|
||||
namespace: istio-system
|
||||
spec:
|
||||
clusterIP: 10.102.230.31
|
||||
ports:
|
||||
- name: grpc-xds
|
||||
port: 15010
|
||||
protocol: TCP
|
||||
targetPort: 15010
|
||||
- name: https-xds
|
||||
port: 15011
|
||||
protocol: TCP
|
||||
targetPort: 15011
|
||||
- name: http-legacy-discovery
|
||||
port: 8080
|
||||
protocol: TCP
|
||||
targetPort: 8080
|
||||
- name: http-monitoring
|
||||
port: 15014
|
||||
protocol: TCP
|
||||
targetPort: 15014
|
||||
selector:
|
||||
istio: pilot
|
||||
sessionAffinity: None
|
||||
type: ClusterIP`
|
||||
|
||||
parseYAMLToK8sObjectTests := []struct {
|
||||
desc string
|
||||
objString string
|
||||
wantGroup string
|
||||
wantKind string
|
||||
wantName string
|
||||
wantNamespace string
|
||||
}{
|
||||
{"ParseYamlToK8sDeployment", testDeploymentYaml, "apps", "Deployment", "istio-citadel", "istio-system"},
|
||||
{"ParseYamlToK8sPod", testPodYaml, "", "Pod", "istio-galley-75bcd59768-hpt5t", "istio-system"},
|
||||
{"ParseYamlToK8sService", testServiceYaml, "", "Service", "istio-pilot", "istio-system"},
|
||||
}
|
||||
|
||||
for _, tt := range parseYAMLToK8sObjectTests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
k8sObj, err := ParseYAMLToK8sObject([]byte(tt.objString))
|
||||
if err != nil {
|
||||
k8sObjStr := k8sObj.YAMLDebugString()
|
||||
if k8sObj.Group != tt.wantGroup {
|
||||
t.Errorf("ParseYAMLToK8sObject(%s): got group %s for k8s object %s, want %s", tt.desc, k8sObj.Group, k8sObjStr, tt.wantGroup)
|
||||
}
|
||||
if k8sObj.Group != tt.wantGroup {
|
||||
t.Errorf("ParseYAMLToK8sObject(%s): got kind %s for k8s object %s, want %s", tt.desc, k8sObj.Kind, k8sObjStr, tt.wantKind)
|
||||
}
|
||||
if k8sObj.Name != tt.wantName {
|
||||
t.Errorf("ParseYAMLToK8sObject(%s): got name %s for k8s object %s, want %s", tt.desc, k8sObj.Name, k8sObjStr, tt.wantName)
|
||||
}
|
||||
if k8sObj.Namespace != tt.wantNamespace {
|
||||
t.Errorf("ParseYAMLToK8sObject(%s): got group %s for k8s object %s, want %s", tt.desc, k8sObj.Namespace, k8sObjStr, tt.wantNamespace)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseK8sObjectsFromYAMLManifest(t *testing.T) {
|
||||
testDeploymentYaml := `apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: istio-citadel
|
||||
namespace: istio-system
|
||||
labels:
|
||||
istio: citadel
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
istio: citadel
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
istio: citadel
|
||||
spec:
|
||||
containers:
|
||||
- name: citadel
|
||||
image: docker.io/istio/citadel:1.1.8
|
||||
args:
|
||||
- "--append-dns-names=true"
|
||||
- "--grpc-port=8060"
|
||||
- "--grpc-hostname=citadel"
|
||||
- "--citadel-storage-namespace=istio-system"
|
||||
- "--custom-dns-names=istio-pilot-service-account.istio-system:istio-pilot.istio-system"
|
||||
- "--monitoring-port=15014"
|
||||
- "--self-signed-ca=true"`
|
||||
|
||||
testPodYaml := `apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: istio-galley-75bcd59768-hpt5t
|
||||
namespace: istio-system
|
||||
labels:
|
||||
istio: galley
|
||||
spec:
|
||||
containers:
|
||||
- name: galley
|
||||
image: docker.io/istio/galley:1.1.8
|
||||
command:
|
||||
- "/usr/local/bin/galley"
|
||||
- server
|
||||
- "--meshConfigFile=/etc/mesh-config/mesh"
|
||||
- "--livenessProbeInterval=1s"
|
||||
- "--livenessProbePath=/healthliveness"
|
||||
- "--readinessProbePath=/healthready"
|
||||
- "--readinessProbeInterval=1s"
|
||||
- "--deployment-namespace=istio-system"
|
||||
- "--insecure=true"
|
||||
- "--validation-webhook-config-file"
|
||||
- "/etc/config/validatingwebhookconfiguration.yaml"
|
||||
- "--monitoringPort=15014"
|
||||
- "--log_output_level=default:info"
|
||||
ports:
|
||||
- containerPort: 443
|
||||
protocol: TCP
|
||||
- containerPort: 15014
|
||||
protocol: TCP
|
||||
- containerPort: 9901
|
||||
protocol: TCP`
|
||||
|
||||
testServiceYaml := `apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
app: pilot
|
||||
name: istio-pilot
|
||||
namespace: istio-system
|
||||
spec:
|
||||
clusterIP: 10.102.230.31
|
||||
ports:
|
||||
- name: grpc-xds
|
||||
port: 15010
|
||||
protocol: TCP
|
||||
targetPort: 15010
|
||||
- name: https-xds
|
||||
port: 15011
|
||||
protocol: TCP
|
||||
targetPort: 15011
|
||||
- name: http-legacy-discovery
|
||||
port: 8080
|
||||
protocol: TCP
|
||||
targetPort: 8080
|
||||
- name: http-monitoring
|
||||
port: 15014
|
||||
protocol: TCP
|
||||
targetPort: 15014
|
||||
selector:
|
||||
istio: pilot
|
||||
sessionAffinity: None
|
||||
type: ClusterIP`
|
||||
|
||||
parseK8sObjectsFromYAMLManifestTests := []struct {
|
||||
desc string
|
||||
objsMap map[string]string
|
||||
}{
|
||||
{
|
||||
"FromHybridYAMLManifest",
|
||||
map[string]string{
|
||||
"Deployment:istio-system:istio-citadel": testDeploymentYaml,
|
||||
"Pod:istio-system:istio-galley-75bcd59768-hpt5t": testPodYaml,
|
||||
"Service:istio-system:istio-pilot": testServiceYaml,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range parseK8sObjectsFromYAMLManifestTests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
testManifestYaml := strings.Join([]string{testDeploymentYaml, testPodYaml, testServiceYaml}, YAMLSeparator)
|
||||
gotK8sObjs, err := ParseK8sObjectsFromYAMLManifest(testManifestYaml)
|
||||
if err != nil {
|
||||
gotK8sObjsMap := gotK8sObjs.ToMap()
|
||||
for objHash, want := range tt.objsMap {
|
||||
if gotObj, ok := gotK8sObjsMap[objHash]; ok {
|
||||
gotObjYaml := gotObj.YAMLDebugString()
|
||||
if !util.IsYAMLEqual(gotObjYaml, want) {
|
||||
t.Errorf("ParseK8sObjectsFromYAMLManifest(%s): got:\n%s\n\nwant:\n%s\nDiff:\n%s\n", tt.desc, gotObjYaml, want, util.YAMLDiff(gotObjYaml, want))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestK8sObject_Equal(t *testing.T) {
|
||||
obj1 := K8sObject{
|
||||
object: &unstructured.Unstructured{Object: map[string]any{
|
||||
"key": "value1",
|
||||
}},
|
||||
}
|
||||
obj2 := K8sObject{
|
||||
object: &unstructured.Unstructured{Object: map[string]any{
|
||||
"key": "value2",
|
||||
}},
|
||||
}
|
||||
cases := []struct {
|
||||
desc string
|
||||
o1 *K8sObject
|
||||
o2 *K8sObject
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
desc: "Equals",
|
||||
o1: &obj1,
|
||||
o2: &obj1,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
desc: "NotEquals",
|
||||
o1: &obj1,
|
||||
o2: &obj2,
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
desc: "NilSource",
|
||||
o1: nil,
|
||||
o2: &obj2,
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
desc: "NilDest",
|
||||
o1: &obj1,
|
||||
o2: nil,
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
desc: "TwoNils",
|
||||
o1: nil,
|
||||
o2: nil,
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range cases {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
res := tt.o1.Equal(tt.o2)
|
||||
if res != tt.want {
|
||||
t.Errorf("got %v, want: %v", res, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestK8sObject_ResolveK8sConflict(t *testing.T) {
|
||||
getK8sObject := func(ystr string) *K8sObject {
|
||||
o, err := ParseYAMLToK8sObject([]byte(ystr))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Ensure that json data is in sync.
|
||||
// Since the object was created using yaml, json is empty.
|
||||
// make sure the object json is set correctly.
|
||||
o.json, _ = o.JSON()
|
||||
return o
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
o1 *K8sObject
|
||||
o2 *K8sObject
|
||||
}{
|
||||
{
|
||||
desc: "not applicable kind",
|
||||
o1: getK8sObject(`
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
app: pilot
|
||||
name: istio-pilot
|
||||
namespace: istio-system
|
||||
spec:
|
||||
clusterIP: 10.102.230.31`),
|
||||
o2: getK8sObject(`
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
app: pilot
|
||||
name: istio-pilot
|
||||
namespace: istio-system
|
||||
spec:
|
||||
clusterIP: 10.102.230.31`),
|
||||
},
|
||||
{
|
||||
desc: "only minAvailable is set",
|
||||
o1: getK8sObject(`
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: zk-pdb
|
||||
spec:
|
||||
minAvailable: 2`),
|
||||
o2: getK8sObject(`
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: zk-pdb
|
||||
spec:
|
||||
minAvailable: 2`),
|
||||
},
|
||||
{
|
||||
desc: "only maxUnavailable is set",
|
||||
o1: getK8sObject(`
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: istio
|
||||
spec:
|
||||
maxUnavailable: 3`),
|
||||
o2: getK8sObject(`
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: istio
|
||||
spec:
|
||||
maxUnavailable: 3`),
|
||||
},
|
||||
{
|
||||
desc: "minAvailable and maxUnavailable are set to none zero values",
|
||||
o1: getK8sObject(`
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: istio
|
||||
spec:
|
||||
maxUnavailable: 50%
|
||||
minAvailable: 3`),
|
||||
o2: getK8sObject(`
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: istio
|
||||
spec:
|
||||
maxUnavailable: 50%`),
|
||||
},
|
||||
{
|
||||
desc: "both minAvailable and maxUnavailable are set default",
|
||||
o1: getK8sObject(`
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: istio
|
||||
spec:
|
||||
minAvailable: 0
|
||||
maxUnavailable: 0`),
|
||||
o2: getK8sObject(`
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: istio
|
||||
spec:
|
||||
maxUnavailable: 0
|
||||
minAvailable: 0`),
|
||||
},
|
||||
}
|
||||
for _, tt := range cases {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
newObj := tt.o1.ResolveK8sConflict()
|
||||
if !newObj.Equal(tt.o2) {
|
||||
newObjjson, _ := newObj.JSON()
|
||||
wantedObjjson, _ := tt.o2.JSON()
|
||||
t.Errorf("Got: %s, want: %s", string(newObjjson), string(wantedObjjson))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
345
pkg/cmd/hgctl/helm/profile.go
Normal file
345
pkg/cmd/hgctl/helm/profile.go
Normal file
@@ -0,0 +1,345 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package helm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"istio.io/istio/operator/pkg/util"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
type InstallMode string
|
||||
|
||||
const (
|
||||
InstallK8s InstallMode = "k8s"
|
||||
InstallLocalK8s InstallMode = "local-k8s"
|
||||
InstallLocalDocker InstallMode = "local-docker"
|
||||
InstallLocal InstallMode = "local"
|
||||
)
|
||||
|
||||
type Profile struct {
|
||||
Profile string `json:"profile,omitempty"`
|
||||
InstallPackagePath string `json:"installPackagePath,omitempty"`
|
||||
HigressVersion string `json:"higressVersion,omitempty"`
|
||||
Global ProfileGlobal `json:"global,omitempty"`
|
||||
Console ProfileConsole `json:"console,omitempty"`
|
||||
Gateway ProfileGateway `json:"gateway,omitempty"`
|
||||
Controller ProfileController `json:"controller,omitempty"`
|
||||
Storage ProfileStorage `json:"storage,omitempty"`
|
||||
Values map[string]any `json:"values,omitempty"`
|
||||
Charts ProfileCharts `json:"charts,omitempty"`
|
||||
}
|
||||
|
||||
type ProfileGlobal struct {
|
||||
Install InstallMode `json:"install,omitempty"`
|
||||
IngressClass string `json:"ingressClass,omitempty"`
|
||||
EnableIstioAPI bool `json:"enableIstioAPI,omitempty"`
|
||||
EnableGatewayAPI bool `json:"enableGatewayAPI,omitempty"`
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
}
|
||||
|
||||
func (p ProfileGlobal) SetFlags(install InstallMode) ([]string, error) {
|
||||
sets := make([]string, 0)
|
||||
if install == InstallK8s || install == InstallLocalK8s {
|
||||
sets = append(sets, fmt.Sprintf("global.ingressClass=%s", p.IngressClass))
|
||||
sets = append(sets, fmt.Sprintf("global.enableIstioAPI=%t", p.EnableIstioAPI))
|
||||
sets = append(sets, fmt.Sprintf("global.enableGatewayAPI=%t", p.EnableGatewayAPI))
|
||||
if install == InstallLocalK8s {
|
||||
sets = append(sets, fmt.Sprintf("global.local=%t", true))
|
||||
}
|
||||
}
|
||||
return sets, nil
|
||||
}
|
||||
|
||||
func (p ProfileGlobal) Validate(install InstallMode) []error {
|
||||
errs := make([]error, 0)
|
||||
// now only support k8s, local-k8s, local-docker installation mode
|
||||
if install != InstallK8s && install != InstallLocalK8s && install != InstallLocalDocker {
|
||||
errs = append(errs, errors.New("global.install only can be set to k8s, local-k8s or local-docker"))
|
||||
}
|
||||
if install == InstallK8s || install == InstallLocalK8s {
|
||||
if len(p.IngressClass) == 0 {
|
||||
errs = append(errs, errors.New("global.ingressClass can't be empty"))
|
||||
}
|
||||
if len(p.Namespace) == 0 {
|
||||
errs = append(errs, errors.New("global.namespace can't be empty"))
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
type ProfileConsole struct {
|
||||
Port uint32 `json:"port,omitempty"`
|
||||
Replicas uint32 `json:"replicas,omitempty"`
|
||||
O11yEnabled bool `json:"o11YEnabled,omitempty"`
|
||||
}
|
||||
|
||||
func (p ProfileConsole) SetFlags(install InstallMode) ([]string, error) {
|
||||
sets := make([]string, 0)
|
||||
if install == InstallK8s || install == InstallLocalK8s {
|
||||
sets = append(sets, fmt.Sprintf("higress-console.replicaCount=%d", p.Replicas))
|
||||
sets = append(sets, fmt.Sprintf("higress-console.o11y.enabled=%t", p.O11yEnabled))
|
||||
}
|
||||
return sets, nil
|
||||
}
|
||||
|
||||
func (p ProfileConsole) Validate(install InstallMode) []error {
|
||||
errs := make([]error, 0)
|
||||
if install == InstallK8s || install == InstallLocalK8s {
|
||||
if p.Replicas <= 0 {
|
||||
errs = append(errs, errors.New("console.replica need be large than zero"))
|
||||
}
|
||||
}
|
||||
|
||||
if install == InstallLocalDocker {
|
||||
if p.Port <= 0 {
|
||||
errs = append(errs, errors.New("console.port need be large than zero"))
|
||||
}
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
type ProfileGateway struct {
|
||||
Replicas uint32 `json:"replicas,omitempty"`
|
||||
HttpPort uint32 `json:"httpPort,omitempty"`
|
||||
HttpsPort uint32 `json:"httpsPort,omitempty"`
|
||||
MetricsPort uint32 `json:"metricsPort,omitempty"`
|
||||
}
|
||||
|
||||
func (p ProfileGateway) SetFlags(install InstallMode) ([]string, error) {
|
||||
sets := make([]string, 0)
|
||||
if install == InstallK8s || install == InstallLocalK8s {
|
||||
sets = append(sets, fmt.Sprintf("higress-core.gateway.replicas=%d", p.Replicas))
|
||||
}
|
||||
return sets, nil
|
||||
}
|
||||
|
||||
func (p ProfileGateway) Validate(install InstallMode) []error {
|
||||
errs := make([]error, 0)
|
||||
if install == InstallK8s || install == InstallLocalK8s {
|
||||
if p.Replicas <= 0 {
|
||||
errs = append(errs, errors.New("gateway.replica need be large than zero"))
|
||||
}
|
||||
}
|
||||
|
||||
if install == InstallLocalDocker {
|
||||
if p.HttpPort <= 0 {
|
||||
errs = append(errs, errors.New("gateway.httpPort need be large than zero"))
|
||||
}
|
||||
if p.HttpsPort <= 0 {
|
||||
errs = append(errs, errors.New("gateway.httpsPort need be large than zero"))
|
||||
}
|
||||
if p.MetricsPort <= 0 {
|
||||
errs = append(errs, errors.New("gateway.MetricsPort need be large than zero"))
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
type ProfileController struct {
|
||||
Replicas uint32 `json:"replicas,omitempty"`
|
||||
}
|
||||
|
||||
func (p ProfileController) SetFlags(install InstallMode) ([]string, error) {
|
||||
sets := make([]string, 0)
|
||||
if install == InstallK8s || install == InstallLocalK8s {
|
||||
sets = append(sets, fmt.Sprintf("higress-core.controller.replicas=%d", p.Replicas))
|
||||
}
|
||||
return sets, nil
|
||||
}
|
||||
|
||||
func (p ProfileController) Validate(install InstallMode) []error {
|
||||
errs := make([]error, 0)
|
||||
if install == InstallK8s || install == InstallLocalK8s {
|
||||
if p.Replicas <= 0 {
|
||||
errs = append(errs, errors.New("controller.replica need be large than zero"))
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
type ProfileStorage struct {
|
||||
Url string `json:"url,omitempty"`
|
||||
Ns string `json:"ns,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
DataEncKey string `json:"DataEncKey,omitempty"`
|
||||
}
|
||||
|
||||
func (p ProfileStorage) Validate(install InstallMode) []error {
|
||||
errs := make([]error, 0)
|
||||
if install == InstallLocalDocker {
|
||||
if len(p.Url) == 0 {
|
||||
errs = append(errs, errors.New("storage.url can't be empty"))
|
||||
}
|
||||
if len(p.Ns) == 0 {
|
||||
errs = append(errs, errors.New("storage.ns can't be empty"))
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(p.Url, "nacos://") && !strings.HasPrefix(p.Url, "file://") {
|
||||
errs = append(errs, fmt.Errorf("invalid storage url: %s", p.Url))
|
||||
} else {
|
||||
// check localhost or 127.0.0.0
|
||||
if strings.Contains(p.Url, "localhost") || strings.Contains(p.Url, "/127.") {
|
||||
errs = append(errs, errors.New("localhost or loopback addresses in nacos url won't work"))
|
||||
}
|
||||
}
|
||||
|
||||
if len(p.DataEncKey) > 0 && len(p.DataEncKey) != 32 {
|
||||
errs = append(errs, fmt.Errorf("expecting 32 characters for dataEncKey, but got %d length", len(p.DataEncKey)))
|
||||
}
|
||||
|
||||
if len(p.Username) > 0 && len(p.Password) == 0 || len(p.Username) == 0 && len(p.Password) > 0 {
|
||||
errs = append(errs, errors.New("both nacos username and password should be provided"))
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
type Chart struct {
|
||||
Url string `json:"url,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
type ProfileCharts struct {
|
||||
Higress Chart `json:"higress,omitempty"`
|
||||
Standalone Chart `json:"standalone,omitempty"`
|
||||
}
|
||||
|
||||
func (p ProfileCharts) Validate(install InstallMode) []error {
|
||||
errs := make([]error, 0)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func (p *Profile) ValuesYaml() (string, error) {
|
||||
setFlags := make([]string, 0)
|
||||
// Get global setting
|
||||
globalFlags, _ := p.Global.SetFlags(p.Global.Install)
|
||||
setFlags = append(setFlags, globalFlags...)
|
||||
|
||||
// Get console setting
|
||||
consoleFlags, _ := p.Console.SetFlags(p.Global.Install)
|
||||
setFlags = append(setFlags, consoleFlags...)
|
||||
|
||||
// Get gateway setting
|
||||
gatewayFlags, _ := p.Gateway.SetFlags(p.Global.Install)
|
||||
setFlags = append(setFlags, gatewayFlags...)
|
||||
|
||||
// Get controller setting
|
||||
controllerFlags, _ := p.Controller.SetFlags(p.Global.Install)
|
||||
setFlags = append(setFlags, controllerFlags...)
|
||||
|
||||
valueOverlayYAML := ""
|
||||
if p.Values != nil {
|
||||
out, err := yaml.Marshal(p.Values)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
valueOverlayYAML = string(out)
|
||||
}
|
||||
|
||||
flagsYAML, err := overlaySetFlagValues("", setFlags)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// merge values and setFlags
|
||||
overlayYAML, err := util.OverlayYAML(flagsYAML, valueOverlayYAML)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return overlayYAML, nil
|
||||
}
|
||||
|
||||
func (p *Profile) IstioEnabled() bool {
|
||||
if (p.Global.Install == InstallK8s || p.Global.Install == InstallLocalK8s) && p.Global.EnableIstioAPI {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *Profile) GatewayAPIEnabled() bool {
|
||||
if (p.Global.Install == InstallK8s || p.Global.Install == InstallLocalK8s) && p.Global.EnableGatewayAPI {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *Profile) GetIstioNamespace() string {
|
||||
if valuesGlobal, ok1 := p.Values["global"]; ok1 {
|
||||
if global, ok2 := valuesGlobal.(map[string]any); ok2 {
|
||||
if istioNamespace, ok3 := global["istioNamespace"]; ok3 {
|
||||
if namespace, ok4 := istioNamespace.(string); ok4 {
|
||||
return namespace
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (p *Profile) Validate() error {
|
||||
errs := make([]error, 0)
|
||||
errsGlobal := p.Global.Validate(p.Global.Install)
|
||||
if len(errsGlobal) > 0 {
|
||||
errs = append(errs, errsGlobal...)
|
||||
}
|
||||
errsConsole := p.Console.Validate(p.Global.Install)
|
||||
if len(errsConsole) > 0 {
|
||||
errs = append(errs, errsConsole...)
|
||||
}
|
||||
errsGateway := p.Gateway.Validate(p.Global.Install)
|
||||
if len(errsGateway) > 0 {
|
||||
errs = append(errs, errsGateway...)
|
||||
}
|
||||
errsController := p.Controller.Validate(p.Global.Install)
|
||||
if len(errsController) > 0 {
|
||||
errs = append(errs, errsController...)
|
||||
}
|
||||
errsStorage := p.Storage.Validate(p.Global.Install)
|
||||
if len(errsStorage) > 0 {
|
||||
errs = append(errs, errsStorage...)
|
||||
}
|
||||
errsCharts := p.Charts.Validate(p.Global.Install)
|
||||
if len(errsCharts) > 0 {
|
||||
errs = append(errs, errsCharts...)
|
||||
}
|
||||
|
||||
if len(errs) == 0 {
|
||||
return nil
|
||||
}
|
||||
return errors.New(ToString(errs, "\n"))
|
||||
}
|
||||
|
||||
// ToString returns a string representation of errors, with elements separated by separator string. Any nil errors in the
|
||||
// slice are skipped.
|
||||
func ToString(errors []error, separator string) string {
|
||||
var out string
|
||||
for i, e := range errors {
|
||||
if e == nil {
|
||||
continue
|
||||
}
|
||||
if i != 0 {
|
||||
out += separator
|
||||
}
|
||||
out += e.Error()
|
||||
}
|
||||
return out
|
||||
}
|
||||
685
pkg/cmd/hgctl/helm/render.go
Normal file
685
pkg/cmd/hgctl/helm/render.go
Normal file
@@ -0,0 +1,685 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package helm
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/manifests"
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/util"
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chart/loader"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/cli"
|
||||
"helm.sh/helm/v3/pkg/downloader"
|
||||
"helm.sh/helm/v3/pkg/engine"
|
||||
"helm.sh/helm/v3/pkg/getter"
|
||||
"helm.sh/helm/v3/pkg/repo"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultProfileName is the name of the default profile for installation.
|
||||
DefaultProfileName = "local-k8s"
|
||||
// DefaultProfileFilename is the name of the default profile yaml file for installation.
|
||||
DefaultProfileFilename = "local-k8s.yaml"
|
||||
// DefaultUninstallProfileName is the name of the default profile yaml file for uninstallation.
|
||||
DefaultUninstallProfileName = "local-k8s"
|
||||
|
||||
// ChartsSubdirName = "charts"
|
||||
profilesRoot = "profiles"
|
||||
|
||||
RepoLatestVersion = "latest"
|
||||
RepoChartIndexYamlHigressIndex = "higress"
|
||||
|
||||
YAMLSeparator = "\n---\n"
|
||||
NotesFileNameSuffix = ".txt"
|
||||
)
|
||||
|
||||
func LoadValues(profileName string, chartsDir string) (string, error) {
|
||||
path := strings.Join([]string{profilesRoot, builtinProfileToFilename(profileName)}, "/")
|
||||
by, err := fs.ReadFile(manifests.BuiltinOrDir(chartsDir), path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(by), nil
|
||||
}
|
||||
|
||||
func readProfiles(chartsDir string) (map[string]bool, error) {
|
||||
profiles := map[string]bool{}
|
||||
f := manifests.BuiltinOrDir(chartsDir)
|
||||
dir, err := fs.ReadDir(f, profilesRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, f := range dir {
|
||||
if f.Name() == "_all.yaml" {
|
||||
continue
|
||||
}
|
||||
trimmedString := strings.TrimSuffix(f.Name(), ".yaml")
|
||||
if f.Name() != trimmedString {
|
||||
profiles[trimmedString] = true
|
||||
}
|
||||
}
|
||||
return profiles, nil
|
||||
}
|
||||
|
||||
func builtinProfileToFilename(name string) string {
|
||||
if name == "" {
|
||||
return DefaultProfileFilename
|
||||
}
|
||||
return name + ".yaml"
|
||||
}
|
||||
|
||||
// stripPrefix removes the given prefix from prefix.
|
||||
func stripPrefix(path, prefix string) string {
|
||||
pl := len(strings.Split(prefix, "/"))
|
||||
pv := strings.Split(path, "/")
|
||||
return strings.Join(pv[pl:], "/")
|
||||
}
|
||||
|
||||
// ListProfiles list all the profiles.
|
||||
func ListProfiles(charts string) ([]string, error) {
|
||||
profiles, err := readProfiles(charts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return util.StringBoolMapToSlice(profiles), nil
|
||||
}
|
||||
|
||||
var DefaultFilters = []util.FilterFunc{
|
||||
util.LicenseFilter,
|
||||
util.FormatterFilter,
|
||||
util.SpaceFilter,
|
||||
}
|
||||
|
||||
// Renderer is responsible for rendering helm chart with new values.
|
||||
type Renderer interface {
|
||||
Init() error
|
||||
RenderManifest(valsYaml string) (string, error)
|
||||
SetVersion(version string)
|
||||
}
|
||||
|
||||
type RendererOptions struct {
|
||||
Name string
|
||||
Namespace string
|
||||
|
||||
// fields for LocalChartRenderer and LocalFileRenderer
|
||||
FS fs.FS
|
||||
Dir string
|
||||
|
||||
// fields for RemoteRenderer
|
||||
Version string
|
||||
RepoURL string
|
||||
|
||||
// Capabilities
|
||||
Capabilities *chartutil.Capabilities
|
||||
|
||||
// rest config
|
||||
restConfig *rest.Config
|
||||
}
|
||||
|
||||
type RendererOption func(*RendererOptions)
|
||||
|
||||
func WithName(name string) RendererOption {
|
||||
return func(opts *RendererOptions) {
|
||||
opts.Name = name
|
||||
}
|
||||
}
|
||||
|
||||
func WithNamespace(ns string) RendererOption {
|
||||
return func(opts *RendererOptions) {
|
||||
opts.Namespace = ns
|
||||
}
|
||||
}
|
||||
|
||||
func WithFS(f fs.FS) RendererOption {
|
||||
return func(opts *RendererOptions) {
|
||||
opts.FS = f
|
||||
}
|
||||
}
|
||||
|
||||
func WithDir(dir string) RendererOption {
|
||||
return func(opts *RendererOptions) {
|
||||
opts.Dir = dir
|
||||
}
|
||||
}
|
||||
|
||||
func WithVersion(version string) RendererOption {
|
||||
return func(opts *RendererOptions) {
|
||||
opts.Version = version
|
||||
}
|
||||
}
|
||||
|
||||
func WithRepoURL(repo string) RendererOption {
|
||||
return func(opts *RendererOptions) {
|
||||
opts.RepoURL = repo
|
||||
}
|
||||
}
|
||||
|
||||
func WithCapabilities(capabilities *chartutil.Capabilities) RendererOption {
|
||||
return func(opts *RendererOptions) {
|
||||
opts.Capabilities = capabilities
|
||||
}
|
||||
}
|
||||
|
||||
func WithRestConfig(config *rest.Config) RendererOption {
|
||||
return func(opts *RendererOptions) {
|
||||
opts.restConfig = config
|
||||
}
|
||||
}
|
||||
|
||||
// LocalFileRenderer load yaml files from local file system
|
||||
type LocalFileRenderer struct {
|
||||
Opts *RendererOptions
|
||||
filesMap map[string]string
|
||||
Started bool
|
||||
}
|
||||
|
||||
func NewLocalFileRenderer(opts ...RendererOption) (Renderer, error) {
|
||||
newOpts := &RendererOptions{}
|
||||
for _, opt := range opts {
|
||||
opt(newOpts)
|
||||
}
|
||||
|
||||
return &LocalFileRenderer{
|
||||
Opts: newOpts,
|
||||
filesMap: make(map[string]string),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *LocalFileRenderer) Init() error {
|
||||
fileNames, err := getFileNames(l.Opts.FS, l.Opts.Dir)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return fmt.Errorf("chart of component %s doesn't exist", l.Opts.Name)
|
||||
}
|
||||
return fmt.Errorf("getFileNames err: %s", err)
|
||||
}
|
||||
for _, fileName := range fileNames {
|
||||
data, err := fs.ReadFile(l.Opts.FS, fileName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ReadFile %s err: %s", fileName, err)
|
||||
}
|
||||
|
||||
l.filesMap[fileName] = string(data)
|
||||
}
|
||||
l.Started = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *LocalFileRenderer) RenderManifest(valsYaml string) (string, error) {
|
||||
if !l.Started {
|
||||
return "", errors.New("LocalFileRenderer has not been init")
|
||||
}
|
||||
keys := make([]string, 0, len(l.filesMap))
|
||||
for key := range l.filesMap {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
// to ensure that every manifest rendered by same values are the same
|
||||
sort.Strings(keys)
|
||||
|
||||
var builder strings.Builder
|
||||
for i := 0; i < len(keys); i++ {
|
||||
file := l.filesMap[keys[i]]
|
||||
file = util.ApplyFilters(file, DefaultFilters...)
|
||||
// ignore empty manifest
|
||||
if file == "" {
|
||||
continue
|
||||
}
|
||||
if !strings.HasSuffix(file, YAMLSeparator) {
|
||||
file += YAMLSeparator
|
||||
}
|
||||
builder.WriteString(file)
|
||||
}
|
||||
return builder.String(), nil
|
||||
}
|
||||
|
||||
func (l *LocalFileRenderer) SetVersion(version string) {
|
||||
l.Opts.Version = version
|
||||
}
|
||||
|
||||
// LocalChartRenderer load chart from local file system
|
||||
type LocalChartRenderer struct {
|
||||
Opts *RendererOptions
|
||||
Chart *chart.Chart
|
||||
Started bool
|
||||
}
|
||||
|
||||
func (lr *LocalChartRenderer) Init() error {
|
||||
fileNames, err := getFileNames(lr.Opts.FS, lr.Opts.Dir)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return fmt.Errorf("chart of component %s doesn't exist", lr.Opts.Name)
|
||||
}
|
||||
return fmt.Errorf("getFileNames err: %s", err)
|
||||
}
|
||||
var files []*loader.BufferedFile
|
||||
for _, fileName := range fileNames {
|
||||
data, err := fs.ReadFile(lr.Opts.FS, fileName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ReadFile %s err: %s", fileName, err)
|
||||
}
|
||||
// todo:// explain why we need to do this
|
||||
name := util.StripPrefix(fileName, lr.Opts.Dir)
|
||||
file := &loader.BufferedFile{
|
||||
Name: name,
|
||||
Data: data,
|
||||
}
|
||||
files = append(files, file)
|
||||
}
|
||||
newChart, err := loader.LoadFiles(files)
|
||||
if err != nil {
|
||||
return fmt.Errorf("load chart of component %s err: %s", lr.Opts.Name, err)
|
||||
}
|
||||
lr.Chart = newChart
|
||||
lr.Started = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (lr *LocalChartRenderer) RenderManifest(valsYaml string) (string, error) {
|
||||
if !lr.Started {
|
||||
return "", errors.New("LocalChartRenderer has not been init")
|
||||
}
|
||||
return renderManifest(valsYaml, lr.Chart, true, lr.Opts, DefaultFilters...)
|
||||
}
|
||||
|
||||
func (lr *LocalChartRenderer) SetVersion(version string) {
|
||||
lr.Opts.Version = version
|
||||
}
|
||||
|
||||
func NewLocalChartRenderer(opts ...RendererOption) (Renderer, error) {
|
||||
newOpts := &RendererOptions{}
|
||||
for _, opt := range opts {
|
||||
opt(newOpts)
|
||||
}
|
||||
|
||||
if err := verifyRendererOptions(newOpts); err != nil {
|
||||
return nil, fmt.Errorf("verify err: %s", err)
|
||||
}
|
||||
return &LocalChartRenderer{
|
||||
Opts: newOpts,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type RemoteRenderer struct {
|
||||
Opts *RendererOptions
|
||||
Chart *chart.Chart
|
||||
Started bool
|
||||
}
|
||||
|
||||
func (rr *RemoteRenderer) initChartPathOptions() *action.ChartPathOptions {
|
||||
return &action.ChartPathOptions{
|
||||
RepoURL: rr.Opts.RepoURL,
|
||||
Version: rr.Opts.Version,
|
||||
}
|
||||
}
|
||||
|
||||
func (rr *RemoteRenderer) Init() error {
|
||||
cpOpts := rr.initChartPathOptions()
|
||||
settings := cli.New()
|
||||
// using release name as chart name by default
|
||||
cp, err := locateChart(cpOpts, rr.Opts.Name, settings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check chart dependencies to make sure all are present in /charts
|
||||
chartRequested, err := loader.Load(cp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := verifyInstallable(chartRequested); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rr.Chart = chartRequested
|
||||
rr.Started = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rr *RemoteRenderer) SetVersion(version string) {
|
||||
rr.Opts.Version = version
|
||||
}
|
||||
|
||||
func (rr *RemoteRenderer) RenderManifest(valsYaml string) (string, error) {
|
||||
if !rr.Started {
|
||||
return "", errors.New("RemoteRenderer has not been init")
|
||||
}
|
||||
return renderManifest(valsYaml, rr.Chart, false, rr.Opts, DefaultFilters...)
|
||||
}
|
||||
|
||||
func NewRemoteRenderer(opts ...RendererOption) (Renderer, error) {
|
||||
newOpts := &RendererOptions{}
|
||||
for _, opt := range opts {
|
||||
opt(newOpts)
|
||||
}
|
||||
|
||||
return &RemoteRenderer{
|
||||
Opts: newOpts,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func verifyRendererOptions(opts *RendererOptions) error {
|
||||
if opts.Name == "" {
|
||||
return errors.New("missing component name for Renderer")
|
||||
}
|
||||
if opts.Namespace == "" {
|
||||
return errors.New("missing component namespace for Renderer")
|
||||
}
|
||||
if opts.FS == nil {
|
||||
return errors.New("missing chart FS for Renderer")
|
||||
}
|
||||
if opts.Dir == "" {
|
||||
return errors.New("missing chart dir for Renderer")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// read all files recursively under root path from a certain local file system
|
||||
func getFileNames(f fs.FS, root string) ([]string, error) {
|
||||
var fileNames []string
|
||||
if err := fs.WalkDir(f, root, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d.IsDir() {
|
||||
return nil
|
||||
}
|
||||
fileNames = append(fileNames, path)
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return fileNames, nil
|
||||
}
|
||||
|
||||
func verifyInstallable(cht *chart.Chart) error {
|
||||
typ := cht.Metadata.Type
|
||||
if typ == "" || typ == "application" {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("%s chart %s is not installable", typ, cht.Name())
|
||||
}
|
||||
|
||||
func renderManifest(valsYaml string, cht *chart.Chart, builtIn bool, opts *RendererOptions, filters ...util.FilterFunc) (string, error) {
|
||||
valsMap := make(map[string]any)
|
||||
if err := yaml.Unmarshal([]byte(valsYaml), &valsMap); err != nil {
|
||||
return "", fmt.Errorf("unmarshal failed err: %s", err)
|
||||
}
|
||||
RelOpts := chartutil.ReleaseOptions{
|
||||
Name: opts.Name,
|
||||
Namespace: opts.Namespace,
|
||||
}
|
||||
var caps *chartutil.Capabilities
|
||||
caps = opts.Capabilities
|
||||
if caps == nil {
|
||||
caps = chartutil.DefaultCapabilities
|
||||
}
|
||||
// maybe we need a configuration to change this caps
|
||||
resVals, err := chartutil.ToRenderValues(cht, valsMap, RelOpts, caps)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("ToRenderValues failed err: %s", err)
|
||||
}
|
||||
if builtIn {
|
||||
resVals["Values"].(chartutil.Values)["enabled"] = true
|
||||
}
|
||||
filesMap, err := engine.RenderWithClient(cht, resVals, opts.restConfig)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Render chart failed err: %s", err)
|
||||
}
|
||||
keys := make([]string, 0, len(filesMap))
|
||||
for key := range filesMap {
|
||||
// remove notation files such as Notes.txt
|
||||
if strings.HasSuffix(key, NotesFileNameSuffix) {
|
||||
continue
|
||||
}
|
||||
keys = append(keys, key)
|
||||
}
|
||||
// to ensure that every manifest rendered by same values are the same
|
||||
sort.Strings(keys)
|
||||
|
||||
var builder strings.Builder
|
||||
for i := 0; i < len(keys); i++ {
|
||||
file := filesMap[keys[i]]
|
||||
file = util.ApplyFilters(file, filters...)
|
||||
// ignore empty manifest
|
||||
if file == "" {
|
||||
continue
|
||||
}
|
||||
if !strings.HasSuffix(file, YAMLSeparator) {
|
||||
file += YAMLSeparator
|
||||
}
|
||||
builder.WriteString(file)
|
||||
}
|
||||
|
||||
// render CRD
|
||||
crdFiles := cht.CRDObjects()
|
||||
// Sort crd files by name to ensure stable manifest output
|
||||
sort.Slice(crdFiles, func(i, j int) bool { return crdFiles[i].Name < crdFiles[j].Name })
|
||||
for _, crdFile := range crdFiles {
|
||||
f := string(crdFile.File.Data)
|
||||
// add yaml separator if the rendered file doesn't have one at the end
|
||||
f = strings.TrimSpace(f) + "\n"
|
||||
if !strings.HasSuffix(f, YAMLSeparator) {
|
||||
f += YAMLSeparator
|
||||
}
|
||||
builder.WriteString(f)
|
||||
}
|
||||
|
||||
return builder.String(), nil
|
||||
}
|
||||
|
||||
// locateChart locate the target chart path by sequential orders:
|
||||
// 1. find local helm repository using "name-version.tgz" format
|
||||
// 2. using downloader to pull remote chart
|
||||
func locateChart(cpOpts *action.ChartPathOptions, name string, settings *cli.EnvSettings) (string, error) {
|
||||
name = strings.TrimSpace(name)
|
||||
version := strings.TrimSpace(cpOpts.Version)
|
||||
|
||||
// check if it's in Helm's chart cache
|
||||
// cacheName is hardcoded as format of helm. eg: grafana-6.31.1.tgz
|
||||
cacheName := name + "-" + cpOpts.Version + ".tgz"
|
||||
cachePath := path.Join(settings.RepositoryCache, cacheName)
|
||||
if _, err := os.Stat(cachePath); err == nil {
|
||||
abs, err := filepath.Abs(cachePath)
|
||||
if err != nil {
|
||||
return abs, err
|
||||
}
|
||||
if cpOpts.Verify {
|
||||
if _, err := downloader.VerifyChart(abs, cpOpts.Keyring); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return abs, nil
|
||||
}
|
||||
|
||||
dl := downloader.ChartDownloader{
|
||||
Out: os.Stdout,
|
||||
Keyring: cpOpts.Keyring,
|
||||
Getters: getter.All(settings),
|
||||
Options: []getter.Option{
|
||||
getter.WithPassCredentialsAll(cpOpts.PassCredentialsAll),
|
||||
getter.WithTLSClientConfig(cpOpts.CertFile, cpOpts.KeyFile, cpOpts.CaFile),
|
||||
getter.WithInsecureSkipVerifyTLS(cpOpts.InsecureSkipTLSverify),
|
||||
},
|
||||
RepositoryConfig: settings.RepositoryConfig,
|
||||
RepositoryCache: settings.RepositoryCache,
|
||||
}
|
||||
|
||||
if cpOpts.Verify {
|
||||
dl.Verify = downloader.VerifyAlways
|
||||
}
|
||||
if cpOpts.RepoURL != "" {
|
||||
chartURL, err := repo.FindChartInAuthAndTLSAndPassRepoURL(cpOpts.RepoURL, cpOpts.Username, cpOpts.Password, name, version,
|
||||
cpOpts.CertFile, cpOpts.KeyFile, cpOpts.CaFile, cpOpts.InsecureSkipTLSverify, cpOpts.PassCredentialsAll, getter.All(settings))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
name = chartURL
|
||||
|
||||
// Only pass the user/pass on when the user has said to or when the
|
||||
// location of the chart repo and the chart are the same domain.
|
||||
u1, err := url.Parse(cpOpts.RepoURL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
u2, err := url.Parse(chartURL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Host on URL (returned from url.Parse) contains the port if present.
|
||||
// This check ensures credentials are not passed between different
|
||||
// services on different ports.
|
||||
if cpOpts.PassCredentialsAll || (u1.Scheme == u2.Scheme && u1.Host == u2.Host) {
|
||||
dl.Options = append(dl.Options, getter.WithBasicAuth(cpOpts.Username, cpOpts.Password))
|
||||
} else {
|
||||
dl.Options = append(dl.Options, getter.WithBasicAuth("", ""))
|
||||
}
|
||||
} else {
|
||||
dl.Options = append(dl.Options, getter.WithBasicAuth(cpOpts.Username, cpOpts.Password))
|
||||
}
|
||||
|
||||
// if RepositoryCache doesn't exist, create it
|
||||
if err := os.MkdirAll(settings.RepositoryCache, 0o755); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
filename, _, err := dl.DownloadTo(name, version, settings.RepositoryCache)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
fileAbsPath, err := filepath.Abs(filename)
|
||||
if err != nil {
|
||||
return filename, err
|
||||
}
|
||||
return fileAbsPath, nil
|
||||
}
|
||||
|
||||
func ParseLatestVersion(repoUrl string, version string) (string, error) {
|
||||
|
||||
cpOpts := &action.ChartPathOptions{
|
||||
RepoURL: repoUrl,
|
||||
Version: version,
|
||||
}
|
||||
settings := cli.New()
|
||||
|
||||
indexURL, err := repo.ResolveReferenceURL(repoUrl, "index.yaml")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
u, err := url.Parse(repoUrl)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("invalid chart URL format: %s", repoUrl)
|
||||
}
|
||||
|
||||
client, err := getter.All(settings).ByScheme(u.Scheme)
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not find protocol handler for: %s", u.Scheme)
|
||||
}
|
||||
|
||||
resp, err := client.Get(indexURL,
|
||||
getter.WithURL(cpOpts.RepoURL),
|
||||
getter.WithInsecureSkipVerifyTLS(cpOpts.InsecureSkipTLSverify),
|
||||
getter.WithTLSClientConfig(cpOpts.CertFile, cpOpts.KeyFile, cpOpts.CaFile),
|
||||
getter.WithBasicAuth(cpOpts.Username, cpOpts.Password),
|
||||
getter.WithPassCredentialsAll(cpOpts.PassCredentialsAll),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
index, err := io.ReadAll(resp)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
indexFile, err := loadIndex(index)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// get higress helm chart latest version
|
||||
if entries, ok := indexFile.Entries[RepoChartIndexYamlHigressIndex]; ok {
|
||||
return entries[0].AppVersion, nil
|
||||
}
|
||||
|
||||
return "", errors.New("can't find higress latest version")
|
||||
}
|
||||
|
||||
// loadIndex loads an index file and does minimal validity checking.
|
||||
//
|
||||
// The source parameter is only used for logging.
|
||||
// This will fail if API Version is not set (ErrNoAPIVersion) or if the unmarshal fails.
|
||||
func loadIndex(data []byte) (*repo.IndexFile, error) {
|
||||
i := &repo.IndexFile{}
|
||||
if len(data) == 0 {
|
||||
return i, errors.New("empty index.yaml file")
|
||||
}
|
||||
if err := jsonOrYamlUnmarshal(data, i); err != nil {
|
||||
return i, err
|
||||
}
|
||||
for _, cvs := range i.Entries {
|
||||
for idx := len(cvs) - 1; idx >= 0; idx-- {
|
||||
if cvs[idx] == nil {
|
||||
continue
|
||||
}
|
||||
if cvs[idx].APIVersion == "" {
|
||||
cvs[idx].APIVersion = chart.APIVersionV1
|
||||
}
|
||||
if err := cvs[idx].Validate(); err != nil {
|
||||
cvs = append(cvs[:idx], cvs[idx+1:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
i.SortEntries()
|
||||
if i.APIVersion == "" {
|
||||
return i, errors.New("no API version specified")
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
// jsonOrYamlUnmarshal unmarshals the given byte slice containing JSON or YAML
|
||||
// into the provided interface.
|
||||
//
|
||||
// It automatically detects whether the data is in JSON or YAML format by
|
||||
// checking its validity as JSON. If the data is valid JSON, it will use the
|
||||
// `encoding/json` package to unmarshal it. Otherwise, it will use the
|
||||
// `sigs.k8s.io/yaml` package to unmarshal the YAML data.
|
||||
func jsonOrYamlUnmarshal(b []byte, i interface{}) error {
|
||||
if json.Valid(b) {
|
||||
return json.Unmarshal(b, i)
|
||||
}
|
||||
return yaml.UnmarshalStrict(b, i)
|
||||
}
|
||||
548
pkg/cmd/hgctl/helm/tpath/tree.go
Normal file
548
pkg/cmd/hgctl/helm/tpath/tree.go
Normal file
@@ -0,0 +1,548 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tpath
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/util"
|
||||
"gopkg.in/yaml.v2"
|
||||
yaml2 "sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
// PathContext provides a means for traversing a tree towards the root.
|
||||
type PathContext struct {
|
||||
// Parent in the Parent of this PathContext.
|
||||
Parent *PathContext
|
||||
// KeyToChild is the key required to reach the child.
|
||||
KeyToChild any
|
||||
// Node is the actual Node in the data tree.
|
||||
Node any
|
||||
}
|
||||
|
||||
// String implements the Stringer interface.
|
||||
func (nc *PathContext) String() string {
|
||||
ret := "\n--------------- NodeContext ------------------\n"
|
||||
if nc.Parent != nil {
|
||||
ret += fmt.Sprintf("Parent.Node=\n%s\n", nc.Parent.Node)
|
||||
ret += fmt.Sprintf("KeyToChild=%v\n", nc.Parent.KeyToChild)
|
||||
}
|
||||
|
||||
ret += fmt.Sprintf("Node=\n%s\n", nc.Node)
|
||||
ret += "----------------------------------------------\n"
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// GetPathContext returns the PathContext for the Node which has the given path from root.
|
||||
// It returns false and no error if the given path is not found, or an error code in other error situations, like
|
||||
// a malformed path.
|
||||
// It also creates a tree of PathContexts during the traversal so that Parent nodes can be updated if required. This is
|
||||
// required when (say) appending to a list, where the parent list itself must be updated.
|
||||
func GetPathContext(root any, path util.Path, createMissing bool) (*PathContext, bool, error) {
|
||||
return getPathContext(&PathContext{Node: root}, path, path, createMissing)
|
||||
}
|
||||
|
||||
// WritePathContext writes the given value to the Node in the given PathContext.
|
||||
func WritePathContext(nc *PathContext, value any, merge bool) error {
|
||||
|
||||
if !util.IsValueNil(value) {
|
||||
return setPathContext(nc, value, merge)
|
||||
}
|
||||
|
||||
if nc.Parent == nil {
|
||||
return errors.New("cannot delete root element")
|
||||
}
|
||||
|
||||
switch {
|
||||
case isSliceOrPtrInterface(nc.Parent.Node):
|
||||
if err := util.DeleteFromSlicePtr(nc.Parent.Node, nc.Parent.KeyToChild.(int)); err != nil {
|
||||
return err
|
||||
}
|
||||
if isMapOrInterface(nc.Parent.Parent.Node) {
|
||||
return util.InsertIntoMap(nc.Parent.Parent.Node, nc.Parent.Parent.KeyToChild, nc.Parent.Node)
|
||||
}
|
||||
// TODO: The case of deleting a list.list.node element is not currently supported.
|
||||
return fmt.Errorf("cannot delete path: unsupported parent.parent type %T for delete", nc.Parent.Parent.Node)
|
||||
case util.IsMap(nc.Parent.Node):
|
||||
return util.DeleteFromMap(nc.Parent.Node, nc.Parent.KeyToChild)
|
||||
default:
|
||||
}
|
||||
return fmt.Errorf("cannot delete path: unsupported parent type %T for delete", nc.Parent.Node)
|
||||
}
|
||||
|
||||
// WriteNode writes value to the tree in root at the given path, creating any required missing internal nodes in path.
|
||||
func WriteNode(root any, path util.Path, value any) error {
|
||||
pc, _, err := getPathContext(&PathContext{Node: root}, path, path, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return WritePathContext(pc, value, false)
|
||||
}
|
||||
|
||||
// MergeNode merges value to the tree in root at the given path, creating any required missing internal nodes in path.
|
||||
func MergeNode(root any, path util.Path, value any) error {
|
||||
pc, _, err := getPathContext(&PathContext{Node: root}, path, path, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return WritePathContext(pc, value, true)
|
||||
}
|
||||
|
||||
// Find returns the value at path from the given tree, or false if the path does not exist.
|
||||
// It behaves differently from GetPathContext in that it never creates map entries at the leaf and does not provide
|
||||
// a way to mutate the parent of the found node.
|
||||
func Find(inputTree map[string]any, path util.Path) (any, bool, error) {
|
||||
if len(path) == 0 {
|
||||
return nil, false, fmt.Errorf("path is empty")
|
||||
}
|
||||
node, found := find(inputTree, path)
|
||||
return node, found, nil
|
||||
}
|
||||
|
||||
// Delete sets value at path of input untyped tree to nil
|
||||
func Delete(root map[string]any, path util.Path) (bool, error) {
|
||||
pc, _, err := getPathContext(&PathContext{Node: root}, path, path, false)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, WritePathContext(pc, nil, false)
|
||||
}
|
||||
|
||||
// getPathContext is the internal implementation of GetPathContext.
|
||||
// If createMissing is true, it creates any missing map (but NOT list) path entries in root.
|
||||
func getPathContext(nc *PathContext, fullPath, remainPath util.Path, createMissing bool) (*PathContext, bool, error) {
|
||||
if len(remainPath) == 0 {
|
||||
return nc, true, nil
|
||||
}
|
||||
pe := remainPath[0]
|
||||
|
||||
if nc.Node == nil {
|
||||
if !createMissing {
|
||||
return nil, false, fmt.Errorf("node %s is zero", pe)
|
||||
}
|
||||
if util.IsNPathElement(pe) || util.IsKVPathElement(pe) {
|
||||
nc.Node = []any{}
|
||||
} else {
|
||||
nc.Node = make(map[string]any)
|
||||
}
|
||||
}
|
||||
|
||||
v := reflect.ValueOf(nc.Node)
|
||||
if v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface {
|
||||
v = v.Elem()
|
||||
}
|
||||
ncNode := v.Interface()
|
||||
|
||||
// For list types, we need a key to identify the selected list item. This can be either a value key of the
|
||||
// form :matching_value in the case of a leaf list, or a matching key:value in the case of a non-leaf list.
|
||||
if lst, ok := ncNode.([]any); ok {
|
||||
// If the path element has the form [N], a list element is being selected by index. Return the element at index
|
||||
// N if it exists.
|
||||
if util.IsNPathElement(pe) {
|
||||
idx, err := util.PathN(pe)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("path %s, index %s: %s", fullPath, pe, err)
|
||||
}
|
||||
var foundNode any
|
||||
if idx >= len(lst) || idx < 0 {
|
||||
if !createMissing {
|
||||
return nil, false, fmt.Errorf("index %d exceeds list length %d at path %s", idx, len(lst), remainPath)
|
||||
}
|
||||
idx = len(lst)
|
||||
foundNode = make(map[string]any)
|
||||
} else {
|
||||
foundNode = lst[idx]
|
||||
}
|
||||
nn := &PathContext{
|
||||
Parent: nc,
|
||||
Node: foundNode,
|
||||
}
|
||||
nc.KeyToChild = idx
|
||||
return getPathContext(nn, fullPath, remainPath[1:], createMissing)
|
||||
}
|
||||
|
||||
// Otherwise the path element must have form [key:value]. In this case, go through all list elements, which
|
||||
// must have map type, and try to find one which has a matching key:value.
|
||||
for idx, le := range lst {
|
||||
// non-leaf list, expect to match item by key:value.
|
||||
if lm, ok := le.(map[any]any); ok {
|
||||
k, v, err := util.PathKV(pe)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("path %s: %s", fullPath, err)
|
||||
}
|
||||
if stringsEqual(lm[k], v) {
|
||||
nn := &PathContext{
|
||||
Parent: nc,
|
||||
Node: lm,
|
||||
}
|
||||
nc.KeyToChild = idx
|
||||
nn.KeyToChild = k
|
||||
if len(remainPath) == 1 {
|
||||
return nn, true, nil
|
||||
}
|
||||
return getPathContext(nn, fullPath, remainPath[1:], createMissing)
|
||||
}
|
||||
continue
|
||||
}
|
||||
// repeat of the block above for the case where tree unmarshals to map[string]interface{}. There doesn't
|
||||
// seem to be a way to merge this case into the above block.
|
||||
if lm, ok := le.(map[string]any); ok {
|
||||
k, v, err := util.PathKV(pe)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("path %s: %s", fullPath, err)
|
||||
}
|
||||
if stringsEqual(lm[k], v) {
|
||||
nn := &PathContext{
|
||||
Parent: nc,
|
||||
Node: lm,
|
||||
}
|
||||
nc.KeyToChild = idx
|
||||
nn.KeyToChild = k
|
||||
if len(remainPath) == 1 {
|
||||
return nn, true, nil
|
||||
}
|
||||
return getPathContext(nn, fullPath, remainPath[1:], createMissing)
|
||||
}
|
||||
continue
|
||||
}
|
||||
// leaf list, expect path element [V], match based on value V.
|
||||
v, err := util.PathV(pe)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("path %s: %s", fullPath, err)
|
||||
}
|
||||
if matchesRegex(v, le) {
|
||||
nn := &PathContext{
|
||||
Parent: nc,
|
||||
Node: le,
|
||||
}
|
||||
nc.KeyToChild = idx
|
||||
return getPathContext(nn, fullPath, remainPath[1:], createMissing)
|
||||
}
|
||||
}
|
||||
return nil, false, fmt.Errorf("path %s: element %s not found", fullPath, pe)
|
||||
}
|
||||
|
||||
if util.IsMap(ncNode) {
|
||||
var nn any
|
||||
if m, ok := ncNode.(map[any]any); ok {
|
||||
nn, ok = m[pe]
|
||||
if !ok {
|
||||
// remainPath == 1 means the patch is creation of a new leaf.
|
||||
if createMissing || len(remainPath) == 1 {
|
||||
m[pe] = make(map[any]any)
|
||||
nn = m[pe]
|
||||
} else {
|
||||
return nil, false, fmt.Errorf("path not found at element %s in path %s", pe, fullPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
if reflect.ValueOf(ncNode).IsNil() {
|
||||
ncNode = make(map[string]any)
|
||||
nc.Node = ncNode
|
||||
}
|
||||
if m, ok := ncNode.(map[string]any); ok {
|
||||
nn, ok = m[pe]
|
||||
if !ok {
|
||||
// remainPath == 1 means the patch is creation of a new leaf.
|
||||
if createMissing || len(remainPath) == 1 {
|
||||
nextElementNPath := len(remainPath) > 1 && util.IsNPathElement(remainPath[1])
|
||||
if nextElementNPath {
|
||||
m[pe] = make([]any, 0)
|
||||
} else {
|
||||
m[pe] = make(map[string]any)
|
||||
}
|
||||
nn = m[pe]
|
||||
} else {
|
||||
return nil, false, fmt.Errorf("path not found at element %s in path %s", pe, fullPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
npc := &PathContext{
|
||||
Parent: nc,
|
||||
Node: nn,
|
||||
}
|
||||
// for slices, use the address so that the slice can be mutated.
|
||||
if util.IsSlice(nn) {
|
||||
npc.Node = &nn
|
||||
}
|
||||
nc.KeyToChild = pe
|
||||
return getPathContext(npc, fullPath, remainPath[1:], createMissing)
|
||||
}
|
||||
|
||||
return nil, false, fmt.Errorf("leaf type %T in non-leaf Node %s", nc.Node, remainPath)
|
||||
}
|
||||
|
||||
// setPathContext writes the given value to the Node in the given PathContext,
|
||||
// enlarging all PathContext lists to ensure all indexes are valid.
|
||||
func setPathContext(nc *PathContext, value any, merge bool) error {
|
||||
processParent, err := setValueContext(nc, value, merge)
|
||||
if err != nil || !processParent {
|
||||
return err
|
||||
}
|
||||
|
||||
// If the path included insertions, process them now
|
||||
if nc.Parent.Parent == nil {
|
||||
return nil
|
||||
}
|
||||
return setPathContext(nc.Parent, nc.Parent.Node, false) // note: tail recursive
|
||||
}
|
||||
|
||||
// setValueContext writes the given value to the Node in the given PathContext.
|
||||
// If setting the value requires growing the final slice, grows it.
|
||||
func setValueContext(nc *PathContext, value any, merge bool) (bool, error) {
|
||||
if nc.Parent == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
vv, mapFromString := tryToUnmarshalStringToYAML(value)
|
||||
|
||||
switch parentNode := nc.Parent.Node.(type) {
|
||||
case *any:
|
||||
switch vParentNode := (*parentNode).(type) {
|
||||
case []any:
|
||||
idx := nc.Parent.KeyToChild.(int)
|
||||
if idx == -1 {
|
||||
// Treat -1 as insert-at-end of list
|
||||
idx = len(vParentNode)
|
||||
}
|
||||
|
||||
if idx >= len(vParentNode) {
|
||||
newElements := make([]any, idx-len(vParentNode)+1)
|
||||
vParentNode = append(vParentNode, newElements...)
|
||||
*parentNode = vParentNode
|
||||
}
|
||||
|
||||
merged, err := mergeConditional(vv, nc.Node, merge)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
vParentNode[idx] = merged
|
||||
nc.Node = merged
|
||||
default:
|
||||
return false, fmt.Errorf("don't know about vtype %T", vParentNode)
|
||||
}
|
||||
case map[string]any:
|
||||
key := nc.Parent.KeyToChild.(string)
|
||||
|
||||
// Update is treated differently depending on whether the value is a scalar or map type. If scalar,
|
||||
// insert a new element into the terminal node, otherwise replace the terminal node with the new subtree.
|
||||
if ncNode, ok := nc.Node.(*any); ok && !mapFromString {
|
||||
switch vNcNode := (*ncNode).(type) {
|
||||
case []any:
|
||||
switch vv.(type) {
|
||||
case map[string]any:
|
||||
// the vv is a map, and the node is a slice
|
||||
mergedValue := append(vNcNode, vv)
|
||||
parentNode[key] = mergedValue
|
||||
case *any:
|
||||
merged, err := mergeConditional(vv, vNcNode, merge)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
parentNode[key] = merged
|
||||
nc.Node = merged
|
||||
default:
|
||||
// the vv is an basic JSON type (int, float, string, bool)
|
||||
vv = append(vNcNode, vv)
|
||||
parentNode[key] = vv
|
||||
nc.Node = vv
|
||||
}
|
||||
default:
|
||||
return false, fmt.Errorf("don't know about vnc type %T", vNcNode)
|
||||
}
|
||||
} else {
|
||||
// For map passed as string type, the root is the new key.
|
||||
if mapFromString {
|
||||
if err := util.DeleteFromMap(nc.Parent.Node, nc.Parent.KeyToChild); err != nil {
|
||||
return false, err
|
||||
}
|
||||
vm := vv.(map[string]any)
|
||||
newKey := getTreeRoot(vm)
|
||||
return false, util.InsertIntoMap(nc.Parent.Node, newKey, vm[newKey])
|
||||
}
|
||||
parentNode[key] = vv
|
||||
nc.Node = vv
|
||||
}
|
||||
// TODO `map[interface{}]interface{}` is used by tests in operator/cmd/mesh, we should add our own tests
|
||||
case map[any]any:
|
||||
key := nc.Parent.KeyToChild.(string)
|
||||
parentNode[key] = vv
|
||||
nc.Node = vv
|
||||
default:
|
||||
return false, fmt.Errorf("don't know about type %T", parentNode)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// mergeConditional returns a merge of newVal and originalVal if merge is true, otherwise it returns newVal.
|
||||
func mergeConditional(newVal, originalVal any, merge bool) (any, error) {
|
||||
if !merge || util.IsValueNilOrDefault(originalVal) {
|
||||
return newVal, nil
|
||||
}
|
||||
newS, err := yaml.Marshal(newVal)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if util.IsYAMLEmpty(string(newS)) {
|
||||
return originalVal, nil
|
||||
}
|
||||
originalS, err := yaml.Marshal(originalVal)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if util.IsYAMLEmpty(string(originalS)) {
|
||||
return newVal, nil
|
||||
}
|
||||
|
||||
mergedS, err := util.OverlayYAML(string(originalS), string(newS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if util.IsMap(originalVal) {
|
||||
// For JSON compatibility
|
||||
out := make(map[string]any)
|
||||
if err := yaml.Unmarshal([]byte(mergedS), &out); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
// For scalars and slices, copy the type
|
||||
out := originalVal
|
||||
if err := yaml.Unmarshal([]byte(mergedS), &out); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// find returns the value at path from the given tree, or false if the path does not exist.
|
||||
func find(treeNode any, path util.Path) (any, bool) {
|
||||
if len(path) == 0 || treeNode == nil {
|
||||
return nil, false
|
||||
}
|
||||
switch nt := treeNode.(type) {
|
||||
case map[any]any:
|
||||
val := nt[path[0]]
|
||||
if val == nil {
|
||||
return nil, false
|
||||
}
|
||||
if len(path) == 1 {
|
||||
return val, true
|
||||
}
|
||||
return find(val, path[1:])
|
||||
case map[string]any:
|
||||
val := nt[path[0]]
|
||||
if val == nil {
|
||||
return nil, false
|
||||
}
|
||||
if len(path) == 1 {
|
||||
return val, true
|
||||
}
|
||||
return find(val, path[1:])
|
||||
case []any:
|
||||
idx, err := strconv.Atoi(path[0])
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
if idx >= len(nt) {
|
||||
return nil, false
|
||||
}
|
||||
val := nt[idx]
|
||||
return find(val, path[1:])
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
// stringsEqual reports whether the string representations of a and b are equal. a and b may have different types.
|
||||
func stringsEqual(a, b any) bool {
|
||||
return fmt.Sprint(a) == fmt.Sprint(b)
|
||||
}
|
||||
|
||||
// matchesRegex reports whether str regex matches pattern.
|
||||
func matchesRegex(pattern, str any) bool {
|
||||
match, err := regexp.MatchString(fmt.Sprint(pattern), fmt.Sprint(str))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return match
|
||||
}
|
||||
|
||||
// isSliceOrPtrInterface reports whether v is a slice, a ptr to slice or interface to slice.
|
||||
func isSliceOrPtrInterface(v any) bool {
|
||||
vv := reflect.ValueOf(v)
|
||||
if vv.Kind() == reflect.Ptr {
|
||||
vv = vv.Elem()
|
||||
}
|
||||
if vv.Kind() == reflect.Interface {
|
||||
vv = vv.Elem()
|
||||
}
|
||||
return vv.Kind() == reflect.Slice
|
||||
}
|
||||
|
||||
// isMapOrInterface reports whether v is a map, or interface to a map.
|
||||
func isMapOrInterface(v any) bool {
|
||||
vv := reflect.ValueOf(v)
|
||||
if vv.Kind() == reflect.Interface {
|
||||
vv = vv.Elem()
|
||||
}
|
||||
return vv.Kind() == reflect.Map
|
||||
}
|
||||
|
||||
// tryToUnmarshalStringToYAML tries to unmarshal something that may be a YAML list or map into a structure. If not
|
||||
// possible, returns original scalar value.
|
||||
func tryToUnmarshalStringToYAML(s any) (any, bool) {
|
||||
// If value type is a string it could either be a literal string or a map type passed as a string. Try to unmarshal
|
||||
// to discover it's the latter.
|
||||
vv := s
|
||||
|
||||
if reflect.TypeOf(vv).Kind() == reflect.String {
|
||||
sv := strings.Split(vv.(string), "\n")
|
||||
// Need to be careful not to transform string literals into maps unless they really are maps, since scalar handling
|
||||
// is different for inserts.
|
||||
if len(sv) == 1 && strings.Contains(s.(string), ": ") ||
|
||||
len(sv) > 1 && strings.Contains(s.(string), ":") {
|
||||
nv := make(map[string]any)
|
||||
if err := json.Unmarshal([]byte(vv.(string)), &nv); err == nil {
|
||||
// treat JSON as string
|
||||
return vv, false
|
||||
}
|
||||
if err := yaml2.Unmarshal([]byte(vv.(string)), &nv); err == nil {
|
||||
return nv, true
|
||||
}
|
||||
}
|
||||
}
|
||||
// looks like a literal or failed unmarshal, return original type.
|
||||
return vv, false
|
||||
}
|
||||
|
||||
// getTreeRoot returns the first key found in m. It assumes a single root tree.
|
||||
func getTreeRoot(m map[string]any) string {
|
||||
for k := range m {
|
||||
return k
|
||||
}
|
||||
return ""
|
||||
}
|
||||
843
pkg/cmd/hgctl/helm/tpath/tree_test.go
Normal file
843
pkg/cmd/hgctl/helm/tpath/tree_test.go
Normal file
@@ -0,0 +1,843 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tpath
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/util"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
func TestWritePathContext(t *testing.T) {
|
||||
rootYAML := `
|
||||
a:
|
||||
b:
|
||||
- name: n1
|
||||
value: v1
|
||||
- name: n2
|
||||
list:
|
||||
- v1
|
||||
- v2
|
||||
- v3_regex
|
||||
`
|
||||
tests := []struct {
|
||||
desc string
|
||||
path string
|
||||
value any
|
||||
want string
|
||||
wantFound bool
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
desc: "AddListEntry",
|
||||
path: `a.b.[name:n2].list`,
|
||||
value: `foo`,
|
||||
wantFound: true,
|
||||
want: `
|
||||
a:
|
||||
b:
|
||||
- name: n1
|
||||
value: v1
|
||||
- name: n2
|
||||
list:
|
||||
- v1
|
||||
- v2
|
||||
- v3_regex
|
||||
- foo
|
||||
`,
|
||||
},
|
||||
{
|
||||
desc: "ModifyListEntryValue",
|
||||
path: `a.b.[name:n1].value`,
|
||||
value: `v2`,
|
||||
wantFound: true,
|
||||
want: `
|
||||
a:
|
||||
b:
|
||||
- name: n1
|
||||
value: v2
|
||||
- list:
|
||||
- v1
|
||||
- v2
|
||||
- v3_regex
|
||||
name: n2
|
||||
`,
|
||||
},
|
||||
{
|
||||
desc: "ModifyListEntryValueQuoted",
|
||||
path: `a.b.[name:n1].value`,
|
||||
value: `v2`,
|
||||
wantFound: true,
|
||||
want: `
|
||||
a:
|
||||
b:
|
||||
- name: "n1"
|
||||
value: v2
|
||||
- list:
|
||||
- v1
|
||||
- v2
|
||||
- v3_regex
|
||||
name: n2
|
||||
`,
|
||||
},
|
||||
{
|
||||
desc: "ModifyListEntry",
|
||||
path: `a.b.[name:n2].list.[:v2]`,
|
||||
value: `v3`,
|
||||
wantFound: true,
|
||||
want: `
|
||||
a:
|
||||
b:
|
||||
- name: n1
|
||||
value: v1
|
||||
- list:
|
||||
- v1
|
||||
- v3
|
||||
- v3_regex
|
||||
name: n2
|
||||
`,
|
||||
},
|
||||
{
|
||||
desc: "ModifyListEntryMapValue",
|
||||
path: `a.b.[name:n2]`,
|
||||
value: `name: n2
|
||||
list:
|
||||
- nk1: nv1
|
||||
- nk2: nv2`,
|
||||
wantFound: true,
|
||||
want: `
|
||||
a:
|
||||
b:
|
||||
- name: n1
|
||||
value: v1
|
||||
- name: n2
|
||||
list:
|
||||
- nk1: nv1
|
||||
- nk2: nv2
|
||||
`,
|
||||
},
|
||||
{
|
||||
desc: "ModifyNthListEntry",
|
||||
path: `a.b.[1].list.[:v2]`,
|
||||
value: `v-the-second`,
|
||||
wantFound: true,
|
||||
want: `
|
||||
a:
|
||||
b:
|
||||
- name: n1
|
||||
value: v1
|
||||
- list:
|
||||
- v1
|
||||
- v-the-second
|
||||
- v3_regex
|
||||
name: n2
|
||||
`,
|
||||
},
|
||||
{
|
||||
desc: "ModifyNthLeafListEntry",
|
||||
path: `a.b.[1].list.[2]`,
|
||||
value: `v-the-third`,
|
||||
wantFound: true,
|
||||
want: `
|
||||
a:
|
||||
b:
|
||||
- name: n1
|
||||
value: v1
|
||||
- list:
|
||||
- v1
|
||||
- v2
|
||||
- v-the-third
|
||||
name: n2
|
||||
`,
|
||||
},
|
||||
{
|
||||
desc: "ModifyListEntryValueDotless",
|
||||
path: `a.b[name:n1].value`,
|
||||
value: `v2`,
|
||||
wantFound: true,
|
||||
want: `
|
||||
a:
|
||||
b:
|
||||
- name: n1
|
||||
value: v2
|
||||
- list:
|
||||
- v1
|
||||
- v2
|
||||
- v3_regex
|
||||
name: n2
|
||||
`,
|
||||
},
|
||||
{
|
||||
desc: "DeleteListEntry",
|
||||
path: `a.b.[name:n1]`,
|
||||
wantFound: true,
|
||||
want: `
|
||||
a:
|
||||
b:
|
||||
- list:
|
||||
- v1
|
||||
- v2
|
||||
- v3_regex
|
||||
name: n2
|
||||
`,
|
||||
},
|
||||
{
|
||||
desc: "DeleteListEntryValue",
|
||||
path: `a.b.[name:n2].list.[:v2]`,
|
||||
wantFound: true,
|
||||
want: `
|
||||
a:
|
||||
b:
|
||||
- name: n1
|
||||
value: v1
|
||||
- list:
|
||||
- v1
|
||||
- v3_regex
|
||||
name: n2
|
||||
`,
|
||||
},
|
||||
{
|
||||
desc: "DeleteListEntryIndex",
|
||||
path: `a.b.[name:n2].list.[1]`,
|
||||
wantFound: true,
|
||||
want: `
|
||||
a:
|
||||
b:
|
||||
- name: n1
|
||||
value: v1
|
||||
- list:
|
||||
- v1
|
||||
- v3_regex
|
||||
name: n2
|
||||
`,
|
||||
},
|
||||
{
|
||||
desc: "DeleteListEntryValueRegex",
|
||||
path: `a.b.[name:n2].list.[:v3]`,
|
||||
wantFound: true,
|
||||
want: `
|
||||
a:
|
||||
b:
|
||||
- name: n1
|
||||
value: v1
|
||||
- list:
|
||||
- v1
|
||||
- v2
|
||||
name: n2
|
||||
`,
|
||||
},
|
||||
{
|
||||
desc: "DeleteListLeafEntryBogusIndex",
|
||||
path: `a.b.[name:n2].list.[-200]`,
|
||||
wantFound: false,
|
||||
wantErr: `path a.b.[name:n2].list.[-200]: element [-200] not found`,
|
||||
},
|
||||
{
|
||||
desc: "DeleteListEntryBogusIndex",
|
||||
path: `a.b.[1000000].list.[:v2]`,
|
||||
wantFound: false,
|
||||
wantErr: `index 1000000 exceeds list length 2 at path [1000000].list.[:v2]`,
|
||||
},
|
||||
{
|
||||
desc: "AddMapEntry",
|
||||
path: `a.new_key`,
|
||||
value: `new_val`,
|
||||
wantFound: true,
|
||||
want: `
|
||||
a:
|
||||
b:
|
||||
- name: n1
|
||||
value: v1
|
||||
- name: n2
|
||||
list:
|
||||
- v1
|
||||
- v2
|
||||
- v3_regex
|
||||
new_key: new_val
|
||||
`,
|
||||
},
|
||||
{
|
||||
desc: "AddMapEntryMapValue",
|
||||
path: `a.new_key`,
|
||||
value: `new_key:
|
||||
nk1:
|
||||
nk2: nv2`,
|
||||
wantFound: true,
|
||||
want: `
|
||||
a:
|
||||
b:
|
||||
- name: n1
|
||||
value: v1
|
||||
- name: n2
|
||||
list:
|
||||
- v1
|
||||
- v2
|
||||
- v3_regex
|
||||
new_key:
|
||||
nk1:
|
||||
nk2: nv2
|
||||
`,
|
||||
},
|
||||
{
|
||||
desc: "ModifyMapEntryMapValue",
|
||||
path: `a.b`,
|
||||
value: `nk1:
|
||||
nk2: nv2`,
|
||||
wantFound: true,
|
||||
want: `
|
||||
a:
|
||||
nk1:
|
||||
nk2: nv2
|
||||
`,
|
||||
},
|
||||
{
|
||||
desc: "DeleteMapEntry",
|
||||
path: `a.b`,
|
||||
wantFound: true,
|
||||
want: `
|
||||
a: {}
|
||||
`,
|
||||
},
|
||||
{
|
||||
desc: "path not found",
|
||||
path: `a.c.[name:n2].list.[:v3]`,
|
||||
wantFound: false,
|
||||
wantErr: `path not found at element c in path a.c.[name:n2].list.[:v3]`,
|
||||
},
|
||||
{
|
||||
desc: "error key",
|
||||
path: `a.b.[].list`,
|
||||
wantFound: false,
|
||||
wantErr: `path a.b.[].list: [] is not a valid key:value path element`,
|
||||
},
|
||||
{
|
||||
desc: "invalid index",
|
||||
path: `a.c.[n2].list.[:v3]`,
|
||||
wantFound: false,
|
||||
wantErr: `path not found at element c in path a.c.[n2].list.[:v3]`,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
root := make(map[string]any)
|
||||
if err := yaml.Unmarshal([]byte(rootYAML), &root); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pc, gotFound, gotErr := GetPathContext(root, util.PathFromString(tt.path), false)
|
||||
if gotErr, wantErr := errToString(gotErr), tt.wantErr; gotErr != wantErr {
|
||||
t.Fatalf("GetPathContext(%s): gotErr:%s, wantErr:%s", tt.desc, gotErr, wantErr)
|
||||
}
|
||||
if gotFound != tt.wantFound {
|
||||
t.Fatalf("GetPathContext(%s): gotFound:%v, wantFound:%v", tt.desc, gotFound, tt.wantFound)
|
||||
}
|
||||
if tt.wantErr != "" || !tt.wantFound {
|
||||
if tt.want != "" {
|
||||
t.Error("tt.want is set but never checked")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
err := WritePathContext(pc, tt.value, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
gotYAML := util.ToYAML(root)
|
||||
diff := util.YAMLDiff(gotYAML, tt.want)
|
||||
if diff != "" {
|
||||
t.Errorf("%s: (got:-, want:+):\n%s\n", tt.desc, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteNode(t *testing.T) {
|
||||
testTreeYAML := `
|
||||
a:
|
||||
b:
|
||||
c: val1
|
||||
list1:
|
||||
- i1: val1
|
||||
- i2: val2
|
||||
- i3a: key1
|
||||
i3b:
|
||||
list2:
|
||||
- i1: val1
|
||||
- i2: val2
|
||||
- i3a: key1
|
||||
i3b:
|
||||
i1: va11
|
||||
`
|
||||
tests := []struct {
|
||||
desc string
|
||||
baseYAML string
|
||||
path string
|
||||
value string
|
||||
want string
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
desc: "insert empty",
|
||||
path: "a.b.c",
|
||||
value: "val1",
|
||||
want: `
|
||||
a:
|
||||
b:
|
||||
c: val1
|
||||
`,
|
||||
},
|
||||
{
|
||||
desc: "overwrite",
|
||||
baseYAML: testTreeYAML,
|
||||
path: "a.b.c",
|
||||
value: "val2",
|
||||
want: `
|
||||
a:
|
||||
b:
|
||||
c: val2
|
||||
list1:
|
||||
- i1: val1
|
||||
- i2: val2
|
||||
- i3a: key1
|
||||
i3b:
|
||||
list2:
|
||||
- i1: val1
|
||||
- i2: val2
|
||||
- i3a: key1
|
||||
i3b:
|
||||
i1: va11
|
||||
`,
|
||||
},
|
||||
{
|
||||
desc: "partial create",
|
||||
baseYAML: testTreeYAML,
|
||||
path: "a.b.d",
|
||||
value: "val3",
|
||||
want: `
|
||||
a:
|
||||
b:
|
||||
c: val1
|
||||
d: val3
|
||||
list1:
|
||||
- i1: val1
|
||||
- i2: val2
|
||||
- i3a: key1
|
||||
i3b:
|
||||
list2:
|
||||
- i1: val1
|
||||
- i2: val2
|
||||
- i3a: key1
|
||||
i3b:
|
||||
i1: va11
|
||||
`,
|
||||
},
|
||||
{
|
||||
desc: "list keys",
|
||||
baseYAML: testTreeYAML,
|
||||
path: "a.b.list1.[i3a:key1].i3b.list2.[i3a:key1].i3b.i1",
|
||||
value: "val2",
|
||||
want: `
|
||||
a:
|
||||
b:
|
||||
c: val1
|
||||
list1:
|
||||
- i1: val1
|
||||
- i2: val2
|
||||
- i3a: key1
|
||||
i3b:
|
||||
list2:
|
||||
- i1: val1
|
||||
- i2: val2
|
||||
- i3a: key1
|
||||
i3b:
|
||||
i1: val2
|
||||
`,
|
||||
},
|
||||
// For https://github.com/istio/istio/issues/20950
|
||||
{
|
||||
desc: "with initial list",
|
||||
baseYAML: `
|
||||
components:
|
||||
ingressGateways:
|
||||
- enabled: true
|
||||
`,
|
||||
path: "components.ingressGateways[0].enabled",
|
||||
value: "false",
|
||||
want: `
|
||||
components:
|
||||
ingressGateways:
|
||||
- enabled: "false"
|
||||
`,
|
||||
},
|
||||
{
|
||||
desc: "no initial list",
|
||||
baseYAML: "",
|
||||
path: "components.ingressGateways[0].enabled",
|
||||
value: "false",
|
||||
want: `
|
||||
components:
|
||||
ingressGateways:
|
||||
- enabled: "false"
|
||||
`,
|
||||
},
|
||||
{
|
||||
desc: "no initial list for entry",
|
||||
baseYAML: `
|
||||
a: {}
|
||||
`,
|
||||
path: "a.list.[0]",
|
||||
value: "v1",
|
||||
want: `
|
||||
a:
|
||||
list:
|
||||
- v1
|
||||
`,
|
||||
},
|
||||
{
|
||||
desc: "ExtendNthLeafListEntry",
|
||||
baseYAML: `
|
||||
a:
|
||||
list:
|
||||
- v1
|
||||
`,
|
||||
path: `a.list.[1]`,
|
||||
value: `v2`,
|
||||
want: `
|
||||
a:
|
||||
list:
|
||||
- v1
|
||||
- v2
|
||||
`,
|
||||
},
|
||||
{
|
||||
desc: "ExtendLeafListEntryLargeIndex",
|
||||
baseYAML: `
|
||||
a:
|
||||
list:
|
||||
- v1
|
||||
`,
|
||||
path: `a.list.[999]`,
|
||||
value: `v2`,
|
||||
want: `
|
||||
a:
|
||||
list:
|
||||
- v1
|
||||
- v2
|
||||
`,
|
||||
},
|
||||
{
|
||||
desc: "ExtendLeafListEntryNegativeIndex",
|
||||
baseYAML: `
|
||||
a:
|
||||
list:
|
||||
- v1
|
||||
`,
|
||||
path: `a.list.[-1]`,
|
||||
value: `v2`,
|
||||
want: `
|
||||
a:
|
||||
list:
|
||||
- v1
|
||||
- v2
|
||||
`,
|
||||
},
|
||||
{
|
||||
desc: "ExtendNthListEntry",
|
||||
baseYAML: `
|
||||
a:
|
||||
list:
|
||||
- name: foo
|
||||
`,
|
||||
path: `a.list.[1].name`,
|
||||
value: `bar`,
|
||||
want: `
|
||||
a:
|
||||
list:
|
||||
- name: foo
|
||||
- name: bar
|
||||
`,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
root := make(map[string]any)
|
||||
if tt.baseYAML != "" {
|
||||
if err := yaml.Unmarshal([]byte(tt.baseYAML), &root); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
p := util.PathFromString(tt.path)
|
||||
err := WriteNode(root, p, tt.value)
|
||||
if gotErr, wantErr := errToString(err), tt.wantErr; gotErr != wantErr {
|
||||
t.Errorf("%s: gotErr:%s, wantErr:%s", tt.desc, gotErr, wantErr)
|
||||
return
|
||||
}
|
||||
if got, want := util.ToYAML(root), tt.want; err == nil && util.YAMLDiff(got, want) != "" {
|
||||
t.Errorf("%s: got:\n%s\nwant:\n%s\ndiff:\n%s\n", tt.desc, got, want, util.YAMLDiff(got, want))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeNode(t *testing.T) {
|
||||
testTreeYAML := `
|
||||
a:
|
||||
b:
|
||||
c: val1
|
||||
list1:
|
||||
- i1: val1
|
||||
- i2: val2
|
||||
`
|
||||
tests := []struct {
|
||||
desc string
|
||||
baseYAML string
|
||||
path string
|
||||
value string
|
||||
want string
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
desc: "merge list entry",
|
||||
baseYAML: testTreeYAML,
|
||||
path: "a.b.list1.[i1:val1]",
|
||||
value: `
|
||||
i2b: val2`,
|
||||
want: `
|
||||
a:
|
||||
b:
|
||||
c: val1
|
||||
list1:
|
||||
- i1: val1
|
||||
i2b: val2
|
||||
- i2: val2
|
||||
`,
|
||||
},
|
||||
{
|
||||
desc: "merge list 2",
|
||||
baseYAML: testTreeYAML,
|
||||
path: "a.b.list1",
|
||||
value: `
|
||||
i3:
|
||||
a: val3
|
||||
`,
|
||||
want: `
|
||||
a:
|
||||
b:
|
||||
c: val1
|
||||
list1:
|
||||
- i1: val1
|
||||
- i2: val2
|
||||
- i3:
|
||||
a: val3
|
||||
`,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
root := make(map[string]any)
|
||||
if tt.baseYAML != "" {
|
||||
if err := yaml.Unmarshal([]byte(tt.baseYAML), &root); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
p := util.PathFromString(tt.path)
|
||||
iv := make(map[string]any)
|
||||
err := yaml.Unmarshal([]byte(tt.value), &iv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = MergeNode(root, p, iv)
|
||||
if gotErr, wantErr := errToString(err), tt.wantErr; gotErr != wantErr {
|
||||
t.Errorf("%s: gotErr:%s, wantErr:%s", tt.desc, gotErr, wantErr)
|
||||
return
|
||||
}
|
||||
if got, want := util.ToYAML(root), tt.want; err == nil && util.YAMLDiff(got, want) != "" {
|
||||
t.Errorf("%s: got:\n%s\nwant:\n%s\ndiff:\n%s\n", tt.desc, got, want, util.YAMLDiff(got, want))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// errToString returns the string representation of err and the empty string if
|
||||
// err is nil.
|
||||
func errToString(err error) string {
|
||||
if err == nil {
|
||||
return ""
|
||||
}
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
// TestSecretVolumes simulates https://github.com/istio/istio/issues/20381
|
||||
func TestSecretVolumes(t *testing.T) {
|
||||
rootYAML := `
|
||||
values:
|
||||
gateways:
|
||||
istio-egressgateway:
|
||||
secretVolumes: []
|
||||
`
|
||||
root := make(map[string]any)
|
||||
if err := yaml.Unmarshal([]byte(rootYAML), &root); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
overrides := []struct {
|
||||
path string
|
||||
value any
|
||||
}{
|
||||
{
|
||||
path: "values.gateways.istio-egressgateway.secretVolumes[0].name",
|
||||
value: "egressgateway-certs",
|
||||
},
|
||||
{
|
||||
path: "values.gateways.istio-egressgateway.secretVolumes[0].secretName",
|
||||
value: "istio-egressgateway-certs",
|
||||
},
|
||||
{
|
||||
path: "values.gateways.istio-egressgateway.secretVolumes[0].mountPath",
|
||||
value: "/etc/istio/egressgateway-certs",
|
||||
},
|
||||
{
|
||||
path: "values.gateways.istio-egressgateway.secretVolumes[1].name",
|
||||
value: "egressgateway-ca-certs",
|
||||
},
|
||||
{
|
||||
path: "values.gateways.istio-egressgateway.secretVolumes[1].secretName",
|
||||
value: "istio-egressgateway-ca-certs",
|
||||
},
|
||||
{
|
||||
path: "values.gateways.istio-egressgateway.secretVolumes[1].mountPath",
|
||||
value: "/etc/istio/egressgateway-ca-certs",
|
||||
},
|
||||
{
|
||||
path: "values.gateways.istio-egressgateway.secretVolumes[2].name",
|
||||
value: "nginx-client-certs",
|
||||
},
|
||||
{
|
||||
path: "values.gateways.istio-egressgateway.secretVolumes[2].secretName",
|
||||
value: "nginx-client-certs",
|
||||
},
|
||||
{
|
||||
path: "values.gateways.istio-egressgateway.secretVolumes[2].mountPath",
|
||||
value: "/etc/istio/nginx-client-certs",
|
||||
},
|
||||
{
|
||||
path: "values.gateways.istio-egressgateway.secretVolumes[3].name",
|
||||
value: "nginx-ca-certs",
|
||||
},
|
||||
{
|
||||
path: "values.gateways.istio-egressgateway.secretVolumes[3].secretName",
|
||||
value: "nginx-ca-certs",
|
||||
},
|
||||
{
|
||||
path: "values.gateways.istio-egressgateway.secretVolumes[3].mountPath",
|
||||
value: "/etc/istio/nginx-ca-certs",
|
||||
},
|
||||
}
|
||||
|
||||
for _, override := range overrides {
|
||||
|
||||
pc, _, err := GetPathContext(root, util.PathFromString(override.path), true)
|
||||
if err != nil {
|
||||
t.Fatalf("GetPathContext(%q): %v", override.path, err)
|
||||
}
|
||||
err = WritePathContext(pc, override.value, false)
|
||||
if err != nil {
|
||||
t.Fatalf("WritePathContext(%q): %v", override.path, err)
|
||||
}
|
||||
}
|
||||
|
||||
want := `
|
||||
values:
|
||||
gateways:
|
||||
istio-egressgateway:
|
||||
secretVolumes:
|
||||
- mountPath: /etc/istio/egressgateway-certs
|
||||
name: egressgateway-certs
|
||||
secretName: istio-egressgateway-certs
|
||||
- mountPath: /etc/istio/egressgateway-ca-certs
|
||||
name: egressgateway-ca-certs
|
||||
secretName: istio-egressgateway-ca-certs
|
||||
- mountPath: /etc/istio/nginx-client-certs
|
||||
name: nginx-client-certs
|
||||
secretName: nginx-client-certs
|
||||
- mountPath: /etc/istio/nginx-ca-certs
|
||||
name: nginx-ca-certs
|
||||
secretName: nginx-ca-certs
|
||||
`
|
||||
gotYAML := util.ToYAML(root)
|
||||
diff := util.YAMLDiff(gotYAML, want)
|
||||
if diff != "" {
|
||||
t.Errorf("TestSecretVolumes: diff:\n%s\n", diff)
|
||||
}
|
||||
}
|
||||
|
||||
// Simulates https://github.com/istio/istio/issues/19196
|
||||
func TestWriteEscapedPathContext(t *testing.T) {
|
||||
rootYAML := `
|
||||
values:
|
||||
sidecarInjectorWebhook:
|
||||
injectedAnnotations: {}
|
||||
`
|
||||
tests := []struct {
|
||||
desc string
|
||||
path string
|
||||
value any
|
||||
want string
|
||||
wantFound bool
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
desc: "ModifyEscapedPathValue",
|
||||
path: `values.sidecarInjectorWebhook.injectedAnnotations.container\.apparmor\.security\.beta\.kubernetes\.io/istio-proxy`,
|
||||
value: `runtime/default`,
|
||||
wantFound: true,
|
||||
want: `
|
||||
values:
|
||||
sidecarInjectorWebhook:
|
||||
injectedAnnotations:
|
||||
container.apparmor.security.beta.kubernetes.io/istio-proxy: runtime/default
|
||||
`,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
root := make(map[string]any)
|
||||
if err := yaml.Unmarshal([]byte(rootYAML), &root); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pc, gotFound, gotErr := GetPathContext(root, util.PathFromString(tt.path), false)
|
||||
if gotErr, wantErr := errToString(gotErr), tt.wantErr; gotErr != wantErr {
|
||||
t.Fatalf("GetPathContext(%s): gotErr:%s, wantErr:%s", tt.desc, gotErr, wantErr)
|
||||
}
|
||||
if gotFound != tt.wantFound {
|
||||
t.Fatalf("GetPathContext(%s): gotFound:%v, wantFound:%v", tt.desc, gotFound, tt.wantFound)
|
||||
}
|
||||
if tt.wantErr != "" || !tt.wantFound {
|
||||
return
|
||||
}
|
||||
|
||||
err := WritePathContext(pc, tt.value, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
gotYAML := util.ToYAML(root)
|
||||
diff := util.YAMLDiff(gotYAML, tt.want)
|
||||
if diff != "" {
|
||||
t.Errorf("%s: diff:\n%s\n", tt.desc, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
58
pkg/cmd/hgctl/helm/tpath/util.go
Normal file
58
pkg/cmd/hgctl/helm/tpath/util.go
Normal file
@@ -0,0 +1,58 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tpath
|
||||
|
||||
import (
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/util"
|
||||
"gopkg.in/yaml.v2"
|
||||
yaml2 "sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
// AddSpecRoot adds a root node called "spec" to the given tree and returns the resulting tree.
|
||||
func AddSpecRoot(tree string) (string, error) {
|
||||
t, nt := make(map[string]any), make(map[string]any)
|
||||
if err := yaml.Unmarshal([]byte(tree), &t); err != nil {
|
||||
return "", err
|
||||
}
|
||||
nt["spec"] = t
|
||||
out, err := yaml.Marshal(nt)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(out), nil
|
||||
}
|
||||
|
||||
// GetSpecSubtree returns the subtree under "spec".
|
||||
func GetSpecSubtree(yml string) (string, error) {
|
||||
return GetConfigSubtree(yml, "spec")
|
||||
}
|
||||
|
||||
// GetConfigSubtree returns the subtree at the given path.
|
||||
func GetConfigSubtree(manifest, path string) (string, error) {
|
||||
root := make(map[string]any)
|
||||
if err := yaml2.Unmarshal([]byte(manifest), &root); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
nc, _, err := GetPathContext(root, util.PathFromString(path), false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
out, err := yaml2.Marshal(nc.Node)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(out), nil
|
||||
}
|
||||
122
pkg/cmd/hgctl/helm/tpath/util_test.go
Normal file
122
pkg/cmd/hgctl/helm/tpath/util_test.go
Normal file
@@ -0,0 +1,122 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tpath
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAddSpecRoot(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
in string
|
||||
expect string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "empty",
|
||||
in: ``,
|
||||
expect: `spec: {}
|
||||
`,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "add-root",
|
||||
in: `
|
||||
a: va
|
||||
b: foo`,
|
||||
expect: `spec:
|
||||
a: va
|
||||
b: foo
|
||||
`,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "err",
|
||||
in: `i can't be yaml, can I?`,
|
||||
expect: ``,
|
||||
err: errors.New(""),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
if got, err := AddSpecRoot(tt.in); got != tt.expect ||
|
||||
((err != nil && tt.err == nil) || (err == nil && tt.err != nil)) {
|
||||
t.Errorf("%s AddSpecRoot(%s) => %s, want %s", tt.desc, tt.in, got, tt.expect)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetConfigSubtree(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
manifest string
|
||||
path string
|
||||
expect string
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
desc: "empty",
|
||||
manifest: ``,
|
||||
path: ``,
|
||||
expect: `{}
|
||||
`,
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
desc: "subtree",
|
||||
manifest: `
|
||||
a:
|
||||
b:
|
||||
- name: n1
|
||||
value: v2
|
||||
- list:
|
||||
- v1
|
||||
- v2
|
||||
- v3_regex
|
||||
name: n2
|
||||
`,
|
||||
path: `a`,
|
||||
expect: `b:
|
||||
- name: n1
|
||||
value: v2
|
||||
- list:
|
||||
- v1
|
||||
- v2
|
||||
- v3_regex
|
||||
name: n2
|
||||
`,
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
desc: "err",
|
||||
manifest: "not-yaml",
|
||||
path: "not-subnode",
|
||||
expect: ``,
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
if got, err := GetConfigSubtree(tt.manifest, tt.path); got != tt.expect || (err == nil) == tt.err {
|
||||
t.Errorf("%s GetConfigSubtree(%s, %s) => %s, want %s", tt.desc, tt.manifest, tt.path, got, tt.expect)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
207
pkg/cmd/hgctl/install.go
Normal file
207
pkg/cmd/hgctl/install.go
Normal file
@@ -0,0 +1,207 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package hgctl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/helm"
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/installer"
|
||||
"github.com/alibaba/higress/pkg/cmd/options"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const (
|
||||
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).`
|
||||
// manifestsFlagHelpStr is the command line description for --manifests
|
||||
manifestsFlagHelpStr = `Specify a path to a directory of profiles
|
||||
(e.g. ~/Downloads/higress/manifests).`
|
||||
filenameFlagHelpStr = "Path to file containing helm custom values"
|
||||
outputHelpstr = "Specify a file to write profile yaml"
|
||||
|
||||
profileNameK8s = "k8s"
|
||||
profileNameLocalK8s = "local-k8s"
|
||||
profileNameLocalDocker = "local-docker"
|
||||
)
|
||||
|
||||
type InstallArgs struct {
|
||||
// InFilenames is a filename to helm custom values
|
||||
InFilenames []string
|
||||
// KubeConfigPath is the path to kube config file.
|
||||
KubeConfigPath string
|
||||
// Context is the cluster context in the kube config
|
||||
Context string
|
||||
// Set is a string with element format "path=value" where path is an profile path and the value is a
|
||||
// value to set the node at that path to.
|
||||
Set []string
|
||||
// ManifestsPath is a path to a ManifestsPath and profiles directory in the local filesystem with a release tgz.
|
||||
ManifestsPath string
|
||||
}
|
||||
|
||||
func (a *InstallArgs) String() string {
|
||||
var b strings.Builder
|
||||
b.WriteString("KubeConfigPath: " + a.KubeConfigPath + "\n")
|
||||
b.WriteString("Context: " + a.Context + "\n")
|
||||
b.WriteString("Set: " + fmt.Sprint(a.Set) + "\n")
|
||||
b.WriteString("ManifestsPath: " + a.ManifestsPath + "\n")
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func addInstallFlags(cmd *cobra.Command, args *InstallArgs) {
|
||||
cmd.PersistentFlags().StringSliceVarP(&args.InFilenames, "filename", "f", nil, filenameFlagHelpStr)
|
||||
cmd.PersistentFlags().StringArrayVarP(&args.Set, "set", "s", nil, setFlagHelpStr)
|
||||
cmd.PersistentFlags().StringVarP(&args.ManifestsPath, "manifests", "d", "", manifestsFlagHelpStr)
|
||||
}
|
||||
|
||||
// --manifests is an alias for --set installPackagePath=
|
||||
func applyFlagAliases(flags []string, manifestsPath string) []string {
|
||||
if manifestsPath != "" {
|
||||
flags = append(flags, fmt.Sprintf("installPackagePath=%s", manifestsPath))
|
||||
}
|
||||
return flags
|
||||
}
|
||||
|
||||
// newInstallCmd generates a higress install manifest and applies it to a cluster
|
||||
func newInstallCmd() *cobra.Command {
|
||||
iArgs := &InstallArgs{}
|
||||
installCmd := &cobra.Command{
|
||||
Use: "install",
|
||||
Short: "Applies an higress manifest, installing or reconfiguring higress on a cluster.",
|
||||
Long: "The install command generates an higress install manifest and applies it to a cluster.",
|
||||
// nolint: lll
|
||||
Example: ` # Apply a default higress installation
|
||||
hgctl install
|
||||
|
||||
# Install higress on local kubernetes cluster
|
||||
hgctl install --set profile=local-k8s
|
||||
|
||||
# Install higress on local docker environment with specific gateway port
|
||||
hgctl install --set profile=local-docker --set gateway.httpPort=80 --set gateway.httpsPort=443
|
||||
|
||||
# To override profile setting
|
||||
hgctl install --set profile=local-k8s --set global.enableIstioAPI=true --set gateway.replicas=2"
|
||||
|
||||
# To override helm setting
|
||||
hgctl install --set profile=local-k8s --set values.global.proxy.resources.requsts.cpu=500m"
|
||||
|
||||
|
||||
`,
|
||||
Args: cobra.ExactArgs(0),
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return install(cmd.OutOrStdout(), iArgs)
|
||||
},
|
||||
}
|
||||
addInstallFlags(installCmd, iArgs)
|
||||
flags := installCmd.Flags()
|
||||
options.AddKubeConfigFlags(flags)
|
||||
return installCmd
|
||||
}
|
||||
|
||||
func install(writer io.Writer, iArgs *InstallArgs) error {
|
||||
setFlags := applyFlagAliases(iArgs.Set, iArgs.ManifestsPath)
|
||||
|
||||
// check profileName
|
||||
psf := helm.GetValueForSetFlag(setFlags, "profile")
|
||||
if len(psf) == 0 {
|
||||
psf = promptProfileName(writer)
|
||||
setFlags = append(setFlags, fmt.Sprintf("profile=%s", psf))
|
||||
}
|
||||
|
||||
if !promptInstall(writer, psf) {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, profile, profileName, err := helm.GenerateConfig(iArgs.InFilenames, setFlags)
|
||||
if err != nil {
|
||||
return fmt.Errorf("generate config: %v", err)
|
||||
}
|
||||
|
||||
fmt.Fprintf(writer, "\n🧐 Validating Profile: \"%s\" \n", profileName)
|
||||
err = profile.Validate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = installManifests(profile, writer)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to install manifests: %v", err)
|
||||
}
|
||||
|
||||
// Remove "~/.hgctl/profiles/install.yaml"
|
||||
if oldProfileName, isExisted := installer.GetInstalledYamlPath(); isExisted {
|
||||
_ = os.Remove(oldProfileName)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func promptInstall(writer io.Writer, profileName string) bool {
|
||||
answer := ""
|
||||
for {
|
||||
fmt.Fprintf(writer, "\nThis will install Higress \"%s\" profile into the cluster. \nProceed? (y/N)", profileName)
|
||||
fmt.Scanln(&answer)
|
||||
if strings.TrimSpace(answer) == "y" {
|
||||
fmt.Fprintf(writer, "\n")
|
||||
return true
|
||||
}
|
||||
if strings.TrimSpace(answer) == "N" {
|
||||
fmt.Fprintf(writer, "Cancelled.\n")
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func promptProfileName(writer io.Writer) string {
|
||||
answer := ""
|
||||
fmt.Fprintf(writer, "\nPlease select higress install configration profile:\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, "\n3.Install higress to local docker environment\n")
|
||||
for {
|
||||
fmt.Fprintf(writer, "\nPlease input 1, 2 or 3 to select, input your selection:")
|
||||
fmt.Scanln(&answer)
|
||||
if strings.TrimSpace(answer) == "1" {
|
||||
return profileNameLocalK8s
|
||||
}
|
||||
if strings.TrimSpace(answer) == "2" {
|
||||
return profileNameK8s
|
||||
}
|
||||
if strings.TrimSpace(answer) == "3" {
|
||||
return profileNameLocalDocker
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func installManifests(profile *helm.Profile, writer io.Writer) error {
|
||||
installer, err := installer.NewInstaller(profile, writer, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = installer.Install()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
121
pkg/cmd/hgctl/installer/component.go
Normal file
121
pkg/cmd/hgctl/installer/component.go
Normal file
@@ -0,0 +1,121 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package installer
|
||||
|
||||
import (
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/helm"
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/util"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
type ComponentName string
|
||||
|
||||
var ComponentMap = map[ComponentName]struct{}{
|
||||
Higress: {},
|
||||
Istio: {},
|
||||
}
|
||||
|
||||
type Component interface {
|
||||
// ComponentName returns the name of the component.
|
||||
ComponentName() ComponentName
|
||||
// Namespace returns the namespace for the component.
|
||||
Namespace() string
|
||||
// Enabled reports whether the component is enabled.
|
||||
Enabled() bool
|
||||
// Run starts the component. Must be called before the component is used.
|
||||
Run() error
|
||||
RenderManifest() (string, error)
|
||||
}
|
||||
|
||||
type ComponentOptions struct {
|
||||
Name string
|
||||
Namespace string
|
||||
// local
|
||||
ChartPath string
|
||||
// remote
|
||||
RepoURL string
|
||||
ChartName string
|
||||
Version string
|
||||
Quiet bool
|
||||
// Capabilities
|
||||
Capabilities *chartutil.Capabilities
|
||||
}
|
||||
|
||||
type ComponentOption func(*ComponentOptions)
|
||||
|
||||
func WithComponentNamespace(namespace string) ComponentOption {
|
||||
return func(opts *ComponentOptions) {
|
||||
opts.Namespace = namespace
|
||||
}
|
||||
}
|
||||
|
||||
func WithComponentChartPath(path string) ComponentOption {
|
||||
return func(opts *ComponentOptions) {
|
||||
opts.ChartPath = path
|
||||
}
|
||||
}
|
||||
|
||||
func WithComponentChartName(chartName string) ComponentOption {
|
||||
return func(opts *ComponentOptions) {
|
||||
opts.ChartName = chartName
|
||||
}
|
||||
}
|
||||
|
||||
func WithComponentRepoURL(url string) ComponentOption {
|
||||
return func(opts *ComponentOptions) {
|
||||
opts.RepoURL = url
|
||||
}
|
||||
}
|
||||
|
||||
func WithComponentVersion(version string) ComponentOption {
|
||||
return func(opts *ComponentOptions) {
|
||||
opts.Version = version
|
||||
}
|
||||
}
|
||||
|
||||
func WithComponentCapabilities(capabilities *chartutil.Capabilities) ComponentOption {
|
||||
return func(opts *ComponentOptions) {
|
||||
opts.Capabilities = capabilities
|
||||
}
|
||||
}
|
||||
|
||||
func WithQuiet() ComponentOption {
|
||||
return func(opts *ComponentOptions) {
|
||||
opts.Quiet = true
|
||||
}
|
||||
}
|
||||
|
||||
func renderComponentManifest(spec any, renderer helm.Renderer, addOn bool, name ComponentName, namespace string) (string, error) {
|
||||
var valsBytes []byte
|
||||
var valsYaml string
|
||||
var err error
|
||||
if yamlString, ok := spec.(string); ok {
|
||||
valsYaml = yamlString
|
||||
} else {
|
||||
if !util.IsValueNil(spec) {
|
||||
valsBytes, err = yaml.Marshal(spec)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
valsYaml = string(valsBytes)
|
||||
}
|
||||
}
|
||||
final, err := renderer.RenderManifest(valsYaml)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return final, nil
|
||||
}
|
||||
113
pkg/cmd/hgctl/installer/gateway_api.go
Normal file
113
pkg/cmd/hgctl/installer/gateway_api.go
Normal file
@@ -0,0 +1,113 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package installer
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/helm"
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/kubernetes"
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/manifests"
|
||||
)
|
||||
|
||||
const (
|
||||
GatewayAPI ComponentName = "gatewayAPI"
|
||||
)
|
||||
|
||||
type GatewayAPIComponent struct {
|
||||
profile *helm.Profile
|
||||
started bool
|
||||
opts *ComponentOptions
|
||||
renderer helm.Renderer
|
||||
writer io.Writer
|
||||
kubeCli kubernetes.CLIClient
|
||||
}
|
||||
|
||||
func NewGatewayAPIComponent(kubeCli kubernetes.CLIClient, profile *helm.Profile, writer io.Writer, opts ...ComponentOption) (Component, error) {
|
||||
newOpts := &ComponentOptions{}
|
||||
for _, opt := range opts {
|
||||
opt(newOpts)
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(newOpts.RepoURL, "embed://") {
|
||||
return nil, errors.New("GatewayAPI Url need start with embed://")
|
||||
}
|
||||
|
||||
chartDir := strings.TrimPrefix(newOpts.RepoURL, "embed://")
|
||||
// GatewayAPI can only be installed by embed type
|
||||
renderer, err := helm.NewLocalFileRenderer(
|
||||
helm.WithName(newOpts.ChartName),
|
||||
helm.WithNamespace(newOpts.Namespace),
|
||||
helm.WithRepoURL(newOpts.RepoURL),
|
||||
helm.WithVersion(newOpts.Version),
|
||||
helm.WithFS(manifests.BuiltinOrDir("")),
|
||||
helm.WithDir(chartDir),
|
||||
helm.WithCapabilities(newOpts.Capabilities),
|
||||
helm.WithRestConfig(kubeCli.RESTConfig()),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gatewayAPIComponent := &GatewayAPIComponent{
|
||||
profile: profile,
|
||||
renderer: renderer,
|
||||
opts: newOpts,
|
||||
writer: writer,
|
||||
kubeCli: kubeCli,
|
||||
}
|
||||
return gatewayAPIComponent, nil
|
||||
}
|
||||
|
||||
func (i *GatewayAPIComponent) ComponentName() ComponentName {
|
||||
return GatewayAPI
|
||||
}
|
||||
|
||||
func (i *GatewayAPIComponent) Namespace() string {
|
||||
return i.opts.Namespace
|
||||
}
|
||||
|
||||
func (i *GatewayAPIComponent) Enabled() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (i *GatewayAPIComponent) Run() error {
|
||||
if !i.opts.Quiet {
|
||||
fmt.Fprintf(i.writer, "🏄 Downloading GatewayAPI Yaml Files version: %s, url: %s\n", i.opts.Version, i.opts.RepoURL)
|
||||
}
|
||||
if err := i.renderer.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
i.started = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *GatewayAPIComponent) RenderManifest() (string, error) {
|
||||
if !i.started {
|
||||
return "", nil
|
||||
}
|
||||
if !i.opts.Quiet {
|
||||
fmt.Fprintf(i.writer, "📦 Rendering GatewayAPI Yaml Files\n")
|
||||
}
|
||||
values := make(map[string]any)
|
||||
manifest, err := renderComponentManifest(values, i.renderer, false, i.ComponentName(), i.opts.Namespace)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return manifest, nil
|
||||
}
|
||||
93
pkg/cmd/hgctl/installer/helm_agent.go
Normal file
93
pkg/cmd/hgctl/installer/helm_agent.go
Normal file
@@ -0,0 +1,93 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package installer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/helm"
|
||||
"github.com/alibaba/higress/pkg/cmd/options"
|
||||
)
|
||||
|
||||
type HelmRelease struct {
|
||||
appVersion string `json:"app_version,omitempty"`
|
||||
chart string `json:"chart,omitempty"`
|
||||
name string `json:"name,omitempty"`
|
||||
namespace string `json:"namespace,omitempty"`
|
||||
revision string `json:"revision,omitempty"`
|
||||
status string `json:"status,omitempty"`
|
||||
updated string `json:"updated,omitempty"`
|
||||
}
|
||||
|
||||
type HelmAgent struct {
|
||||
profile *helm.Profile
|
||||
writer io.Writer
|
||||
helmBinaryName string
|
||||
quiet bool
|
||||
}
|
||||
|
||||
func NewHelmAgent(profile *helm.Profile, writer io.Writer, quiet bool) *HelmAgent {
|
||||
return &HelmAgent{
|
||||
profile: profile,
|
||||
writer: writer,
|
||||
helmBinaryName: "helm",
|
||||
quiet: quiet,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HelmAgent) IsHigressInstalled() (bool, error) {
|
||||
args := []string{"list", "-n", h.profile.Global.Namespace, "-f", "higress"}
|
||||
if len(*options.DefaultConfigFlags.KubeConfig) > 0 {
|
||||
args = append(args, fmt.Sprintf("--kubeconfig=%s", *options.DefaultConfigFlags.KubeConfig))
|
||||
}
|
||||
if len(*options.DefaultConfigFlags.Context) > 0 {
|
||||
args = append(args, fmt.Sprintf("--kube-context=%s", *options.DefaultConfigFlags.Context))
|
||||
}
|
||||
if !h.quiet {
|
||||
fmt.Fprintf(h.writer, "\n📦 Running command: %s %s\n\n", h.helmBinaryName, strings.Join(args, " "))
|
||||
}
|
||||
cmd := exec.Command(h.helmBinaryName, args...)
|
||||
var out bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
cmd.Stderr = &stderr
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
done := make(chan error, 1)
|
||||
go func() {
|
||||
done <- cmd.Wait()
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-done:
|
||||
if err == nil {
|
||||
content := out.String()
|
||||
if !h.quiet {
|
||||
fmt.Fprintf(h.writer, "\n%s\n", content)
|
||||
}
|
||||
if strings.Contains(content, "deployed") {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
127
pkg/cmd/hgctl/installer/higress.go
Normal file
127
pkg/cmd/hgctl/installer/higress.go
Normal file
@@ -0,0 +1,127 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package installer
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/helm"
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/kubernetes"
|
||||
)
|
||||
|
||||
const (
|
||||
Higress ComponentName = "higress"
|
||||
)
|
||||
|
||||
type HigressComponent struct {
|
||||
profile *helm.Profile
|
||||
started bool
|
||||
opts *ComponentOptions
|
||||
renderer helm.Renderer
|
||||
writer io.Writer
|
||||
kubeCli kubernetes.CLIClient
|
||||
}
|
||||
|
||||
func (h *HigressComponent) ComponentName() ComponentName {
|
||||
return Higress
|
||||
}
|
||||
|
||||
func (h *HigressComponent) Namespace() string {
|
||||
return h.opts.Namespace
|
||||
}
|
||||
|
||||
func (h *HigressComponent) Enabled() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (h *HigressComponent) Run() error {
|
||||
// Parse latest version
|
||||
if h.opts.Version == helm.RepoLatestVersion {
|
||||
|
||||
latestVersion, err := helm.ParseLatestVersion(h.opts.RepoURL, h.opts.Version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !h.opts.Quiet {
|
||||
fmt.Fprintf(h.writer, "⚡️ Fetching Higress Helm Chart latest version \"%s\" \n", latestVersion)
|
||||
}
|
||||
|
||||
// Reset Helm Chart version
|
||||
h.opts.Version = latestVersion
|
||||
h.renderer.SetVersion(latestVersion)
|
||||
}
|
||||
if !h.opts.Quiet {
|
||||
fmt.Fprintf(h.writer, "🏄 Downloading Higress Helm Chart version: %s, url: %s\n", h.opts.Version, h.opts.RepoURL)
|
||||
}
|
||||
if err := h.renderer.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
h.profile.HigressVersion = h.opts.Version
|
||||
h.started = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *HigressComponent) RenderManifest() (string, error) {
|
||||
if !h.started {
|
||||
return "", nil
|
||||
}
|
||||
if !h.opts.Quiet {
|
||||
fmt.Fprintf(h.writer, "📦 Rendering Higress Helm Chart\n")
|
||||
}
|
||||
valsYaml, err := h.profile.ValuesYaml()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
manifest, err2 := renderComponentManifest(valsYaml, h.renderer, true, h.ComponentName(), h.opts.Namespace)
|
||||
if err2 != nil {
|
||||
return "", err
|
||||
}
|
||||
return manifest, nil
|
||||
}
|
||||
|
||||
func NewHigressComponent(kubeCli kubernetes.CLIClient, profile *helm.Profile, writer io.Writer, opts ...ComponentOption) (Component, error) {
|
||||
newOpts := &ComponentOptions{}
|
||||
for _, opt := range opts {
|
||||
opt(newOpts)
|
||||
}
|
||||
|
||||
if len(newOpts.RepoURL) == 0 {
|
||||
return nil, errors.New("Higress helm chart url can't be empty")
|
||||
}
|
||||
|
||||
// Higress can only be installed by remote type
|
||||
renderer, err := helm.NewRemoteRenderer(
|
||||
helm.WithName(newOpts.ChartName),
|
||||
helm.WithNamespace(newOpts.Namespace),
|
||||
helm.WithRepoURL(newOpts.RepoURL),
|
||||
helm.WithVersion(newOpts.Version),
|
||||
helm.WithCapabilities(newOpts.Capabilities),
|
||||
helm.WithRestConfig(kubeCli.RESTConfig()),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
higressComponent := &HigressComponent{
|
||||
profile: profile,
|
||||
renderer: renderer,
|
||||
opts: newOpts,
|
||||
writer: writer,
|
||||
kubeCli: kubeCli,
|
||||
}
|
||||
return higressComponent, nil
|
||||
}
|
||||
130
pkg/cmd/hgctl/installer/installer.go
Normal file
130
pkg/cmd/hgctl/installer/installer.go
Normal file
@@ -0,0 +1,130 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package installer
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/helm"
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/kubernetes"
|
||||
"github.com/alibaba/higress/pkg/cmd/options"
|
||||
"k8s.io/client-go/util/homedir"
|
||||
)
|
||||
|
||||
const (
|
||||
HgctlHomeDirPath = ".hgctl"
|
||||
StandaloneInstalledPath = "higress-standalone"
|
||||
ProfileInstalledPath = "profiles"
|
||||
InstalledYamlFileName = "install.yaml"
|
||||
DefaultGatewayAPINamespace = "gateway-system"
|
||||
DefaultIstioNamespace = "istio-system"
|
||||
)
|
||||
|
||||
type Installer interface {
|
||||
Install() error
|
||||
UnInstall() error
|
||||
Upgrade() error
|
||||
}
|
||||
|
||||
func NewInstaller(profile *helm.Profile, writer io.Writer, quiet bool) (Installer, error) {
|
||||
switch profile.Global.Install {
|
||||
case helm.InstallK8s, helm.InstallLocalK8s:
|
||||
cliClient, err := kubernetes.NewCLIClient(options.DefaultConfigFlags.ToRawKubeConfigLoader())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build kubernetes client: %w", err)
|
||||
}
|
||||
installer, err := NewK8sInstaller(profile, cliClient, writer, quiet)
|
||||
return installer, err
|
||||
case helm.InstallLocalDocker:
|
||||
installer, err := NewDockerInstaller(profile, writer, quiet)
|
||||
return installer, err
|
||||
default:
|
||||
return nil, errors.New("install is not supported")
|
||||
}
|
||||
}
|
||||
|
||||
func GetHomeDir() (string, error) {
|
||||
home := homedir.HomeDir()
|
||||
if home == "" {
|
||||
return "", fmt.Errorf("No user home environment variable found for OS %s", runtime.GOOS)
|
||||
}
|
||||
|
||||
return home, nil
|
||||
}
|
||||
|
||||
func GetHgctlPath() (string, error) {
|
||||
home, err := GetHomeDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
hgctlPath := filepath.Join(home, HgctlHomeDirPath)
|
||||
if _, err := os.Stat(hgctlPath); os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(hgctlPath, os.ModePerm); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
return hgctlPath, nil
|
||||
}
|
||||
|
||||
func GetDefaultInstallPackagePath() (string, error) {
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
path := filepath.Join(dir, StandaloneInstalledPath)
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(path, os.ModePerm); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
return path, err
|
||||
}
|
||||
|
||||
func GetProfileInstalledPath() (string, error) {
|
||||
hgctlPath, err := GetHgctlPath()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
profilesPath := filepath.Join(hgctlPath, ProfileInstalledPath)
|
||||
if _, err := os.Stat(profilesPath); os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(profilesPath, os.ModePerm); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
return profilesPath, nil
|
||||
}
|
||||
|
||||
func GetInstalledYamlPath() (string, bool) {
|
||||
profileInstalledPath, err := GetProfileInstalledPath()
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
installedYamlFile := filepath.Join(profileInstalledPath, InstalledYamlFileName)
|
||||
if _, err := os.Stat(installedYamlFile); os.IsNotExist(err) {
|
||||
return installedYamlFile, false
|
||||
}
|
||||
return installedYamlFile, true
|
||||
}
|
||||
112
pkg/cmd/hgctl/installer/installer_docker.go
Normal file
112
pkg/cmd/hgctl/installer/installer_docker.go
Normal file
@@ -0,0 +1,112 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package installer
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/helm"
|
||||
)
|
||||
|
||||
type DockerInstaller struct {
|
||||
started bool
|
||||
standalone *StandaloneComponent
|
||||
profile *helm.Profile
|
||||
writer io.Writer
|
||||
profileStore ProfileStore
|
||||
}
|
||||
|
||||
func (d *DockerInstaller) Install() error {
|
||||
fmt.Fprintf(d.writer, "\n⌛️ Processing installation... \n\n")
|
||||
|
||||
if err := d.standalone.Install(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
profileName, err1 := d.profileStore.Save(d.profile)
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
fmt.Fprintf(d.writer, "\n✔️ Wrote Profile: \"%s\" \n", profileName)
|
||||
|
||||
fmt.Fprintf(d.writer, "\n🎊 Install All Resources Complete!\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DockerInstaller) UnInstall() error {
|
||||
|
||||
fmt.Fprintf(d.writer, "\n⌛️ Processing uninstallation... \n\n")
|
||||
|
||||
if err := d.standalone.UnInstall(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
profileName, err1 := d.profileStore.Delete(d.profile)
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
fmt.Fprintf(d.writer, "\n✔️ Removed Profile: \"%s\" \n", profileName)
|
||||
|
||||
fmt.Fprintf(d.writer, "\n🎊 Uninstall All Resources Complete!\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DockerInstaller) Upgrade() error {
|
||||
fmt.Fprintf(d.writer, "\n⌛️ Processing upgrade... \n\n")
|
||||
|
||||
if err := d.standalone.Upgrade(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(d.writer, "\n🎊 Install All Resources Complete!\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewDockerInstaller(profile *helm.Profile, writer io.Writer, quiet bool) (*DockerInstaller, error) {
|
||||
if profile == nil {
|
||||
return nil, errors.New("install profile is empty")
|
||||
}
|
||||
// initialize components
|
||||
opts := []ComponentOption{
|
||||
WithComponentVersion(profile.Charts.Standalone.Version),
|
||||
WithComponentRepoURL(profile.Charts.Standalone.Url),
|
||||
WithComponentChartName(profile.Charts.Standalone.Name),
|
||||
}
|
||||
if quiet {
|
||||
opts = append(opts, WithQuiet())
|
||||
}
|
||||
standaloneComponent, err := NewStandaloneComponent(profile, writer, opts...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("NewStandaloneComponent failed, err: %s", err)
|
||||
}
|
||||
|
||||
profileInstalledPath, err1 := GetProfileInstalledPath()
|
||||
if err1 != nil {
|
||||
return nil, err1
|
||||
}
|
||||
profileStore, err2 := NewFileDirProfileStore(profileInstalledPath)
|
||||
if err2 != nil {
|
||||
return nil, err2
|
||||
}
|
||||
op := &DockerInstaller{
|
||||
profile: profile,
|
||||
standalone: standaloneComponent,
|
||||
writer: writer,
|
||||
profileStore: profileStore,
|
||||
}
|
||||
return op, nil
|
||||
}
|
||||
343
pkg/cmd/hgctl/installer/installer_k8s.go
Normal file
343
pkg/cmd/hgctl/installer/installer_k8s.go
Normal file
@@ -0,0 +1,343 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package installer
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/helm"
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/helm/object"
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/kubernetes"
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/util"
|
||||
)
|
||||
|
||||
type K8sInstaller struct {
|
||||
started bool
|
||||
components map[ComponentName]Component
|
||||
kubeCli kubernetes.CLIClient
|
||||
profile *helm.Profile
|
||||
writer io.Writer
|
||||
profileStore ProfileStore
|
||||
}
|
||||
|
||||
func (o *K8sInstaller) Install() error {
|
||||
// check if higress is installed by helm
|
||||
fmt.Fprintf(o.writer, "\n⌛️ Detecting higress installed by helm or not... \n\n")
|
||||
helmAgent := NewHelmAgent(o.profile, o.writer, false)
|
||||
if helmInstalled, _ := helmAgent.IsHigressInstalled(); helmInstalled {
|
||||
fmt.Fprintf(o.writer, "\n🧐 You have already installed higress by helm, please use \"helm upgrade\" to upgrade higress!\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := o.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
manifestMap, err := o.RenderManifests()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(o.writer, "\n⌛️ Processing installation... \n\n")
|
||||
if err := o.ApplyManifests(manifestMap); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
profileName, err1 := o.profileStore.Save(o.profile)
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
fmt.Fprintf(o.writer, "\n✔️ Wrote Profile in kubernetes configmap: \"%s\" \n", profileName)
|
||||
fmt.Fprintf(o.writer, "\n Use bellow kubectl command to edit profile for upgrade. \n")
|
||||
fmt.Fprintf(o.writer, " ================================================================================== \n")
|
||||
names := strings.Split(profileName, "/")
|
||||
fmt.Fprintf(o.writer, " kubectl edit configmap %s -n %s \n", names[1], names[0])
|
||||
fmt.Fprintf(o.writer, " ================================================================================== \n")
|
||||
|
||||
fmt.Fprintf(o.writer, "\n🎊 Install All Resources Complete!\n")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *K8sInstaller) UnInstall() error {
|
||||
if _, err := GetProfileInstalledPath(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := o.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
manifestMap, err := o.RenderManifests()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(o.writer, "\n⌛️ Processing uninstallation... \n\n")
|
||||
if err := o.DeleteManifests(manifestMap); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
profileName, err1 := o.profileStore.Delete(o.profile)
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
fmt.Fprintf(o.writer, "\n✔️ Removed Profile: \"%s\" \n", profileName)
|
||||
|
||||
fmt.Fprintf(o.writer, "\n🎊 Uninstall All Resources Complete!\n")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *K8sInstaller) Upgrade() error {
|
||||
return o.Install()
|
||||
}
|
||||
|
||||
// Run must be invoked before invoking other functions.
|
||||
func (o *K8sInstaller) Run() error {
|
||||
for name, component := range o.components {
|
||||
if !component.Enabled() {
|
||||
continue
|
||||
}
|
||||
if err := component.Run(); err != nil {
|
||||
return fmt.Errorf("component %s run failed, err: %s", name, err)
|
||||
}
|
||||
}
|
||||
o.started = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// RenderManifests renders component manifests specified by profile.
|
||||
func (o *K8sInstaller) RenderManifests() (map[ComponentName]string, error) {
|
||||
if !o.started {
|
||||
return nil, errors.New("higress installer is not running")
|
||||
}
|
||||
res := make(map[ComponentName]string)
|
||||
for name, component := range o.components {
|
||||
if !component.Enabled() {
|
||||
continue
|
||||
}
|
||||
manifest, err := component.RenderManifest()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("component %s RenderManifest err: %v", name, err)
|
||||
}
|
||||
res[name] = manifest
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// GenerateManifests generates component manifests to k8s cluster
|
||||
func (o *K8sInstaller) GenerateManifests(manifestMap map[ComponentName]string) error {
|
||||
if o.kubeCli == nil {
|
||||
return errors.New("no injected k8s cli into K8sInstaller")
|
||||
}
|
||||
for _, manifest := range manifestMap {
|
||||
fmt.Fprint(o.writer, manifest)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ApplyManifests apply component manifests to k8s cluster
|
||||
func (o *K8sInstaller) ApplyManifests(manifestMap map[ComponentName]string) error {
|
||||
if o.kubeCli == nil {
|
||||
return errors.New("no injected k8s cli into K8sInstaller")
|
||||
}
|
||||
for name, manifest := range manifestMap {
|
||||
namespace := o.components[name].Namespace()
|
||||
if err := o.applyManifest(manifest, namespace); err != nil {
|
||||
return fmt.Errorf("component %s ApplyManifest err: %v", name, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *K8sInstaller) applyManifest(manifest string, ns string) error {
|
||||
if err := o.kubeCli.CreateNamespace(ns); err != nil {
|
||||
return err
|
||||
}
|
||||
objs, err := object.ParseK8sObjectsFromYAMLManifest(manifest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, obj := range objs {
|
||||
// check namespaced object if namespace property has been existed
|
||||
if obj.Namespace == "" && o.isNamespacedObject(obj) {
|
||||
obj.Namespace = ns
|
||||
obj.UnstructuredObject().SetNamespace(ns)
|
||||
}
|
||||
if o.isNamespacedObject(obj) {
|
||||
fmt.Fprintf(o.writer, "✔️ Installed %s:%s:%s.\n", obj.Kind, obj.Name, obj.Namespace)
|
||||
} else {
|
||||
fmt.Fprintf(o.writer, "✔️ Installed %s::%s.\n", obj.Kind, obj.Name)
|
||||
}
|
||||
if err := o.kubeCli.ApplyObject(obj.UnstructuredObject()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteManifests delete component manifests to k8s cluster
|
||||
func (o *K8sInstaller) DeleteManifests(manifestMap map[ComponentName]string) error {
|
||||
if o.kubeCli == nil {
|
||||
return errors.New("no injected k8s cli into K8sInstaller")
|
||||
}
|
||||
for name, manifest := range manifestMap {
|
||||
namespace := o.components[name].Namespace()
|
||||
if err := o.deleteManifest(manifest, namespace); err != nil {
|
||||
return fmt.Errorf("component %s DeleteManifest err: %v", name, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteManifests write component manifests to local files
|
||||
func (o *K8sInstaller) WriteManifests(manifestMap map[ComponentName]string) error {
|
||||
if o.kubeCli == nil {
|
||||
return errors.New("no injected k8s cli into K8sInstaller")
|
||||
}
|
||||
rootPath, _ := os.Getwd()
|
||||
for name, manifest := range manifestMap {
|
||||
fileName := filepath.Join(rootPath, string(name)+".yaml")
|
||||
util.WriteFileString(fileName, manifest, 0o644)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// deleteManifest delete manifest to certain namespace
|
||||
func (o *K8sInstaller) deleteManifest(manifest string, ns string) error {
|
||||
objs, err := object.ParseK8sObjectsFromYAMLManifest(manifest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, obj := range objs {
|
||||
// check namespaced object if namespace property has been existed
|
||||
if obj.Namespace == "" && o.isNamespacedObject(obj) {
|
||||
obj.Namespace = ns
|
||||
obj.UnstructuredObject().SetNamespace(ns)
|
||||
}
|
||||
if o.isNamespacedObject(obj) {
|
||||
fmt.Fprintf(o.writer, "✔️ Removed %s:%s:%s.\n", obj.Kind, obj.Name, obj.Namespace)
|
||||
} else {
|
||||
fmt.Fprintf(o.writer, "✔️ Removed %s::%s.\n", obj.Kind, obj.Name)
|
||||
}
|
||||
if err := o.kubeCli.DeleteObject(obj.UnstructuredObject()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *K8sInstaller) isNamespacedObject(obj *object.K8sObject) bool {
|
||||
if obj.Kind != "CustomResourceDefinition" && obj.Kind != "ClusterRole" && obj.Kind != "ClusterRoleBinding" {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func NewK8sInstaller(profile *helm.Profile, cli kubernetes.CLIClient, writer io.Writer, quiet bool) (*K8sInstaller, error) {
|
||||
if profile == nil {
|
||||
return nil, errors.New("install profile is empty")
|
||||
}
|
||||
// initialize server info
|
||||
serverInfo, _ := NewServerInfo(cli)
|
||||
fmt.Fprintf(writer, "\n⌛️ Detecting kubernetes version ... ")
|
||||
capabilities, err := serverInfo.GetCapabilities()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmt.Fprintf(writer, "%s\n", capabilities.KubeVersion.Version)
|
||||
// initialize components
|
||||
components := make(map[ComponentName]Component)
|
||||
opts := []ComponentOption{
|
||||
WithComponentNamespace(profile.Global.Namespace),
|
||||
WithComponentChartPath(profile.InstallPackagePath),
|
||||
WithComponentVersion(profile.Charts.Higress.Version),
|
||||
WithComponentRepoURL(profile.Charts.Higress.Url),
|
||||
WithComponentChartName(profile.Charts.Higress.Name),
|
||||
WithComponentCapabilities(capabilities),
|
||||
}
|
||||
if quiet {
|
||||
opts = append(opts, WithQuiet())
|
||||
}
|
||||
higressComponent, err := NewHigressComponent(cli, profile, writer, opts...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("NewHigressComponent failed, err: %s", err)
|
||||
}
|
||||
components[Higress] = higressComponent
|
||||
|
||||
if profile.IstioEnabled() {
|
||||
istioNamespace := profile.GetIstioNamespace()
|
||||
if len(istioNamespace) == 0 {
|
||||
istioNamespace = DefaultIstioNamespace
|
||||
}
|
||||
opts := []ComponentOption{
|
||||
WithComponentNamespace(istioNamespace),
|
||||
WithComponentVersion("1.18.2"),
|
||||
WithComponentRepoURL("embed://istiobase"),
|
||||
WithComponentChartName("istio"),
|
||||
WithComponentCapabilities(capabilities),
|
||||
}
|
||||
if quiet {
|
||||
opts = append(opts, WithQuiet())
|
||||
}
|
||||
|
||||
istioCRDComponent, err := NewIstioCRDComponent(cli, profile, writer, opts...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("NewIstioCRDComponent failed, err: %s", err)
|
||||
}
|
||||
components[Istio] = istioCRDComponent
|
||||
}
|
||||
|
||||
if profile.GatewayAPIEnabled() {
|
||||
opts := []ComponentOption{
|
||||
WithComponentNamespace(DefaultGatewayAPINamespace),
|
||||
WithComponentVersion("1.0.0"),
|
||||
WithComponentRepoURL("embed://gatewayapi"),
|
||||
WithComponentChartName("gatewayAPI"),
|
||||
WithComponentCapabilities(capabilities),
|
||||
}
|
||||
if quiet {
|
||||
opts = append(opts, WithQuiet())
|
||||
}
|
||||
|
||||
gatewayAPIComponent, err := NewGatewayAPIComponent(cli, profile, writer, opts...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("NewGatewayAPIComponent failed, err: %s", err)
|
||||
}
|
||||
components[GatewayAPI] = gatewayAPIComponent
|
||||
}
|
||||
|
||||
profileStore, err := NewConfigmapProfileStore(cli)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
op := &K8sInstaller{
|
||||
profile: profile,
|
||||
components: components,
|
||||
kubeCli: cli,
|
||||
writer: writer,
|
||||
profileStore: profileStore,
|
||||
}
|
||||
return op, nil
|
||||
}
|
||||
125
pkg/cmd/hgctl/installer/istio.go
Normal file
125
pkg/cmd/hgctl/installer/istio.go
Normal file
@@ -0,0 +1,125 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package installer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/helm"
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/kubernetes"
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/manifests"
|
||||
)
|
||||
|
||||
const (
|
||||
Istio ComponentName = "istio"
|
||||
)
|
||||
|
||||
type IstioCRDComponent struct {
|
||||
profile *helm.Profile
|
||||
started bool
|
||||
opts *ComponentOptions
|
||||
renderer helm.Renderer
|
||||
writer io.Writer
|
||||
kubeCli kubernetes.CLIClient
|
||||
}
|
||||
|
||||
func NewIstioCRDComponent(kubeCli kubernetes.CLIClient, profile *helm.Profile, writer io.Writer, opts ...ComponentOption) (Component, error) {
|
||||
newOpts := &ComponentOptions{}
|
||||
for _, opt := range opts {
|
||||
opt(newOpts)
|
||||
}
|
||||
|
||||
var renderer helm.Renderer
|
||||
var err error
|
||||
|
||||
// Istio can be installed by embed type or remote type
|
||||
if strings.HasPrefix(newOpts.RepoURL, "embed://") {
|
||||
chartDir := strings.TrimPrefix(newOpts.RepoURL, "embed://")
|
||||
renderer, err = helm.NewLocalChartRenderer(
|
||||
helm.WithName(newOpts.ChartName),
|
||||
helm.WithNamespace(newOpts.Namespace),
|
||||
helm.WithRepoURL(newOpts.RepoURL),
|
||||
helm.WithVersion(newOpts.Version),
|
||||
helm.WithFS(manifests.BuiltinOrDir("")),
|
||||
helm.WithDir(chartDir),
|
||||
helm.WithCapabilities(newOpts.Capabilities),
|
||||
helm.WithRestConfig(kubeCli.RESTConfig()),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
renderer, err = helm.NewRemoteRenderer(
|
||||
helm.WithName(newOpts.ChartName),
|
||||
helm.WithNamespace(newOpts.Namespace),
|
||||
helm.WithRepoURL(newOpts.RepoURL),
|
||||
helm.WithVersion(newOpts.Version),
|
||||
helm.WithCapabilities(newOpts.Capabilities),
|
||||
helm.WithRestConfig(kubeCli.RESTConfig()),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
istioComponent := &IstioCRDComponent{
|
||||
profile: profile,
|
||||
renderer: renderer,
|
||||
opts: newOpts,
|
||||
writer: writer,
|
||||
kubeCli: kubeCli,
|
||||
}
|
||||
return istioComponent, nil
|
||||
}
|
||||
|
||||
func (i *IstioCRDComponent) ComponentName() ComponentName {
|
||||
return Istio
|
||||
}
|
||||
|
||||
func (i *IstioCRDComponent) Namespace() string {
|
||||
return i.opts.Namespace
|
||||
}
|
||||
|
||||
func (i *IstioCRDComponent) Enabled() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (i *IstioCRDComponent) Run() error {
|
||||
if !i.opts.Quiet {
|
||||
fmt.Fprintf(i.writer, "🏄 Downloading Istio Helm Chart version: %s, url: %s\n", i.opts.Version, i.opts.RepoURL)
|
||||
}
|
||||
if err := i.renderer.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
i.started = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *IstioCRDComponent) RenderManifest() (string, error) {
|
||||
if !i.started {
|
||||
return "", nil
|
||||
}
|
||||
if !i.opts.Quiet {
|
||||
fmt.Fprintf(i.writer, "📦 Rendering Istio Helm Chart\n")
|
||||
}
|
||||
values := make(map[string]any)
|
||||
manifest, err := renderComponentManifest(values, i.renderer, false, i.ComponentName(), i.opts.Namespace)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return manifest, nil
|
||||
}
|
||||
247
pkg/cmd/hgctl/installer/profile_store.go
Normal file
247
pkg/cmd/hgctl/installer/profile_store.go
Normal file
@@ -0,0 +1,247 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package installer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/helm"
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/kubernetes"
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/util"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
ProfileConfigmapKey = "profile"
|
||||
ProfileConfigmapName = "higress-profile"
|
||||
ProfileConfigmapAnnotation = "higress.io/install"
|
||||
ProfileFilePrefix = "install"
|
||||
)
|
||||
|
||||
type ProfileContext struct {
|
||||
Profile *helm.Profile
|
||||
SourceType string
|
||||
Namespace string
|
||||
PathOrName string
|
||||
Install helm.InstallMode
|
||||
HigressVersion string
|
||||
}
|
||||
|
||||
type ProfileStore interface {
|
||||
Save(profile *helm.Profile) (string, error)
|
||||
List() ([]*ProfileContext, error)
|
||||
Delete(profile *helm.Profile) (string, error)
|
||||
}
|
||||
|
||||
type FileDirProfileStore struct {
|
||||
profilesPath string
|
||||
}
|
||||
|
||||
func (f *FileDirProfileStore) Save(profile *helm.Profile) (string, error) {
|
||||
namespace := profile.Global.Namespace
|
||||
install := profile.Global.Install
|
||||
var profileName = ""
|
||||
if install == helm.InstallK8s || install == helm.InstallLocalK8s {
|
||||
profileName = filepath.Join(f.profilesPath, fmt.Sprintf("%s-%s.yaml", ProfileFilePrefix, namespace))
|
||||
} else {
|
||||
profileName = filepath.Join(f.profilesPath, fmt.Sprintf("%s-%s.yaml", ProfileFilePrefix, install))
|
||||
}
|
||||
if err := util.WriteFileString(profileName, util.ToYAML(profile), 0o644); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return profileName, nil
|
||||
}
|
||||
|
||||
func (f *FileDirProfileStore) List() ([]*ProfileContext, error) {
|
||||
profileContexts := make([]*ProfileContext, 0)
|
||||
dir, err := os.ReadDir(f.profilesPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, file := range dir {
|
||||
if !strings.HasSuffix(file.Name(), ".yaml") {
|
||||
continue
|
||||
}
|
||||
if file.IsDir() {
|
||||
continue
|
||||
}
|
||||
fileName := filepath.Join(f.profilesPath, file.Name())
|
||||
content, err2 := os.ReadFile(fileName)
|
||||
if err2 != nil {
|
||||
continue
|
||||
}
|
||||
profile, err3 := helm.UnmarshalProfile(string(content))
|
||||
if err3 != nil {
|
||||
continue
|
||||
}
|
||||
profileContext := &ProfileContext{
|
||||
Profile: profile,
|
||||
Namespace: profile.Global.Namespace,
|
||||
Install: profile.Global.Install,
|
||||
HigressVersion: profile.HigressVersion,
|
||||
SourceType: "file",
|
||||
PathOrName: fileName,
|
||||
}
|
||||
profileContexts = append(profileContexts, profileContext)
|
||||
}
|
||||
return profileContexts, nil
|
||||
}
|
||||
|
||||
func (f *FileDirProfileStore) Delete(profile *helm.Profile) (string, error) {
|
||||
namespace := profile.Global.Namespace
|
||||
install := profile.Global.Install
|
||||
var profileName = ""
|
||||
if install == helm.InstallK8s || install == helm.InstallLocalK8s {
|
||||
profileName = filepath.Join(f.profilesPath, fmt.Sprintf("%s-%s.yaml", ProfileFilePrefix, namespace))
|
||||
} else {
|
||||
profileName = filepath.Join(f.profilesPath, fmt.Sprintf("%s-%s.yaml", ProfileFilePrefix, install))
|
||||
}
|
||||
if err := os.Remove(profileName); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return profileName, nil
|
||||
}
|
||||
|
||||
func NewFileDirProfileStore(profilesPath string) (ProfileStore, error) {
|
||||
if _, err := os.Stat(profilesPath); os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(profilesPath, os.ModePerm); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
profileStore := &FileDirProfileStore{
|
||||
profilesPath: profilesPath,
|
||||
}
|
||||
return profileStore, nil
|
||||
}
|
||||
|
||||
type ConfigmapProfileStore struct {
|
||||
kubeCli kubernetes.CLIClient
|
||||
}
|
||||
|
||||
func (c *ConfigmapProfileStore) Save(profile *helm.Profile) (string, error) {
|
||||
bytes, err := json.Marshal(profile)
|
||||
jsonProfile := ""
|
||||
if err == nil {
|
||||
jsonProfile = string(bytes)
|
||||
}
|
||||
annotation := make(map[string]string, 0)
|
||||
annotation[ProfileConfigmapAnnotation] = jsonProfile
|
||||
configmap := &corev1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: profile.Global.Namespace,
|
||||
Name: ProfileConfigmapName,
|
||||
Annotations: annotation,
|
||||
},
|
||||
}
|
||||
configmap.Data = make(map[string]string, 0)
|
||||
configmap.Data[ProfileConfigmapKey] = util.ToYAML(profile)
|
||||
name := fmt.Sprintf("%s/%s", profile.Global.Namespace, ProfileConfigmapName)
|
||||
if err := c.applyConfigmap(configmap); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return name, nil
|
||||
}
|
||||
|
||||
func (c *ConfigmapProfileStore) List() ([]*ProfileContext, error) {
|
||||
profileContexts := make([]*ProfileContext, 0)
|
||||
configmapList, err := c.listConfigmaps(ProfileConfigmapName, "", 100)
|
||||
if err != nil {
|
||||
return profileContexts, err
|
||||
}
|
||||
for _, configmap := range configmapList.Items {
|
||||
if data, ok := configmap.Data[ProfileConfigmapKey]; ok {
|
||||
profile, err := helm.UnmarshalProfile(data)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
profileContext := &ProfileContext{
|
||||
Profile: profile,
|
||||
Namespace: profile.Global.Namespace,
|
||||
Install: profile.Global.Install,
|
||||
HigressVersion: profile.HigressVersion,
|
||||
SourceType: "configmap",
|
||||
PathOrName: fmt.Sprintf("%s/%s", profile.Global.Namespace, configmap.Name),
|
||||
}
|
||||
profileContexts = append(profileContexts, profileContext)
|
||||
}
|
||||
}
|
||||
return profileContexts, nil
|
||||
}
|
||||
|
||||
func (c *ConfigmapProfileStore) Delete(profile *helm.Profile) (string, error) {
|
||||
configmap := &corev1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: profile.Global.Namespace,
|
||||
Name: ProfileConfigmapName,
|
||||
},
|
||||
}
|
||||
name := fmt.Sprintf("%s/%s", profile.Global.Namespace, ProfileConfigmapName)
|
||||
if err := c.deleteConfigmap(configmap); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return name, nil
|
||||
}
|
||||
|
||||
func (c *ConfigmapProfileStore) listConfigmaps(name string, namespace string, size int64) (*corev1.ConfigMapList, error) {
|
||||
var result *corev1.ConfigMapList
|
||||
var err error
|
||||
if len(namespace) == 0 {
|
||||
result, err = c.kubeCli.KubernetesInterface().CoreV1().ConfigMaps("").List(context.Background(), metav1.ListOptions{Limit: size, FieldSelector: fmt.Sprintf("metadata.name=%s", name)})
|
||||
} else {
|
||||
result, err = c.kubeCli.KubernetesInterface().CoreV1().ConfigMaps(namespace).List(context.Background(), metav1.ListOptions{Limit: size, FieldSelector: fmt.Sprintf("metadata.name=%s", name)})
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (c *ConfigmapProfileStore) applyConfigmap(configmap *corev1.ConfigMap) error {
|
||||
_, err := c.kubeCli.KubernetesInterface().CoreV1().ConfigMaps(configmap.Namespace).Get(context.Background(), configmap.Name, metav1.GetOptions{})
|
||||
if err != nil && errors.IsNotFound(err) {
|
||||
_, err = c.kubeCli.KubernetesInterface().CoreV1().ConfigMaps(configmap.Namespace).Create(context.Background(), configmap, metav1.CreateOptions{})
|
||||
return err
|
||||
} else if err != nil {
|
||||
return err
|
||||
} else {
|
||||
_, err = c.kubeCli.KubernetesInterface().CoreV1().ConfigMaps(configmap.Namespace).Update(context.Background(), configmap, metav1.UpdateOptions{})
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ConfigmapProfileStore) deleteConfigmap(configmap *corev1.ConfigMap) error {
|
||||
err := c.kubeCli.KubernetesInterface().CoreV1().ConfigMaps(configmap.Namespace).Delete(context.Background(), configmap.Name, metav1.DeleteOptions{})
|
||||
if err != nil {
|
||||
if !errors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewConfigmapProfileStore(kubeCli kubernetes.CLIClient) (ProfileStore, error) {
|
||||
profileStore := &ConfigmapProfileStore{
|
||||
kubeCli: kubeCli,
|
||||
}
|
||||
return profileStore, nil
|
||||
}
|
||||
66
pkg/cmd/hgctl/installer/server_info.go
Normal file
66
pkg/cmd/hgctl/installer/server_info.go
Normal file
@@ -0,0 +1,66 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package installer
|
||||
|
||||
import (
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/kubernetes"
|
||||
"github.com/pkg/errors"
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"k8s.io/client-go/discovery"
|
||||
)
|
||||
|
||||
type ServerInfo struct {
|
||||
kubeCli kubernetes.CLIClient
|
||||
}
|
||||
|
||||
func (c *ServerInfo) GetCapabilities() (*chartutil.Capabilities, error) {
|
||||
// force a discovery cache invalidation to always fetch the latest server version/capabilities.
|
||||
dc := c.kubeCli.KubernetesInterface().Discovery()
|
||||
|
||||
kubeVersion, err := dc.ServerVersion()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get server version from Kubernetes")
|
||||
}
|
||||
// Issue #6361:
|
||||
// Client-Go emits an error when an API service is registered but unimplemented.
|
||||
// We trap that error here and print a warning. But since the discovery client continues
|
||||
// building the API object, it is correctly populated with all valid APIs.
|
||||
// See https://github.com/kubernetes/kubernetes/issues/72051#issuecomment-521157642
|
||||
apiVersions, err := action.GetVersionSet(dc)
|
||||
if err != nil {
|
||||
if discovery.IsGroupDiscoveryFailedError(err) {
|
||||
} else {
|
||||
return nil, errors.Wrap(err, "could not get apiVersions from Kubernetes")
|
||||
}
|
||||
}
|
||||
capabilities := &chartutil.Capabilities{
|
||||
APIVersions: apiVersions,
|
||||
KubeVersion: chartutil.KubeVersion{
|
||||
Version: kubeVersion.GitVersion,
|
||||
Major: kubeVersion.Major,
|
||||
Minor: kubeVersion.Minor,
|
||||
},
|
||||
HelmVersion: chartutil.DefaultCapabilities.HelmVersion,
|
||||
}
|
||||
return capabilities, nil
|
||||
}
|
||||
|
||||
func NewServerInfo(kubCli kubernetes.CLIClient) (*ServerInfo, error) {
|
||||
serverInfo := &ServerInfo{
|
||||
kubeCli: kubCli,
|
||||
}
|
||||
return serverInfo, nil
|
||||
}
|
||||
141
pkg/cmd/hgctl/installer/standalone.go
Normal file
141
pkg/cmd/hgctl/installer/standalone.go
Normal file
@@ -0,0 +1,141 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package installer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/helm"
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/util"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultHttpRequestTimeout = 15 * time.Second
|
||||
defaultHttpMaxTry = 3
|
||||
defaultHttpBufferSize = 1024 * 1024 * 2
|
||||
)
|
||||
|
||||
type StandaloneComponent struct {
|
||||
profile *helm.Profile
|
||||
started bool
|
||||
opts *ComponentOptions
|
||||
writer io.Writer
|
||||
httpFetcher *util.HTTPFetcher
|
||||
agent *Agent
|
||||
}
|
||||
|
||||
func (s *StandaloneComponent) Install() error {
|
||||
if !s.opts.Quiet {
|
||||
fmt.Fprintf(s.writer, "\n🏄 Downloading installer from %s\n", s.opts.RepoURL)
|
||||
}
|
||||
// download get-higress.sh
|
||||
data, err := s.httpFetcher.Fetch(context.Background(), s.opts.RepoURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// write installer binary shell
|
||||
if err := util.WriteFileString(s.agent.installBinaryName, string(data), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
// start to install higress
|
||||
if err := s.agent.Install(); err != nil {
|
||||
return err
|
||||
}
|
||||
// Set Higress version
|
||||
if version, err := s.agent.Version(); err == nil {
|
||||
s.profile.HigressVersion = version
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *StandaloneComponent) UnInstall() error {
|
||||
if err := s.agent.Uninstall(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *StandaloneComponent) Upgrade() error {
|
||||
if !s.opts.Quiet {
|
||||
fmt.Fprintf(s.writer, "\n🏄 Downloading installer from %s\n", s.opts.RepoURL)
|
||||
}
|
||||
// download get-higress.sh
|
||||
data, err := s.httpFetcher.Fetch(context.Background(), s.opts.RepoURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// write installer binary shell
|
||||
if err := util.WriteFileString(s.agent.installBinaryName, string(data), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
// start to upgrade higress
|
||||
if err := s.agent.Upgrade(); err != nil {
|
||||
return err
|
||||
}
|
||||
// Set Higress version
|
||||
if version, err := s.agent.Version(); err != nil {
|
||||
s.profile.HigressVersion = version
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewStandaloneComponent(profile *helm.Profile, writer io.Writer, opts ...ComponentOption) (*StandaloneComponent, error) {
|
||||
newOpts := &ComponentOptions{}
|
||||
for _, opt := range opts {
|
||||
opt(newOpts)
|
||||
}
|
||||
|
||||
httpFetcher := util.NewHTTPFetcher(defaultHttpRequestTimeout, defaultHttpMaxTry, defaultHttpBufferSize)
|
||||
if err := prepareProfile(profile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
agent := NewAgent(profile, writer, newOpts.Quiet)
|
||||
standaloneComponent := &StandaloneComponent{
|
||||
profile: profile,
|
||||
opts: newOpts,
|
||||
writer: writer,
|
||||
httpFetcher: httpFetcher,
|
||||
agent: agent,
|
||||
}
|
||||
return standaloneComponent, nil
|
||||
}
|
||||
|
||||
func prepareProfile(profile *helm.Profile) error {
|
||||
if len(profile.InstallPackagePath) == 0 {
|
||||
dir, err := GetDefaultInstallPackagePath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
profile.InstallPackagePath = dir
|
||||
}
|
||||
|
||||
if _, err := os.Stat(profile.InstallPackagePath); os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(profile.InstallPackagePath, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// parse INSTALLPACKAGEPATH in storage.url
|
||||
if strings.HasPrefix(profile.Storage.Url, "file://") {
|
||||
profile.Storage.Url = strings.ReplaceAll(profile.Storage.Url, "${INSTALLPACKAGEPATH}", profile.InstallPackagePath)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
356
pkg/cmd/hgctl/installer/standalone_agent.go
Normal file
356
pkg/cmd/hgctl/installer/standalone_agent.go
Normal file
@@ -0,0 +1,356 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package installer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/helm"
|
||||
)
|
||||
|
||||
type RunSudoState string
|
||||
|
||||
const (
|
||||
NoSudo RunSudoState = "NoSudo"
|
||||
SudoWithoutPassword RunSudoState = "SudoWithoutPassword"
|
||||
SudoWithPassword RunSudoState = "SudoWithPassword"
|
||||
)
|
||||
|
||||
type Agent struct {
|
||||
profile *helm.Profile
|
||||
writer io.Writer
|
||||
shutdownBinaryName string
|
||||
resetBinaryName string
|
||||
startupBinaryName string
|
||||
installBinaryName string
|
||||
installPath string
|
||||
configuredPath string
|
||||
higressPath string
|
||||
versionPath string
|
||||
quiet bool
|
||||
runSudoState RunSudoState
|
||||
}
|
||||
|
||||
func NewAgent(profile *helm.Profile, writer io.Writer, quiet bool) *Agent {
|
||||
installPath := profile.InstallPackagePath
|
||||
return &Agent{
|
||||
profile: profile,
|
||||
writer: writer,
|
||||
installPath: installPath,
|
||||
higressPath: filepath.Join(installPath, "higress"),
|
||||
installBinaryName: filepath.Join(installPath, "get-higress.sh"),
|
||||
shutdownBinaryName: filepath.Join(installPath, "higress", "bin", "shutdown.sh"),
|
||||
resetBinaryName: filepath.Join(installPath, "higress", "bin", "reset.sh"),
|
||||
startupBinaryName: filepath.Join(installPath, "higress", "bin", "startup.sh"),
|
||||
configuredPath: filepath.Join(installPath, "higress", "compose", ".configured"),
|
||||
versionPath: filepath.Join(installPath, "higress", "VERSION"),
|
||||
quiet: quiet,
|
||||
runSudoState: NoSudo,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Agent) profileArgs() []string {
|
||||
args := []string{
|
||||
fmt.Sprintf("--nacos-ns=%s", a.profile.Storage.Ns),
|
||||
fmt.Sprintf("--config-url=%s", a.profile.Storage.Url),
|
||||
fmt.Sprintf("--nacos-ns=%s", a.profile.Storage.Ns),
|
||||
fmt.Sprintf("--nacos-password=%s", a.profile.Storage.Password),
|
||||
fmt.Sprintf("--nacos-username=%s", a.profile.Storage.Username),
|
||||
fmt.Sprintf("--data-enc-key=%s", a.profile.Storage.DataEncKey),
|
||||
fmt.Sprintf("--console-port=%d", a.profile.Console.Port),
|
||||
fmt.Sprintf("--gateway-http-port=%d", a.profile.Gateway.HttpPort),
|
||||
fmt.Sprintf("--gateway-https-port=%d", a.profile.Gateway.HttpsPort),
|
||||
fmt.Sprintf("--gateway-metrics-port=%d", a.profile.Gateway.MetricsPort),
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
func (a *Agent) run(binaryName string, args []string, autoSudo bool) error {
|
||||
var cmd *exec.Cmd
|
||||
if !autoSudo || a.runSudoState == NoSudo {
|
||||
if !a.quiet {
|
||||
fmt.Fprintf(a.writer, "\n📦 Running command: %s %s\n\n", binaryName, strings.Join(args, " "))
|
||||
}
|
||||
cmd = exec.Command(binaryName, args...)
|
||||
} else {
|
||||
newArgs := make([]string, 0)
|
||||
newArgs = append(newArgs, binaryName)
|
||||
newArgs = append(newArgs, args...)
|
||||
if !a.quiet {
|
||||
fmt.Fprintf(a.writer, "\n📦 Running command: %s %s\n\n", "sudo", strings.Join(newArgs, " "))
|
||||
}
|
||||
cmd = exec.Command("sudo", newArgs...)
|
||||
}
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Dir = a.installPath
|
||||
if err := cmd.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
done := make(chan error, 1)
|
||||
go func() {
|
||||
done <- cmd.Wait()
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-done:
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Agent) checkSudoPermission() error {
|
||||
if !a.quiet {
|
||||
fmt.Fprintf(a.writer, "\n⌛️ Checking docker command sudo permission... ")
|
||||
}
|
||||
// check docker ps command
|
||||
cmd := exec.Command("docker", "ps")
|
||||
var out bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
cmd.Stderr = &stderr
|
||||
cmd.Dir = a.installPath
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
done := make(chan error, 1)
|
||||
go func() {
|
||||
done <- cmd.Wait()
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-done:
|
||||
if err == nil {
|
||||
if !a.quiet {
|
||||
fmt.Fprintf(a.writer, "checked result: no need sudo permission\n")
|
||||
}
|
||||
a.runSudoState = NoSudo
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// check sudo docker ps command
|
||||
cmd2 := exec.Command("sudo", "-S", "docker", "ps")
|
||||
var out2 bytes.Buffer
|
||||
var stderr2 bytes.Buffer
|
||||
cmd2.Stdout = &out2
|
||||
cmd2.Stderr = &stderr2
|
||||
cmd2.Dir = a.installPath
|
||||
stdin, _ := cmd2.StdinPipe()
|
||||
defer stdin.Close()
|
||||
|
||||
if err := cmd2.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
done2 := make(chan error, 1)
|
||||
go func() {
|
||||
done2 <- cmd2.Wait()
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-time.After(5 * time.Second):
|
||||
cmd2.Process.Signal(os.Interrupt)
|
||||
if !a.quiet {
|
||||
fmt.Fprintf(a.writer, "checked result: timeout execeed and need sudo with password\n")
|
||||
}
|
||||
a.runSudoState = SudoWithPassword
|
||||
|
||||
case err := <-done2:
|
||||
if err == nil {
|
||||
if !a.quiet {
|
||||
fmt.Fprintf(a.writer, "checked result: need sudo without password\n")
|
||||
}
|
||||
a.runSudoState = SudoWithoutPassword
|
||||
} else {
|
||||
if !a.quiet {
|
||||
fmt.Fprintf(a.writer, "checked result: need sudo with password\n")
|
||||
}
|
||||
a.runSudoState = SudoWithPassword
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Agent) Install() error {
|
||||
a.checkSudoPermission()
|
||||
if a.runSudoState == SudoWithPassword {
|
||||
if !a.promptSudo() {
|
||||
return errors.New("cancel installation")
|
||||
}
|
||||
}
|
||||
|
||||
if a.hasConfigured() {
|
||||
a.Reset()
|
||||
}
|
||||
|
||||
if !a.quiet {
|
||||
fmt.Fprintf(a.writer, "\n⌛️ Starting to install higress.. \n")
|
||||
}
|
||||
args := []string{"./higress"}
|
||||
args = append(args, a.profileArgs()...)
|
||||
return a.run(a.installBinaryName, args, true)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Agent) Uninstall() error {
|
||||
a.checkSudoPermission()
|
||||
if a.runSudoState == SudoWithPassword {
|
||||
if !a.promptSudo() {
|
||||
return errors.New("cancel uninstall")
|
||||
}
|
||||
}
|
||||
|
||||
if !a.quiet {
|
||||
fmt.Fprintf(a.writer, "\n⌛️ Starting to uninstall higress... \n")
|
||||
}
|
||||
|
||||
if err := a.Reset(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Agent) Upgrade() error {
|
||||
a.checkSudoPermission()
|
||||
if a.runSudoState == SudoWithPassword {
|
||||
if !a.promptSudo() {
|
||||
return errors.New("cancel upgrade")
|
||||
}
|
||||
}
|
||||
|
||||
currentVersion := ""
|
||||
newVersion := ""
|
||||
if !a.quiet {
|
||||
fmt.Fprintf(a.writer, "\n⌛️ Checking current higress version... ")
|
||||
currentVersion, _ = a.Version()
|
||||
fmt.Fprintf(a.writer, "%s\n", currentVersion)
|
||||
}
|
||||
|
||||
if !a.quiet {
|
||||
fmt.Fprintf(a.writer, "\n⌛️ Starting to upgrade higress... \n")
|
||||
}
|
||||
|
||||
if err := a.run(a.installBinaryName, []string{"-u"}, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !a.quiet {
|
||||
fmt.Fprintf(a.writer, "\n⌛️ Checking new higress version... ")
|
||||
newVersion, _ = a.Version()
|
||||
fmt.Fprintf(a.writer, "%s\n", newVersion)
|
||||
}
|
||||
|
||||
if currentVersion == newVersion {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !a.promptRestart() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := a.Shutdown(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := a.Startup(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Agent) Version() (string, error) {
|
||||
version := ""
|
||||
content, err := os.ReadFile(a.versionPath)
|
||||
if err != nil {
|
||||
return version, nil
|
||||
}
|
||||
return string(content), nil
|
||||
}
|
||||
|
||||
func (a *Agent) promptSudo() bool {
|
||||
answer := ""
|
||||
for {
|
||||
fmt.Fprintf(a.writer, "\nThis need sudo permission and input root password to continue installation, Proceed? (y/N)")
|
||||
fmt.Scanln(&answer)
|
||||
if strings.TrimSpace(answer) == "y" {
|
||||
fmt.Fprintf(a.writer, "\n")
|
||||
return true
|
||||
}
|
||||
if strings.TrimSpace(answer) == "N" {
|
||||
fmt.Fprintf(a.writer, "Cancelled.\n")
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Agent) promptRestart() bool {
|
||||
answer := ""
|
||||
for {
|
||||
fmt.Fprintf(a.writer, "\nThis need to restart higress, Proceed? (y/N)")
|
||||
fmt.Scanln(&answer)
|
||||
if strings.TrimSpace(answer) == "y" {
|
||||
fmt.Fprintf(a.writer, "\n")
|
||||
return true
|
||||
}
|
||||
if strings.TrimSpace(answer) == "N" {
|
||||
fmt.Fprintf(a.writer, "Cancelled.\n")
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Agent) Startup() error {
|
||||
if !a.quiet {
|
||||
fmt.Fprintf(a.writer, "\n⌛️ Starting higress... \n")
|
||||
}
|
||||
return a.run(a.startupBinaryName, []string{}, true)
|
||||
}
|
||||
|
||||
func (a *Agent) Shutdown() error {
|
||||
if !a.quiet {
|
||||
fmt.Fprintf(a.writer, "\n⌛️ Shutdowning higress... \n")
|
||||
}
|
||||
return a.run(a.shutdownBinaryName, []string{}, true)
|
||||
}
|
||||
|
||||
func (a *Agent) Reset() error {
|
||||
if !a.quiet {
|
||||
fmt.Fprintf(a.writer, "\n⌛️ Resetting higress....\n")
|
||||
}
|
||||
return a.run(a.resetBinaryName, []string{}, true)
|
||||
}
|
||||
|
||||
func (a *Agent) hasConfigured() bool {
|
||||
if _, err := os.Stat(a.configuredPath); os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -19,17 +19,21 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
kubescheme "k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/client-go/tools/remotecommand"
|
||||
"k8s.io/client-go/util/retry"
|
||||
ctrClient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
type CLIClient interface {
|
||||
@@ -44,6 +48,18 @@ type CLIClient interface {
|
||||
|
||||
// PodExec takes a command and the pod data to run the command in the specified pod.
|
||||
PodExec(namespacedName types.NamespacedName, container string, command string) (stdout string, stderr string, err error)
|
||||
|
||||
// ApplyObject creates or updates unstructured object
|
||||
ApplyObject(obj *unstructured.Unstructured) error
|
||||
|
||||
// DeleteObject delete unstructured object
|
||||
DeleteObject(obj *unstructured.Unstructured) error
|
||||
|
||||
// CreateNamespace create namespace
|
||||
CreateNamespace(namespace string) error
|
||||
|
||||
// KubernetesInterface get kubernetes interface
|
||||
KubernetesInterface() kubernetes.Interface
|
||||
}
|
||||
|
||||
var _ CLIClient = &client{}
|
||||
@@ -52,6 +68,7 @@ type client struct {
|
||||
config *rest.Config
|
||||
restClient *rest.RESTClient
|
||||
kube kubernetes.Interface
|
||||
ctrClient ctrClient.Client
|
||||
}
|
||||
|
||||
func NewCLIClient(clientConfig clientcmd.ClientConfig) (CLIClient, error) {
|
||||
@@ -80,33 +97,13 @@ func newClientInternal(clientConfig clientcmd.ClientConfig) (*client, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.ctrClient, err = ctrClient.New(c.config, ctrClient.Options{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &c, err
|
||||
}
|
||||
|
||||
func setRestDefaults(config *rest.Config) *rest.Config {
|
||||
if config.GroupVersion == nil || config.GroupVersion.Empty() {
|
||||
config.GroupVersion = &corev1.SchemeGroupVersion
|
||||
}
|
||||
if len(config.APIPath) == 0 {
|
||||
if len(config.GroupVersion.Group) == 0 {
|
||||
config.APIPath = "/api"
|
||||
} else {
|
||||
config.APIPath = "/apis"
|
||||
}
|
||||
}
|
||||
if len(config.ContentType) == 0 {
|
||||
config.ContentType = runtime.ContentTypeJSON
|
||||
}
|
||||
if config.NegotiatedSerializer == nil {
|
||||
// This codec factory ensures the resources are not converted. Therefore, resources
|
||||
// will not be round-tripped through internal versions. Defaulting does not happen
|
||||
// on the client.
|
||||
config.NegotiatedSerializer = serializer.NewCodecFactory(kubescheme.Scheme).WithoutConversion()
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
func (c *client) RESTConfig() *rest.Config {
|
||||
if c.config == nil {
|
||||
return nil
|
||||
@@ -170,3 +167,91 @@ func (c *client) PodExec(namespacedName types.NamespacedName, container string,
|
||||
stderr = stderrBuf.String()
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteObject delete unstructured object
|
||||
func (c *client) DeleteObject(obj *unstructured.Unstructured) error {
|
||||
err := c.ctrClient.Delete(context.TODO(), obj, ctrClient.PropagationPolicy(metav1.DeletePropagationBackground))
|
||||
if err != nil {
|
||||
if !errors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ApplyObject creates or updates unstructured object
|
||||
func (c *client) ApplyObject(obj *unstructured.Unstructured) error {
|
||||
if obj.GetKind() == "List" {
|
||||
objList, err := obj.ToList()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, item := range objList.Items {
|
||||
if err := c.ApplyObject(&item); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
key := ctrClient.ObjectKeyFromObject(obj)
|
||||
receiver := &unstructured.Unstructured{}
|
||||
receiver.SetGroupVersionKind(obj.GroupVersionKind())
|
||||
|
||||
if err := retry.RetryOnConflict(wait.Backoff{
|
||||
Duration: time.Millisecond * 10,
|
||||
Factor: 2,
|
||||
Steps: 3,
|
||||
}, func() error {
|
||||
if err := c.ctrClient.Get(context.Background(), key, receiver); err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
if err := c.ctrClient.Create(context.Background(), obj); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if err := applyOverlay(receiver, obj); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.ctrClient.Update(context.Background(), receiver); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateNamespace create namespace
|
||||
func (c *client) CreateNamespace(namespace string) error {
|
||||
key := ctrClient.ObjectKey{
|
||||
Namespace: metav1.NamespaceSystem,
|
||||
Name: namespace,
|
||||
}
|
||||
if err := c.ctrClient.Get(context.Background(), key, &corev1.Namespace{}); err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
nsObj := &corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: metav1.NamespaceSystem,
|
||||
Name: namespace,
|
||||
},
|
||||
}
|
||||
if err := c.ctrClient.Create(context.Background(), nsObj); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("failed to check if namespace %v exists: %v", namespace, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// KubernetesInterface get kubernetes interface
|
||||
func (c *client) KubernetesInterface() kubernetes.Interface {
|
||||
return c.kube
|
||||
|
||||
}
|
||||
|
||||
157
pkg/cmd/hgctl/kubernetes/common.go
Normal file
157
pkg/cmd/hgctl/kubernetes/common.go
Normal file
@@ -0,0 +1,157 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package kubernetes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch/v5"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
kubescheme "k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/kubectl/pkg/scheme"
|
||||
)
|
||||
|
||||
// applyOverlay applies an overlay using JSON patch strategy over the current Object in place.
|
||||
func applyOverlay(current, overlay *unstructured.Unstructured) error {
|
||||
cj, err := runtime.Encode(unstructured.UnstructuredJSONScheme, current)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
overlayUpdated := overlay.DeepCopy()
|
||||
if strings.EqualFold(current.GetKind(), "service") {
|
||||
if err := saveClusterIP(current, overlayUpdated); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
saveNodePorts(current, overlayUpdated)
|
||||
}
|
||||
|
||||
if current.GetKind() == "PersistentVolumeClaim" {
|
||||
if err := savePersistentVolumeClaim(current, overlayUpdated); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
uj, err := runtime.Encode(unstructured.UnstructuredJSONScheme, overlayUpdated)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
merged, err := jsonpatch.MergePatch(cj, uj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return runtime.DecodeInto(unstructured.UnstructuredJSONScheme, merged, current)
|
||||
}
|
||||
|
||||
// createPortMap returns a map, mapping the value of the port and value of the nodePort
|
||||
func createPortMap(current *unstructured.Unstructured) map[string]uint32 {
|
||||
portMap := make(map[string]uint32)
|
||||
svc := &corev1.Service{}
|
||||
if err := scheme.Scheme.Convert(current, svc, nil); err != nil {
|
||||
return portMap
|
||||
}
|
||||
for _, p := range svc.Spec.Ports {
|
||||
portMap[strconv.Itoa(int(p.Port))] = uint32(p.NodePort)
|
||||
}
|
||||
return portMap
|
||||
}
|
||||
|
||||
// savePersistentVolumeClaim copies the storageClassName from the current cluster into the overlay
|
||||
func savePersistentVolumeClaim(current, overlay *unstructured.Unstructured) error {
|
||||
// Save the value of spec.storageClassName set by the cluster
|
||||
if storageClassName, found, err := unstructured.NestedString(current.Object, "spec",
|
||||
"storageClassName"); err != nil {
|
||||
return err
|
||||
} else if found {
|
||||
if _, _, err2 := unstructured.NestedString(overlay.Object, "spec",
|
||||
"storageClassName"); err2 != nil {
|
||||
// override when overlay storageClassName property is not existed
|
||||
if err3 := unstructured.SetNestedField(overlay.Object, storageClassName, "spec",
|
||||
"storageClassName"); err3 != nil {
|
||||
return err3
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// saveNodePorts transfers the port values from the current cluster into the overlay
|
||||
func saveNodePorts(current, overlay *unstructured.Unstructured) {
|
||||
portMap := createPortMap(current)
|
||||
ports, _, _ := unstructured.NestedFieldNoCopy(overlay.Object, "spec", "ports")
|
||||
portList, ok := ports.([]any)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
for _, port := range portList {
|
||||
m, ok := port.(map[string]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if nodePortNum, ok := m["nodePort"]; ok && fmt.Sprintf("%v", nodePortNum) == "0" {
|
||||
if portNum, ok := m["port"]; ok {
|
||||
if v, ok := portMap[fmt.Sprintf("%v", portNum)]; ok {
|
||||
m["nodePort"] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// saveClusterIP copies the cluster IP from the current cluster into the overlay
|
||||
func saveClusterIP(current, overlay *unstructured.Unstructured) error {
|
||||
// Save the value of spec.clusterIP set by the cluster
|
||||
if clusterIP, found, err := unstructured.NestedString(current.Object, "spec",
|
||||
"clusterIP"); err != nil {
|
||||
return err
|
||||
} else if found {
|
||||
if err := unstructured.SetNestedField(overlay.Object, clusterIP, "spec",
|
||||
"clusterIP"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setRestDefaults(config *rest.Config) *rest.Config {
|
||||
if config.GroupVersion == nil || config.GroupVersion.Empty() {
|
||||
config.GroupVersion = &corev1.SchemeGroupVersion
|
||||
}
|
||||
if len(config.APIPath) == 0 {
|
||||
if len(config.GroupVersion.Group) == 0 {
|
||||
config.APIPath = "/api"
|
||||
} else {
|
||||
config.APIPath = "/apis"
|
||||
}
|
||||
}
|
||||
if len(config.ContentType) == 0 {
|
||||
config.ContentType = runtime.ContentTypeJSON
|
||||
}
|
||||
if config.NegotiatedSerializer == nil {
|
||||
// This codec factory ensures the resources are not converted. Therefore, resources
|
||||
// will not be round-tripped through internal versions. Defaulting does not happen
|
||||
// on the client.
|
||||
config.NegotiatedSerializer = serializer.NewCodecFactory(kubescheme.Scheme).WithoutConversion()
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
@@ -28,12 +28,8 @@ import (
|
||||
"k8s.io/client-go/transport/spdy"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultLocalAddress = "localhost"
|
||||
)
|
||||
|
||||
func LocalAvailablePort() (int, error) {
|
||||
l, err := net.Listen("tcp", fmt.Sprintf("%s:0", DefaultLocalAddress))
|
||||
func LocalAvailablePort(localAddress string) (int, error) {
|
||||
l, err := net.Listen("tcp", fmt.Sprintf("%s:0", localAddress))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -48,6 +44,9 @@ type PortForwarder interface {
|
||||
|
||||
// Address returns the address of the local forwarded address.
|
||||
Address() string
|
||||
|
||||
// WaitForStop blocks until connection closed (e.g. control-C interrupt)
|
||||
WaitForStop()
|
||||
}
|
||||
|
||||
var _ PortForwarder = &localForwarder{}
|
||||
@@ -56,23 +55,25 @@ type localForwarder struct {
|
||||
types.NamespacedName
|
||||
CLIClient
|
||||
|
||||
localPort int
|
||||
podPort int
|
||||
localPort int
|
||||
podPort int
|
||||
localAddress string
|
||||
|
||||
stopCh chan struct{}
|
||||
}
|
||||
|
||||
func NewLocalPortForwarder(client CLIClient, namespacedName types.NamespacedName, localPort, podPort int) (PortForwarder, error) {
|
||||
func NewLocalPortForwarder(client CLIClient, namespacedName types.NamespacedName, localPort, podPort int, bindAddress string) (PortForwarder, error) {
|
||||
f := &localForwarder{
|
||||
stopCh: make(chan struct{}),
|
||||
CLIClient: client,
|
||||
NamespacedName: namespacedName,
|
||||
localPort: localPort,
|
||||
podPort: podPort,
|
||||
localAddress: bindAddress,
|
||||
}
|
||||
if f.localPort == 0 {
|
||||
// get a random port
|
||||
p, err := LocalAvailablePort()
|
||||
p, err := LocalAvailablePort(bindAddress)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get a local available port")
|
||||
}
|
||||
@@ -133,7 +134,7 @@ func (f *localForwarder) buildKubernetesPortForwarder(readyCh chan struct{}) (*p
|
||||
|
||||
dialer := spdy.NewDialer(upgrader, &http.Client{Transport: roundTripper}, http.MethodPost, serverURL)
|
||||
fw, err := portforward.NewOnAddresses(dialer,
|
||||
[]string{DefaultLocalAddress},
|
||||
[]string{f.localAddress},
|
||||
[]string{fmt.Sprintf("%d:%d", f.localPort, f.podPort)},
|
||||
f.stopCh,
|
||||
readyCh,
|
||||
@@ -151,5 +152,9 @@ func (f *localForwarder) Stop() {
|
||||
}
|
||||
|
||||
func (f *localForwarder) Address() string {
|
||||
return fmt.Sprintf("%s:%d", DefaultLocalAddress, f.localPort)
|
||||
return fmt.Sprintf("%s:%d", f.localAddress, f.localPort)
|
||||
}
|
||||
|
||||
func (f *localForwarder) WaitForStop() {
|
||||
<-f.stopCh
|
||||
}
|
||||
|
||||
130
pkg/cmd/hgctl/kubernetes/wasmplugin.go
Normal file
130
pkg/cmd/hgctl/kubernetes/wasmplugin.go
Normal file
@@ -0,0 +1,130 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package kubernetes
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultHigressNamespace = "higress-system"
|
||||
HigressExtGroup = "extensions.higress.io"
|
||||
HigressExtVersion = "v1alpha1"
|
||||
HigressExtAPIVersion = HigressExtGroup + "/" + HigressExtVersion
|
||||
|
||||
WasmPluginKind = "WasmPlugin"
|
||||
WasmPluginResource = "wasmplugins"
|
||||
)
|
||||
|
||||
var (
|
||||
HigressNamespace = DefaultHigressNamespace
|
||||
WasmPluginGVK = schema.GroupVersionKind{Group: HigressExtGroup, Version: HigressExtVersion, Kind: WasmPluginKind}
|
||||
WasmPluginGVR = schema.GroupVersionResource{Group: HigressExtGroup, Version: HigressExtVersion, Resource: WasmPluginResource}
|
||||
)
|
||||
|
||||
func AddHigressNamespaceFlags(flags *pflag.FlagSet) {
|
||||
flags.StringVarP(&HigressNamespace, "namespace", "n",
|
||||
DefaultHigressNamespace, "Namespace where Higress was installed")
|
||||
}
|
||||
|
||||
type WasmPluginClient struct {
|
||||
dyn *DynamicClient
|
||||
}
|
||||
|
||||
func NewWasmPluginClient(dynClient *DynamicClient) *WasmPluginClient {
|
||||
return &WasmPluginClient{dynClient}
|
||||
}
|
||||
|
||||
func (c WasmPluginClient) Get(ctx context.Context, name string) (*unstructured.Unstructured, error) {
|
||||
return c.dyn.Get(ctx, WasmPluginGVR, HigressNamespace, name)
|
||||
}
|
||||
|
||||
func (c WasmPluginClient) List(ctx context.Context) (*unstructured.UnstructuredList, error) {
|
||||
return c.dyn.List(ctx, WasmPluginGVR, HigressNamespace)
|
||||
}
|
||||
|
||||
func (c WasmPluginClient) Create(ctx context.Context, obj *unstructured.Unstructured) (*unstructured.Unstructured, error) {
|
||||
return c.dyn.Create(ctx, WasmPluginGVR, HigressNamespace, obj)
|
||||
}
|
||||
|
||||
func (c WasmPluginClient) Delete(ctx context.Context, name string) (*unstructured.Unstructured, error) {
|
||||
return c.dyn.Delete(ctx, WasmPluginGVR, HigressNamespace, name)
|
||||
}
|
||||
|
||||
func (c WasmPluginClient) Update(ctx context.Context, obj *unstructured.Unstructured) (*unstructured.Unstructured, error) {
|
||||
return c.dyn.Update(ctx, WasmPluginGVR, HigressNamespace, obj)
|
||||
}
|
||||
|
||||
// TODO(WeixinX): Will be changed to WasmPlugin specific Client instead of Unstructured
|
||||
type DynamicClient struct {
|
||||
config *rest.Config
|
||||
client dynamic.Interface
|
||||
}
|
||||
|
||||
func NewDynamicClient(clientConfig clientcmd.ClientConfig) (*DynamicClient, error) {
|
||||
var (
|
||||
c DynamicClient
|
||||
err error
|
||||
)
|
||||
|
||||
c.config, err = clientConfig.ClientConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.client, err = dynamic.NewForConfig(c.config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
func (c DynamicClient) Get(ctx context.Context, gvr schema.GroupVersionResource, namespace, name string) (*unstructured.Unstructured, error) {
|
||||
return c.client.Resource(gvr).Namespace(namespace).Get(ctx, name, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
func (c DynamicClient) List(ctx context.Context, gvr schema.GroupVersionResource, namespace string) (*unstructured.UnstructuredList, error) {
|
||||
return c.client.Resource(gvr).Namespace(namespace).List(ctx, metav1.ListOptions{})
|
||||
}
|
||||
|
||||
func (c DynamicClient) Create(ctx context.Context, gvr schema.GroupVersionResource, namespace string, obj *unstructured.Unstructured) (*unstructured.Unstructured, error) {
|
||||
return c.client.Resource(gvr).Namespace(namespace).Create(ctx, obj, metav1.CreateOptions{})
|
||||
}
|
||||
|
||||
func (c DynamicClient) Delete(ctx context.Context, gvr schema.GroupVersionResource, namespace, name string) (*unstructured.Unstructured, error) {
|
||||
result, err := c.client.Resource(gvr).Namespace(namespace).Get(ctx, name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = c.client.Resource(gvr).Namespace(namespace).Delete(ctx, name, metav1.DeleteOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (c DynamicClient) Update(ctx context.Context, gvr schema.GroupVersionResource, namespace string,
|
||||
obj *unstructured.Unstructured) (*unstructured.Unstructured, error) {
|
||||
return c.client.Resource(gvr).Namespace(namespace).Update(ctx, obj, metav1.UpdateOptions{})
|
||||
}
|
||||
146
pkg/cmd/hgctl/manifest.go
Normal file
146
pkg/cmd/hgctl/manifest.go
Normal file
@@ -0,0 +1,146 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package hgctl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/helm"
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/installer"
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/kubernetes"
|
||||
"github.com/alibaba/higress/pkg/cmd/options"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type ManifestArgs struct {
|
||||
InFilenames []string
|
||||
// KubeConfigPath is the path to kube config file.
|
||||
KubeConfigPath string
|
||||
// Context is the cluster context in the kube config
|
||||
Context string
|
||||
// Set is a string with element format "path=value" where path is an profile path and the value is a
|
||||
// value to set the node at that path to.
|
||||
Set []string
|
||||
// ManifestsPath is a path to a ManifestsPath and profiles directory in the local filesystem with a release tgz.
|
||||
ManifestsPath string
|
||||
}
|
||||
|
||||
func (a *ManifestArgs) String() string {
|
||||
var b strings.Builder
|
||||
b.WriteString("KubeConfigPath: " + a.KubeConfigPath + "\n")
|
||||
b.WriteString("Context: " + a.Context + "\n")
|
||||
b.WriteString("Set: " + fmt.Sprint(a.Set) + "\n")
|
||||
b.WriteString("ManifestsPath: " + a.ManifestsPath + "\n")
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// newManifestCmd generates a higress install manifest and applies it to a cluster
|
||||
func newManifestCmd() *cobra.Command {
|
||||
iArgs := &ManifestArgs{}
|
||||
manifestCmd := &cobra.Command{
|
||||
Use: "manifest",
|
||||
Short: "Generate higress manifests.",
|
||||
Long: "The manifest command generates an higress install manifests.",
|
||||
}
|
||||
|
||||
generate := newManifestGenerateCmd(iArgs)
|
||||
addManifestFlags(generate, iArgs)
|
||||
flags := generate.Flags()
|
||||
options.AddKubeConfigFlags(flags)
|
||||
manifestCmd.AddCommand(generate)
|
||||
|
||||
return manifestCmd
|
||||
}
|
||||
|
||||
func addManifestFlags(cmd *cobra.Command, args *ManifestArgs) {
|
||||
cmd.PersistentFlags().StringSliceVarP(&args.InFilenames, "filename", "f", nil, filenameFlagHelpStr)
|
||||
cmd.PersistentFlags().StringArrayVarP(&args.Set, "set", "s", nil, setFlagHelpStr)
|
||||
cmd.PersistentFlags().StringVarP(&args.ManifestsPath, "manifests", "d", "", manifestsFlagHelpStr)
|
||||
}
|
||||
|
||||
// newManifestGenerateCmd generates a higress install manifest and applies it to a cluster
|
||||
func newManifestGenerateCmd(iArgs *ManifestArgs) *cobra.Command {
|
||||
installCmd := &cobra.Command{
|
||||
Use: "generate",
|
||||
Short: "Generate higress manifests.",
|
||||
Long: "The manifest generate command generates higress install manifests.",
|
||||
// nolint: lll
|
||||
Example: ` # Generate higress manifests
|
||||
hgctl manifest generate
|
||||
`,
|
||||
Args: cobra.ExactArgs(0),
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return generate(cmd.OutOrStdout(), iArgs)
|
||||
},
|
||||
}
|
||||
|
||||
return installCmd
|
||||
}
|
||||
|
||||
func generate(writer io.Writer, iArgs *ManifestArgs) error {
|
||||
setFlags := applyFlagAliases(iArgs.Set, iArgs.ManifestsPath)
|
||||
|
||||
// check profileName
|
||||
psf := helm.GetValueForSetFlag(setFlags, "profile")
|
||||
if len(psf) == 0 {
|
||||
setFlags = append(setFlags, fmt.Sprintf("profile=%s", helm.InstallLocalK8s))
|
||||
}
|
||||
|
||||
_, profile, _, err := helm.GenerateConfig(iArgs.InFilenames, setFlags)
|
||||
if err != nil {
|
||||
return fmt.Errorf("generate config: %v", err)
|
||||
}
|
||||
|
||||
err = profile.Validate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = genManifests(profile, writer)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to install manifests: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func genManifests(profile *helm.Profile, writer io.Writer) error {
|
||||
cliClient, err := kubernetes.NewCLIClient(options.DefaultConfigFlags.ToRawKubeConfigLoader())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to build kubernetes client: %w", err)
|
||||
}
|
||||
|
||||
op, err := installer.NewK8sInstaller(profile, cliClient, writer, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := op.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
manifestMap, err := op.RenderManifests()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := op.GenerateManifests(manifestMap); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
11763
pkg/cmd/hgctl/manifests/gatewayapi/experimental-install.yaml
Normal file
11763
pkg/cmd/hgctl/manifests/gatewayapi/experimental-install.yaml
Normal file
File diff suppressed because it is too large
Load Diff
10
pkg/cmd/hgctl/manifests/istiobase/Chart.yaml
Normal file
10
pkg/cmd/hgctl/manifests/istiobase/Chart.yaml
Normal file
@@ -0,0 +1,10 @@
|
||||
apiVersion: v1
|
||||
appVersion: 1.18.2
|
||||
description: Helm chart for deploying Istio cluster resources and CRDs
|
||||
icon: https://istio.io/latest/favicons/android-192x192.png
|
||||
keywords:
|
||||
- istio
|
||||
name: base
|
||||
sources:
|
||||
- https://github.com/istio/istio
|
||||
version: 1.18.2
|
||||
21
pkg/cmd/hgctl/manifests/istiobase/README.md
Normal file
21
pkg/cmd/hgctl/manifests/istiobase/README.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Istio base Helm Chart
|
||||
|
||||
This chart installs resources shared by all Istio revisions. This includes Istio CRDs.
|
||||
|
||||
## Setup Repo Info
|
||||
|
||||
```console
|
||||
helm repo add istio https://istio-release.storage.googleapis.com/charts
|
||||
helm repo update
|
||||
```
|
||||
|
||||
_See [helm repo](https://helm.sh/docs/helm/helm_repo/) for command documentation._
|
||||
|
||||
## Installing the Chart
|
||||
|
||||
To install the chart with the release name `istio-base`:
|
||||
|
||||
```console
|
||||
kubectl create namespace istio-system
|
||||
helm install istio-base istio/base -n istio-system
|
||||
```
|
||||
7199
pkg/cmd/hgctl/manifests/istiobase/crds/crd-all.gen.yaml
Normal file
7199
pkg/cmd/hgctl/manifests/istiobase/crds/crd-all.gen.yaml
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user