mirror of
https://github.com/alibaba/higress.git
synced 2026-02-25 13:10:50 +08:00
Compare commits
31 Commits
v0.7.1
...
v1.0.0-rc.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
05608128e2 | ||
|
|
fbdc301f94 | ||
|
|
cf69234eff | ||
|
|
461f7ed675 | ||
|
|
7e358eb1db | ||
|
|
daffd18674 | ||
|
|
48978e5135 | ||
|
|
311d5c21c2 | ||
|
|
e2b4a52c9e | ||
|
|
51cd5e830e | ||
|
|
10d2b41ad5 | ||
|
|
4f16d6b70f | ||
|
|
8c41dbc376 | ||
|
|
e98788a75c | ||
|
|
3b5850a5ba | ||
|
|
df60dd4307 | ||
|
|
283432b6eb | ||
|
|
a9742bbae1 | ||
|
|
069b636c10 | ||
|
|
f5edac0c58 | ||
|
|
06b09066a3 | ||
|
|
7ff1d2c414 | ||
|
|
acaf3d899a | ||
|
|
96e7153c8c | ||
|
|
0acb04fffb | ||
|
|
affa1207d2 | ||
|
|
e18557d2ea | ||
|
|
0668eaea1e | ||
|
|
41f892b26d | ||
|
|
a5edad1a84 | ||
|
|
0d4b8ee313 |
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Ask a question 💬
|
||||
url: https://github.com/alibaba/higress/discussions
|
||||
about: Ask a question or request support for using Higress.
|
||||
@@ -10,14 +10,6 @@ assignees: ''
|
||||
**If you are reporting *any* crash or *any* potential security issue, *do not*
|
||||
open an issue in this repo. Please report the issue via [ASRC](https://security.alibaba.com/)(Alibaba Security Response Center) where the issue will be triaged appropriately.**
|
||||
|
||||
|
||||
---
|
||||
name: Bug Report
|
||||
about: If you would like to report an issue to Higress, please use this template.
|
||||
|
||||
|
||||
---
|
||||
|
||||
- [ ] I have searched the [issues](https://github.com/alibaba/higress/issues) of this repository and believe that this is not a duplicate.
|
||||
|
||||
### Ⅰ. Issue Description
|
||||
|
||||
68
.github/workflows/latest-release.yaml
vendored
Normal file
68
.github/workflows/latest-release.yaml
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
name: Latest Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
|
||||
jobs:
|
||||
latest-release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Build hgctl latest multiarch binaries
|
||||
run: |
|
||||
GOPROXY="https://proxy.golang.org,direct" make build-hgctl-multiarch
|
||||
tar -zcvf hgctl_latest_linux_amd64.tar.gz out/linux_amd64/
|
||||
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/
|
||||
|
||||
# Ignore the error when we delete the latest release, it might not exist.
|
||||
|
||||
# GitHub APIs take sometime to make effect, we should make sure before Recreate the Latest Release and Tag,
|
||||
# tag and release all get deleted. So we sleep sometime after deleting tag and release to wait for it taking effect.
|
||||
|
||||
- name: Delete the Latest Release
|
||||
continue-on-error: true
|
||||
run: |
|
||||
gh release delete latest --repo $GITHUB_REPOSITORY
|
||||
sleep 4
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_REPOSITORY: ${{ github.repository_owner }}/${{ github.event.repository.name }}
|
||||
|
||||
# Ignore the error when we delete the latest tag, it might not exist.
|
||||
- name: Delete the Latest Tag
|
||||
continue-on-error: true
|
||||
run: |
|
||||
gh api --method DELETE /repos/$GITHUB_REPOSITORY/git/refs/tags/latest
|
||||
sleep 4
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_REPOSITORY: ${{ github.repository_owner }}/${{ github.event.repository.name }}
|
||||
|
||||
- name: Recreate the Latest Release and Tag
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: false
|
||||
prerelease: true
|
||||
tag_name: latest
|
||||
files: |
|
||||
hgctl_latest_linux_amd64.tar.gz
|
||||
hgctl_latest_linux_arm64.tar.gz
|
||||
hgctl_latest_darwin_amd64.tar.gz
|
||||
hgctl_latest_darwin_arm64.tar.gz
|
||||
body: |
|
||||
This is the "latest" release of **Higress**, which contains the most recent commits from the main branch.
|
||||
|
||||
This release **might not be stable**.
|
||||
|
||||
It is only intended for developers wishing to try out the latest features in Higress, some of which may not be fully implemented.
|
||||
|
||||
Try latest version of `hgctl` with:
|
||||
|
||||
``` shell
|
||||
curl -Ls https://raw.githubusercontent.com/alibaba/higress/main/tools/hack/get-hgctl.sh | VERSION=latest bash
|
||||
```
|
||||
@@ -26,6 +26,7 @@ header:
|
||||
- 'VERSION'
|
||||
- 'tools/'
|
||||
- 'test/README.md'
|
||||
- 'pkg/cmd/hgctl/testdata/config'
|
||||
|
||||
comment: on-failure
|
||||
dependency:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/api @johnlanni
|
||||
/envoy @gengleilei @johnlanni @Lynskylate
|
||||
/istio @SpecialYang @johnlanni
|
||||
/pkg @SpecialYang @johnlanni @Charlie17Li
|
||||
/pkg @SpecialYang @johnlanni @Charlie17Li @Xunzhuo
|
||||
/plugins @johnlanni
|
||||
/registry @NameHaibinZhang @johnlanni
|
||||
/test @Xunzhuo
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
## 报告一般问题
|
||||
|
||||
老实说,我们把每一个 Higress 用户都视为非常善良的贡献者。在体验了 Higress 之后,您可能会对项目有一些反馈。然后随时通过 [NEW ISSUE](https://github. com/alibaba/higress/issues/new/choose)打开一个问题。
|
||||
老实说,我们把每一个 Higress 用户都视为非常善良的贡献者。在体验了 Higress 之后,您可能会对项目有一些反馈。然后随时通过 [NEW ISSUE](https://github.com/alibaba/higress/issues/new/choose)打开一个问题。
|
||||
|
||||
因为我们在一个分布式的方式合作项目Higress,我们欣赏写得很好的,详细的,准确的问题报告。为了让沟通更高效,我们希望每个人都可以搜索您的问题是否在搜索列表中。如果您发现它存在,请在现有问题下的评论中添加您的详细信息,而不是打开一个全新的问题。
|
||||
|
||||
|
||||
@@ -28,8 +28,7 @@ Security issues are always treated seriously. As our usual principle, we discour
|
||||
## Reporting general issues
|
||||
|
||||
To be honest, we regard every user of Higress as a very kind contributor. After experiencing Higress, you may have
|
||||
some feedback for the project. Then feel free to open an issue via [NEW ISSUE](https://github.
|
||||
com/alibaba/higress/issues/new/choose).
|
||||
some feedback for the project. Then feel free to open an issue via [NEW ISSUE](https://github.com/alibaba/higress/issues/new/choose).
|
||||
|
||||
Since we collaborate project Higress in a distributed way, we appreciate **WELL-WRITTEN**, **DETAILED**, **EXPLICIT** issue reports. To make the communication more efficient, we wish everyone could search if your issue is an existing one in the searching list. If you find it existing, please add your details in comments under the existing issue instead of opening a brand new one.
|
||||
|
||||
|
||||
@@ -6,13 +6,20 @@ export HUB ?= higress-registry.cn-hangzhou.cr.aliyuncs.com/higress
|
||||
|
||||
export CHARTS ?= higress-registry.cn-hangzhou.cr.aliyuncs.com/charts
|
||||
|
||||
VERSION_PACKAGE := github.com/alibaba/higress/pkg/cmd/version
|
||||
|
||||
GIT_COMMIT:=$(shell git rev-parse HEAD)
|
||||
|
||||
GO_LDFLAGS += -X $(VERSION_PACKAGE).higressVersion=$(shell cat VERSION) \
|
||||
-X $(VERSION_PACKAGE).gitCommitID=$(GIT_COMMIT)
|
||||
|
||||
GO ?= go
|
||||
|
||||
export GOPROXY ?= https://proxy.golang.com.cn,direct
|
||||
|
||||
GOARCH_LOCAL := $(TARGET_ARCH)
|
||||
GOOS_LOCAL := $(TARGET_OS)
|
||||
RELEASE_LDFLAGS='-extldflags -static -s -w'
|
||||
RELEASE_LDFLAGS='$(GO_LDFLAGS) -extldflags -static -s -w'
|
||||
|
||||
export OUT:=$(TARGET_OUT)
|
||||
export OUT_LINUX:=$(TARGET_OUT_LINUX)
|
||||
@@ -32,7 +39,9 @@ endif
|
||||
|
||||
HIGRESS_DOCKER_BUILD_TOP:=${OUT_LINUX}/docker_build
|
||||
|
||||
BINARIES:=./cmd/higress
|
||||
HIGRESS_BINARIES:=./cmd/higress
|
||||
|
||||
HGCTL_BINARIES:=./cmd/hgctl
|
||||
|
||||
$(OUT):
|
||||
@mkdir -p $@
|
||||
@@ -52,11 +61,26 @@ go.test.coverage: prebuild
|
||||
|
||||
.PHONY: build
|
||||
build: prebuild $(OUT)
|
||||
GOPROXY=$(GOPROXY) GOOS=$(GOOS_LOCAL) GOARCH=$(GOARCH_LOCAL) LDFLAGS=$(RELEASE_LDFLAGS) tools/hack/gobuild.sh $(OUT)/ $(BINARIES)
|
||||
GOPROXY=$(GOPROXY) GOOS=$(GOOS_LOCAL) GOARCH=$(GOARCH_LOCAL) LDFLAGS=$(RELEASE_LDFLAGS) tools/hack/gobuild.sh $(OUT)/ $(HIGRESS_BINARIES)
|
||||
|
||||
.PHONY: build-linux
|
||||
build-linux: prebuild $(OUT)
|
||||
GOPROXY=$(GOPROXY) GOOS=linux GOARCH=$(GOARCH_LOCAL) LDFLAGS=$(RELEASE_LDFLAGS) tools/hack/gobuild.sh $(OUT_LINUX)/ $(BINARIES)
|
||||
GOPROXY=$(GOPROXY) GOOS=linux GOARCH=$(GOARCH_LOCAL) LDFLAGS=$(RELEASE_LDFLAGS) tools/hack/gobuild.sh $(OUT_LINUX)/ $(HIGRESS_BINARIES)
|
||||
|
||||
.PHONY: build-hgctl
|
||||
build-hgctl: $(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)
|
||||
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)
|
||||
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)
|
||||
|
||||
# Create targets for OUT_LINUX/binary
|
||||
# There are two use cases here:
|
||||
@@ -73,14 +97,14 @@ $(OUT_LINUX)/$(shell basename $(1)): $(OUT_LINUX)
|
||||
endif
|
||||
endef
|
||||
|
||||
$(foreach bin,$(BINARIES),$(eval $(call build-linux,$(bin),"")))
|
||||
$(foreach bin,$(HIGRESS_BINARIES),$(eval $(call build-linux,$(bin),"")))
|
||||
|
||||
# Create helper targets for each binary, like "pilot-discovery"
|
||||
# As an optimization, these still build everything
|
||||
$(foreach bin,$(BINARIES),$(shell basename $(bin))): build
|
||||
$(foreach bin,$(HIGRESS_BINARIES),$(shell basename $(bin))): build
|
||||
ifneq ($(OUT_LINUX),$(LOCAL_OUT))
|
||||
# if we are on linux already, then this rule is handled by build-linux above, which handles BUILD_ALL variable
|
||||
$(foreach bin,$(BINARIES),${LOCAL_OUT}/$(shell basename $(bin))): build
|
||||
$(foreach bin,$(HIGRESS_BINARIES),${LOCAL_OUT}/$(shell basename $(bin))): build
|
||||
endif
|
||||
|
||||
.PHONY: push
|
||||
@@ -113,20 +137,20 @@ endef
|
||||
|
||||
install: pre-install
|
||||
cd helm/higress; helm dependency build
|
||||
helm install higress helm/higress -n higress-system --create-namespace --set 'global.kind=true'
|
||||
helm install higress helm/higress -n higress-system --create-namespace --set 'global.local=true'
|
||||
|
||||
ENVOY_LATEST_IMAGE_TAG ?= 0.7.0
|
||||
ISTIO_LATEST_IMAGE_TAG ?= 0.7.0
|
||||
ENVOY_LATEST_IMAGE_TAG ?= 1.0.0-rc
|
||||
ISTIO_LATEST_IMAGE_TAG ?= 1.0.0-rc
|
||||
|
||||
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.kind=true'
|
||||
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'
|
||||
|
||||
uninstall:
|
||||
helm uninstall higress -n higress-system
|
||||
|
||||
upgrade: pre-install
|
||||
cd helm/higress; helm dependency build
|
||||
helm upgrade higress helm/higress -n higress-system --set 'global.kind=true'
|
||||
helm upgrade higress helm/higress -n higress-system --set 'global.local=true'
|
||||
|
||||
helm-push:
|
||||
cp api/kubernetes/customresourcedefinitions.gen.yaml helm/core/crds
|
||||
@@ -185,7 +209,7 @@ delete-cluster: $(tools/kind) ## Delete kind cluster.
|
||||
|
||||
# kube-load-image loads a local built docker image into kube cluster.
|
||||
.PHONY: kube-load-image
|
||||
kube-load-image: $(tools/kind) ## Install the EG image to a kind cluster using the provided $IMAGE and $TAG.
|
||||
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)
|
||||
|
||||
# run-ingress-e2e-test starts to run ingress e2e tests.
|
||||
|
||||
47
README.md
47
README.md
@@ -8,9 +8,10 @@
|
||||
[](https://www.apache.org/licenses/LICENSE-2.0.html)
|
||||
|
||||
[**官网**](https://higress.io/) |
|
||||
[**文档**](https://higress.io/zh-cn/docs/overview/what-is-higress.html) |
|
||||
[**博客**](https://higress.io/zh-cn/blog/index.html) |
|
||||
[**开发指引**](https://higress.io/zh-cn/docs/dev/code.html)
|
||||
[**文档**](https://higress.io/zh-cn/docs/overview/what-is-higress) |
|
||||
[**博客**](https://higress.io/zh-cn/blog) |
|
||||
[**开发指引**](https://higress.io/zh-cn/docs/developers/developers_dev) |
|
||||
[**Higress 企业版**](https://www.aliyun.com/product/aliware/mse?spm=higress-website.topbar.0.0.0)
|
||||
|
||||
|
||||
<p>
|
||||
@@ -23,12 +24,14 @@ Higress 是基于阿里内部两年多的 Envoy Gateway 实践沉淀,以开源
|
||||

|
||||
|
||||
## Summary
|
||||
|
||||
|
||||
- [**功能展示**](#功能展示)
|
||||
- [**使用场景**](#使用场景)
|
||||
- [**核心优势**](#核心优势)
|
||||
- [**Quick Start**](https://higress.io/zh-cn/docs/user/quickstart)
|
||||
- [**社区**](#社区)
|
||||
|
||||
|
||||
## 使用场景
|
||||
|
||||
- **Kubernetes Ingress 网关**:
|
||||
@@ -73,6 +76,42 @@ Higress 是基于阿里内部两年多的 Envoy Gateway 实践沉淀,以开源
|
||||
|
||||
插件支持热更新,变更插件逻辑和配置都对流量无损。
|
||||
|
||||
## 功能展示
|
||||
|
||||
- **丰富的可观测**
|
||||
|
||||
提供开箱即用的可观测,Grafana&Prometheus 可以使用内置的也可对接自建的
|
||||
|
||||

|
||||
|
||||
|
||||
- **插件扩展机制**
|
||||
|
||||
官方提供了多种插件,用户也可以[开发](./plugins/wasm-go)自己的插件,构建成 docker/oci 镜像后在控制台配置,可以实时变更插件逻辑,对流量完全无损。
|
||||
|
||||

|
||||
|
||||
|
||||
- **多种服务发现**
|
||||
|
||||
默认提供 K8s Service 服务发现,通过配置可以对接 Nacos/ZooKeeper 等注册中心实现服务发现,也可以基于静态 IP 或者 DNS 来发现
|
||||
|
||||

|
||||
|
||||
|
||||
- **域名和证书**
|
||||
|
||||
可以创建管理 TLS 证书,并配置域名的 HTTP/HTTPS 行为,域名策略里支持对特定域名生效插件
|
||||
|
||||

|
||||
|
||||
|
||||
- **丰富的路由能力**
|
||||
|
||||
通过上面定义的服务发现机制,发现的服务会出现在服务列表中;创建路由时,选择域名,定义路由匹配机制,再选择目标服务进行路由;路由策略里支持对特定路由生效插件
|
||||
|
||||

|
||||
|
||||
|
||||
## 社区
|
||||
|
||||
|
||||
10
README_EN.md
10
README_EN.md
@@ -4,6 +4,16 @@
|
||||
Next-generation Cloud Native Gateway
|
||||
</h1>
|
||||
|
||||
[](https://github.com/alibaba/higress/actions)
|
||||
[](https://www.apache.org/licenses/LICENSE-2.0.html)
|
||||
|
||||
[**Official Site**](https://higress.io/en-us/) |
|
||||
[**Docs**](https://higress.io/en-us/docs/overview/what-is-higress) |
|
||||
[**Blog**](https://higress.io/en-us/blog) |
|
||||
[**Developer**](https://higress.io/en-us/docs/developers/developers_dev) |
|
||||
[**Higress in Cloud**](https://www.alibabacloud.com/product/microservices-engine?spm=higress-website.topbar.0.0.0)
|
||||
|
||||
|
||||
<p>
|
||||
English | <a href="README.md">中文<a/>
|
||||
</p>
|
||||
|
||||
29
cmd/hgctl/main.go
Normal file
29
cmd/hgctl/main.go
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := hgctl.GetRootCommand().Execute(); err != nil {
|
||||
_, _ = fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
@@ -17,119 +17,13 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"istio.io/istio/pilot/pkg/features"
|
||||
"istio.io/istio/pkg/cmd"
|
||||
"istio.io/istio/pkg/config/constants"
|
||||
"istio.io/istio/pkg/keepalive"
|
||||
"istio.io/pkg/log"
|
||||
"istio.io/pkg/version"
|
||||
|
||||
"github.com/alibaba/higress/pkg/bootstrap"
|
||||
innerconstants "github.com/alibaba/higress/pkg/config/constants"
|
||||
"github.com/alibaba/higress/pkg/cmd"
|
||||
)
|
||||
|
||||
var (
|
||||
serverArgs *bootstrap.ServerArgs
|
||||
loggingOptions = log.DefaultOptions()
|
||||
|
||||
serverProvider = func(args *bootstrap.ServerArgs) (bootstrap.ServerInterface, error) {
|
||||
return bootstrap.NewServer(serverArgs)
|
||||
}
|
||||
|
||||
waitForMonitorSignal = func(stop chan struct{}) {
|
||||
cmd.WaitSignal(stop)
|
||||
}
|
||||
|
||||
rootCmd = &cobra.Command{
|
||||
Use: "higress",
|
||||
}
|
||||
|
||||
serveCmd = &cobra.Command{
|
||||
Use: "serve",
|
||||
Aliases: []string{"serve"},
|
||||
Short: "Starts the higress ingress controller",
|
||||
Example: "higress serve",
|
||||
PreRunE: func(c *cobra.Command, args []string) error {
|
||||
return log.Configure(loggingOptions)
|
||||
},
|
||||
RunE: func(c *cobra.Command, args []string) error {
|
||||
cmd.PrintFlags(c.Flags())
|
||||
log.Infof("Version %s", version.Info.String())
|
||||
|
||||
stop := make(chan struct{})
|
||||
|
||||
server, err := serverProvider(serverArgs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("fail to create higress server: %v", err)
|
||||
}
|
||||
|
||||
if err := server.Start(stop); err != nil {
|
||||
return fmt.Errorf("fail to start higress server: %v", err)
|
||||
}
|
||||
|
||||
waitForMonitorSignal(stop)
|
||||
|
||||
server.WaitUntilCompletion()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
serverArgs = &bootstrap.ServerArgs{
|
||||
Debug: true,
|
||||
NativeIstio: true,
|
||||
HttpAddress: ":8888",
|
||||
GrpcAddress: ":15051",
|
||||
GrpcKeepAliveOptions: keepalive.DefaultOption(),
|
||||
XdsOptions: bootstrap.XdsOptions{
|
||||
DebounceAfter: features.DebounceAfter,
|
||||
DebounceMax: features.DebounceMax,
|
||||
EnableEDSDebounce: features.EnableEDSDebounce,
|
||||
},
|
||||
}
|
||||
|
||||
serveCmd.PersistentFlags().StringVar(&serverArgs.GatewaySelectorKey, "gatewaySelectorKey", "higress", "gateway resource selector label key")
|
||||
serveCmd.PersistentFlags().StringVar(&serverArgs.GatewaySelectorValue, "gatewaySelectorValue", "higress-gateway", "gateway resource selector label value")
|
||||
serveCmd.PersistentFlags().BoolVar(&serverArgs.EnableStatus, "enableStatus", true, "enable the ingress status syncer which use to update the ip in ingress's status")
|
||||
serveCmd.PersistentFlags().StringVar(&serverArgs.IngressClass, "ingressClass", innerconstants.DefaultIngressClass, "if not empty, only watch the ingresses have the specified class, otherwise watch all ingresses")
|
||||
serveCmd.PersistentFlags().StringVar(&serverArgs.WatchNamespace, "watchNamespace", "", "if not empty, only wath the ingresses in the specified namespace, otherwise watch in all namespacees")
|
||||
serveCmd.PersistentFlags().BoolVar(&serverArgs.Debug, "debug", serverArgs.Debug, "if true, enables more debug http api")
|
||||
serveCmd.PersistentFlags().StringVar(&serverArgs.HttpAddress, "httpAddress", serverArgs.HttpAddress, "the http address")
|
||||
serveCmd.PersistentFlags().StringVar(&serverArgs.GrpcAddress, "grpcAddress", serverArgs.GrpcAddress, "the grpc address")
|
||||
serveCmd.PersistentFlags().BoolVar(&serverArgs.KeepStaleWhenEmpty, "keepStaleWhenEmpty", false, "keep the stale service entry when there are no endpoints in the service")
|
||||
serveCmd.PersistentFlags().StringVar(&serverArgs.RegistryOptions.ClusterRegistriesNamespace, "clusterRegistriesNamespace",
|
||||
serverArgs.RegistryOptions.ClusterRegistriesNamespace, "Namespace for ConfigMap which stores clusters configs")
|
||||
serveCmd.PersistentFlags().StringVar(&serverArgs.RegistryOptions.KubeConfig, "kubeconfig", "",
|
||||
"Use a Kubernetes configuration file instead of in-cluster configuration")
|
||||
// RegistryOptions Controller options
|
||||
serveCmd.PersistentFlags().DurationVar(&serverArgs.RegistryOptions.KubeOptions.ResyncPeriod, "resync", 60*time.Second,
|
||||
"Controller resync interval")
|
||||
serveCmd.PersistentFlags().StringVar(&serverArgs.RegistryOptions.KubeOptions.DomainSuffix, "domain", constants.DefaultKubernetesDomain,
|
||||
"DNS domain suffix")
|
||||
serveCmd.PersistentFlags().StringVar((*string)(&serverArgs.RegistryOptions.KubeOptions.ClusterID), "clusterID", "Kubernetes",
|
||||
"The ID of the cluster that this instance resides")
|
||||
serveCmd.PersistentFlags().StringToStringVar(&serverArgs.RegistryOptions.KubeOptions.ClusterAliases, "clusterAliases", map[string]string{},
|
||||
"Alias names for clusters")
|
||||
serveCmd.PersistentFlags().Float32Var(&serverArgs.RegistryOptions.KubeOptions.KubernetesAPIQPS, "kubernetesApiQPS", 80.0,
|
||||
"Maximum QPS when communicating with the kubernetes API")
|
||||
|
||||
serveCmd.PersistentFlags().IntVar(&serverArgs.RegistryOptions.KubeOptions.KubernetesAPIBurst, "kubernetesApiBurst", 160,
|
||||
"Maximum burst for throttle when communicating with the kubernetes API")
|
||||
|
||||
loggingOptions.AttachCobraFlags(serveCmd)
|
||||
serverArgs.GrpcKeepAliveOptions.AttachCobraFlags(serveCmd)
|
||||
|
||||
rootCmd.AddCommand(serveCmd)
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.EnableKlogWithCobra()
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
log.Error(err)
|
||||
os.Exit(-1)
|
||||
if err := cmd.GetRootCommand().Execute(); err != nil {
|
||||
_, _ = fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
BIN
docs/images/domain.gif
Normal file
BIN
docs/images/domain.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 MiB |
BIN
docs/images/monitor.gif
Normal file
BIN
docs/images/monitor.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.4 MiB |
BIN
docs/images/plugin.gif
Normal file
BIN
docs/images/plugin.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.2 MiB |
BIN
docs/images/route-service.gif
Normal file
BIN
docs/images/route-service.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 MiB |
BIN
docs/images/service-source.gif
Normal file
BIN
docs/images/service-source.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.3 MiB |
55
envoy/1.20/patches/envoy/20230408-basic-auth.patch
Normal file
55
envoy/1.20/patches/envoy/20230408-basic-auth.patch
Normal file
@@ -0,0 +1,55 @@
|
||||
diff -Naur envoy/bazel/envoy_binary.bzl envoy-new/bazel/envoy_binary.bzl
|
||||
--- envoy/bazel/envoy_binary.bzl 2023-04-08 20:52:57.041729111 +0800
|
||||
+++ envoy-new/bazel/envoy_binary.bzl 2023-04-08 20:50:53.657603065 +0800
|
||||
@@ -80,7 +80,7 @@
|
||||
"@envoy//bazel:boringssl_fips": [],
|
||||
"@envoy//bazel:windows_x86_64": [],
|
||||
"//conditions:default": ["-pie"],
|
||||
- }) + _envoy_select_exported_symbols(["-Wl,-E"])
|
||||
+ }) + _envoy_select_exported_symbols(["-Wl,-E"]) + envoy_select_alimesh(["-lcrypt"])
|
||||
|
||||
def _envoy_stamped_deps():
|
||||
return select({
|
||||
diff -Naur envoy/bazel/repositories.bzl envoy-new/bazel/repositories.bzl
|
||||
--- envoy/bazel/repositories.bzl 2023-04-08 20:52:57.085730582 +0800
|
||||
+++ envoy-new/bazel/repositories.bzl 2023-04-08 20:27:20.110335884 +0800
|
||||
@@ -272,6 +272,8 @@
|
||||
actual = "@bazel_tools//tools/cpp/runfiles",
|
||||
)
|
||||
|
||||
+ _com_github_higress_wasm_extensions()
|
||||
+
|
||||
def _boringssl():
|
||||
external_http_archive(
|
||||
name = "boringssl",
|
||||
@@ -1066,6 +1068,17 @@
|
||||
actual = "@com_github_wasm_c_api//:wasmtime_lib",
|
||||
)
|
||||
|
||||
+def _com_github_higress_wasm_extensions():
|
||||
+ native.local_repository(
|
||||
+ name = "com_github_higress_wasm_extensions",
|
||||
+ path = "../../wasm-cpp",
|
||||
+ )
|
||||
+
|
||||
+ native.bind(
|
||||
+ name = "basic_auth_lib",
|
||||
+ actual = "@com_github_higress_wasm_extensions//extensions/basic_auth:basic_auth_lib",
|
||||
+ )
|
||||
+
|
||||
def _rules_fuzzing():
|
||||
external_http_archive(
|
||||
name = "rules_fuzzing",
|
||||
diff -Naur envoy/source/exe/BUILD envoy-new/source/exe/BUILD
|
||||
--- envoy/source/exe/BUILD 2023-04-08 20:52:57.053729512 +0800
|
||||
+++ envoy-new/source/exe/BUILD 2023-04-08 19:48:37.420667254 +0800
|
||||
@@ -43,6 +43,9 @@
|
||||
"//bazel:darwin": envoy_all_extensions(DARWIN_SKIP_TARGETS),
|
||||
"//conditions:default": envoy_all_extensions(),
|
||||
}),
|
||||
+ alimesh_deps = [
|
||||
+ "//external:basic_auth_lib",
|
||||
+ ],
|
||||
)
|
||||
|
||||
envoy_cc_library(
|
||||
12
go.mod
12
go.mod
@@ -27,11 +27,14 @@ require (
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
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/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.8.1
|
||||
go.uber.org/atomic v1.9.0
|
||||
google.golang.org/grpc v1.48.0
|
||||
google.golang.org/protobuf v1.28.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
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
|
||||
@@ -39,8 +42,11 @@ require (
|
||||
istio.io/pkg v0.0.0-20211115195056-e379f31ee62a
|
||||
k8s.io/api v0.22.2
|
||||
k8s.io/apimachinery v0.22.2
|
||||
k8s.io/cli-runtime v0.22.2
|
||||
k8s.io/client-go v0.22.2
|
||||
k8s.io/kubectl v0.22.2
|
||||
sigs.k8s.io/controller-runtime v0.10.2
|
||||
sigs.k8s.io/yaml v1.3.0
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -142,7 +148,6 @@ require (
|
||||
github.com/opencontainers/runc v1.0.2 // indirect
|
||||
github.com/openshift/api v0.0.0-20200713203337-b2494ecb17dd // indirect
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_golang v1.12.2 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
@@ -154,7 +159,6 @@ require (
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
github.com/spf13/cast v1.3.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3 // indirect
|
||||
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca // indirect
|
||||
github.com/yl2chen/cidranger v1.0.2 // indirect
|
||||
@@ -181,21 +185,17 @@ require (
|
||||
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/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.22.2 // indirect
|
||||
k8s.io/cli-runtime 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/kubectl v0.22.2 // indirect
|
||||
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // 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
|
||||
sigs.k8s.io/mcs-api v0.1.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
)
|
||||
|
||||
replace github.com/dubbogo/gost => github.com/johnlanni/gost v1.11.23-0.20220713132522-0967a24036c6
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
apiVersion: v2
|
||||
appVersion: 0.7.1
|
||||
appVersion: 1.0.0-rc
|
||||
description: Helm chart for deploying higress gateways
|
||||
icon: https://higress.io/img/higress_logo_small.png
|
||||
keywords:
|
||||
@@ -9,4 +9,4 @@ name: higress-core
|
||||
sources:
|
||||
- http://github.com/alibaba/higress
|
||||
type: application
|
||||
version: 0.7.1
|
||||
version: 1.0.0-rc
|
||||
|
||||
407
helm/core/LICENSE
Normal file
407
helm/core/LICENSE
Normal file
@@ -0,0 +1,407 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
========================================================================
|
||||
Higress Subcomponents:
|
||||
|
||||
The Higress project contains subcomponents with separate copyright
|
||||
notices and license terms. Your use of the source code for the these
|
||||
subcomponents is subject to the terms and conditions of the following
|
||||
licenses.
|
||||
========================================================================
|
||||
Apache-2.0 licenses
|
||||
========================================================================
|
||||
|
||||
cloud.google.com/go v0.97.0 Apache-2.0
|
||||
cloud.google.com/go/logging v1.4.2 Apache-2.0
|
||||
contrib.go.opencensus.io/exporter/prometheus v0.4.0 Apache-2.0
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible Apache-2.0
|
||||
github.com/Azure/go-autorest/autorest v0.11.20 Apache-2.0
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.15 Apache-2.0
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0 Apache-2.0
|
||||
github.com/Azure/go-autorest/logger v0.2.1 Apache-2.0
|
||||
github.com/Azure/go-autorest/tracing v0.6.0 Apache-2.0
|
||||
github.com/Masterminds/goutils v1.1.1 Apache-2.0
|
||||
github.com/aws/aws-sdk-go v1.41.7 Apache-2.0
|
||||
github.com/census-instrumentation/opencensus-proto v0.3.0 Apache-2.0
|
||||
github.com/cncf/xds/go v0.0.0-20220520190051-1e77728a1eaa Apache-2.0
|
||||
github.com/containerd/continuity v0.1.0 Apache-2.0
|
||||
github.com/docker/cli v20.10.7+incompatible Apache-2.0
|
||||
github.com/docker/distribution v0.0.0-20191216044856-a8371794149d Apache-2.0
|
||||
github.com/docker/go-units v0.4.0 Apache-2.0
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0 Apache-2.0
|
||||
github.com/go-logr/logr v0.4.0 Apache-2.0
|
||||
github.com/go-openapi/jsonpointer v0.19.5 Apache-2.0
|
||||
github.com/go-openapi/jsonreference v0.19.5 Apache-2.0
|
||||
github.com/go-openapi/swag v0.19.14 Apache-2.0
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da Apache-2.0
|
||||
github.com/google/btree v1.0.1 Apache-2.0
|
||||
github.com/google/go-containerregistry v0.6.0 Apache-2.0
|
||||
github.com/google/gofuzz v1.2.0 Apache-2.0
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 Apache-2.0
|
||||
github.com/googleapis/gnostic v0.5.5 Apache-2.0
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 Apache-2.0
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 Apache-2.0
|
||||
github.com/inconshreveable/mousetrap v1.0.0 Apache-2.0
|
||||
github.com/jmespath/go-jmespath v0.4.0 Apache-2.0
|
||||
github.com/jonboulle/clockwork v0.2.2 Apache-2.0
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 Apache-2.0
|
||||
github.com/moby/moby v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible Apache-2.0
|
||||
github.com/moby/spdystream v0.2.0 Apache-2.0
|
||||
github.com/moby/term v0.0.0-20210610120745-9d4ed1856297 Apache-2.0
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd Apache-2.0
|
||||
github.com/modern-go/reflect2 v1.0.1 Apache-2.0
|
||||
github.com/opencontainers/go-digest v1.0.0 Apache-2.0
|
||||
github.com/opencontainers/image-spec v1.0.1 Apache-2.0
|
||||
github.com/opencontainers/runc v1.0.2 Apache-2.0
|
||||
github.com/openshift/api v0.0.0-20200713203337-b2494ecb17dd Apache-2.0
|
||||
github.com/prometheus/client_golang v1.11.0 Apache-2.0
|
||||
github.com/prometheus/client_model v0.2.0 Apache-2.0
|
||||
github.com/prometheus/common v0.32.1 Apache-2.0
|
||||
github.com/prometheus/procfs v0.6.0 Apache-2.0
|
||||
github.com/prometheus/statsd_exporter v0.21.0 Apache-2.0
|
||||
github.com/spf13/cobra v1.2.1 Apache-2.0
|
||||
go.opencensus.io v0.23.0 Apache-2.0
|
||||
go.opentelemetry.io/proto/otlp v0.7.0 Apache-2.0
|
||||
gomodules.xyz/jsonpatch/v2 v2.2.0 Apache-2.0
|
||||
gomodules.xyz/jsonpatch/v3 v3.0.1 Apache-2.0
|
||||
google.golang.org/appengine v1.6.7 Apache-2.0
|
||||
google.golang.org/genproto v0.0.0-20211020151524-b7c3a969101a Apache-2.0
|
||||
google.golang.org/grpc v1.42.0 Apache-2.0
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 Apache-2.0
|
||||
gopkg.in/yaml.v2 v2.4.0 Apache-2.0
|
||||
istio.io/gogo-genproto v0.0.0-20211115195057-0e34bdd2be67 Apache-2.0
|
||||
k8s.io/api v0.22.2 Apache-2.0
|
||||
k8s.io/apiextensions-apiserver v0.22.2 Apache-2.0
|
||||
k8s.io/apimachinery v0.22.2 Apache-2.0
|
||||
k8s.io/cli-runtime v0.22.2 Apache-2.0
|
||||
k8s.io/client-go v0.22.2 Apache-2.0
|
||||
k8s.io/component-base v0.22.2 Apache-2.0
|
||||
k8s.io/klog/v2 v2.10.0 Apache-2.0
|
||||
k8s.io/kube-openapi v0.0.0-20211020163157-7327e2aaee2b Apache-2.0
|
||||
k8s.io/kubectl v0.22.2 Apache-2.0
|
||||
k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b Apache-2.0
|
||||
sigs.k8s.io/controller-runtime v0.10.2 Apache-2.0
|
||||
sigs.k8s.io/gateway-api v0.4.0 Apache-2.0
|
||||
sigs.k8s.io/kustomize/api v0.8.11 Apache-2.0
|
||||
sigs.k8s.io/kustomize/kyaml v0.11.0 Apache-2.0
|
||||
sigs.k8s.io/mcs-api v0.1.0 Apache-2.0
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.1.2 Apache-2.0
|
||||
|
||||
========================================================================
|
||||
BSD-2-Clause licenses
|
||||
========================================================================
|
||||
|
||||
github.com/pkg/errors v0.9.1 BSD-2-Clause
|
||||
github.com/russross/blackfriday v1.5.2 BSD-2-Clause
|
||||
|
||||
========================================================================
|
||||
BSD-3-Clause licenses
|
||||
========================================================================
|
||||
|
||||
github.com/PuerkitoBio/purell v1.1.1 BSD-3-Clause
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 BSD-3-Clause
|
||||
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 BSD-3-Clause
|
||||
github.com/evanphx/json-patch v4.11.0+incompatible BSD-3-Clause
|
||||
github.com/evanphx/json-patch/v5 v5.6.0 BSD-3-Clause
|
||||
github.com/fsnotify/fsnotify v1.5.1 BSD-3-Clause
|
||||
github.com/gogo/protobuf v1.3.2 BSD-3-Clause
|
||||
github.com/golang/protobuf v1.5.2 BSD-3-Clause
|
||||
github.com/google/go-cmp v0.5.6 BSD-3-Clause
|
||||
github.com/google/uuid v1.3.0 BSD-3-Clause
|
||||
github.com/googleapis/gax-go/v2 v2.1.1 BSD-3-Clause
|
||||
github.com/imdario/mergo v0.3.5 BSD-3-Clause
|
||||
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de BSD-3-Clause
|
||||
github.com/pmezard/go-difflib v1.0.0 BSD-3-Clause
|
||||
github.com/spaolacci/murmur3 v1.1.0 BSD-3-Clause
|
||||
github.com/spf13/pflag v1.0.5 BSD-3-Clause
|
||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 BSD-3-Clause
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 BSD-3-Clause
|
||||
golang.org/x/net v0.0.0-20211020060615-d418f374d309 BSD-3-Clause
|
||||
golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1 BSD-3-Clause
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c BSD-3-Clause
|
||||
golang.org/x/sys v0.0.0-20211020174200-9d6173849985 BSD-3-Clause
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d BSD-3-Clause
|
||||
golang.org/x/text v0.3.6 BSD-3-Clause
|
||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac BSD-3-Clause
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 BSD-3-Clause
|
||||
google.golang.org/api v0.59.0 BSD-3-Clause
|
||||
google.golang.org/protobuf v1.27.1 BSD-3-Clause
|
||||
gopkg.in/inf.v0 v0.9.1 BSD-3-Clause
|
||||
|
||||
========================================================================
|
||||
ISC licenses
|
||||
========================================================================
|
||||
|
||||
github.com/davecgh/go-spew v1.1.1 ISC
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0 ISC
|
||||
|
||||
========================================================================
|
||||
MIT licenses
|
||||
========================================================================
|
||||
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 MIT
|
||||
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd MIT
|
||||
github.com/Masterminds/semver/v3 v3.1.1 MIT
|
||||
github.com/Masterminds/sprig/v3 v3.2.2 MIT
|
||||
github.com/Microsoft/go-winio v0.5.0 MIT
|
||||
github.com/Microsoft/hcsshim v0.8.21 MIT
|
||||
github.com/beorn7/perks v1.0.1 MIT
|
||||
github.com/cenkalti/backoff/v4 v4.1.1 MIT
|
||||
github.com/cespare/xxhash/v2 v2.1.1 MIT
|
||||
github.com/docker/docker-credential-helpers v0.6.3 MIT
|
||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d MIT
|
||||
github.com/fvbommel/sortorder v1.0.1 MIT
|
||||
github.com/go-errors/errors v1.0.1 MIT
|
||||
github.com/go-kit/log v0.1.0 MIT
|
||||
github.com/go-logfmt/logfmt v0.5.0 MIT
|
||||
github.com/goccy/go-json v0.4.8 MIT
|
||||
github.com/golang-jwt/jwt/v4 v4.0.0 MIT
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 MIT
|
||||
github.com/huandu/xstrings v1.3.2 MIT
|
||||
github.com/josharian/intern v1.0.0 MIT
|
||||
github.com/json-iterator/go v1.1.11 MIT
|
||||
github.com/lestrrat-go/backoff/v2 v2.0.7 MIT
|
||||
github.com/lestrrat-go/blackmagic v1.0.0 MIT
|
||||
github.com/lestrrat-go/httpcc v1.0.0 MIT
|
||||
github.com/lestrrat-go/iter v1.0.1 MIT
|
||||
github.com/lestrrat-go/jwx v1.2.0 MIT
|
||||
github.com/lestrrat-go/option v1.0.0 MIT
|
||||
github.com/mailru/easyjson v0.7.6 MIT
|
||||
github.com/mitchellh/copystructure v1.2.0 MIT
|
||||
github.com/mitchellh/go-wordwrap v1.0.0 MIT
|
||||
github.com/mitchellh/reflectwalk v1.0.2 MIT
|
||||
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 MIT
|
||||
github.com/natefinch/lumberjack v2.0.0+incompatible MIT
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible MIT
|
||||
github.com/shopspring/decimal v1.2.0 MIT
|
||||
github.com/sirupsen/logrus v1.8.1 MIT
|
||||
github.com/spf13/cast v1.3.1 MIT
|
||||
github.com/stretchr/testify v1.7.0 MIT
|
||||
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca MIT
|
||||
github.com/yl2chen/cidranger v1.0.2 MIT
|
||||
go.uber.org/atomic v1.9.0 MIT
|
||||
go.uber.org/multierr v1.7.0 MIT
|
||||
go.uber.org/zap v1.19.1 MIT
|
||||
gomodules.xyz/orderedmap v0.1.0 MIT
|
||||
|
||||
========================================================================
|
||||
MIT and Apache-2.0 licenses
|
||||
========================================================================
|
||||
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b MIT and Apache-2.0
|
||||
|
||||
========================================================================
|
||||
MIT and BSD-3-Clause licenses
|
||||
========================================================================
|
||||
|
||||
github.com/ghodss/yaml v1.0.0 MIT and BSD-3-Clause
|
||||
sigs.k8s.io/yaml v1.3.0 MIT and BSD-3-Clause
|
||||
|
||||
========================================================================
|
||||
MPL-2.0 licenses
|
||||
========================================================================
|
||||
|
||||
github.com/hashicorp/errwrap v1.0.0 MPL-2.0
|
||||
github.com/hashicorp/go-multierror v1.1.1 MPL-2.0
|
||||
github.com/hashicorp/go-version v1.3.0 MPL-2.0
|
||||
github.com/hashicorp/golang-lru v0.5.4 MPL-2.0
|
||||
5
helm/core/README.md
Normal file
5
helm/core/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Higress Core Helm Chart
|
||||
|
||||
Installs the core components of cloud-native gateway [Higress](http://higress.io/)
|
||||
|
||||
**Note:** It is highly recommended to install the whole package of Higress. Please visit https://higress.io/docs/user/quickstart/ for details.
|
||||
@@ -29,6 +29,9 @@
|
||||
{{- end }}
|
||||
|
||||
defaultConfig:
|
||||
{{- if .Values.global.disableAlpnH2 }}
|
||||
disableAlpnH2: true
|
||||
{{- end }}
|
||||
{{- if .Values.global.meshID }}
|
||||
meshId: {{ .Values.global.meshID }}
|
||||
{{- end }}
|
||||
|
||||
@@ -128,7 +128,7 @@ spec:
|
||||
- name: CUSTOM_CA_CERT_NAME
|
||||
value: "higress-ca-root-cert"
|
||||
{{- end }}
|
||||
{{- if not .Values.global.kind }}
|
||||
{{- if not (or .Values.global.local .Values.global.kind) }}
|
||||
resources:
|
||||
{{- if .Values.pilot.resources }}
|
||||
{{ toYaml .Values.pilot.resources | trim | indent 12 }}
|
||||
@@ -173,12 +173,12 @@ spec:
|
||||
- "serve"
|
||||
- --gatewaySelectorKey=higress
|
||||
- --gatewaySelectorValue={{ .Release.Namespace }}-{{ include "gateway.name" . }}
|
||||
{{- if not .Values.enableStatus }}
|
||||
- --enableStatus={{ .Values.enableStatus }}
|
||||
{{- if not .Values.global.enableStatus }}
|
||||
- --enableStatus={{ .Values.global.enableStatus }}
|
||||
{{- end }}
|
||||
- --ingressClass={{ .Values.ingressClass }}
|
||||
{{- if .Values.watchNamespace }}
|
||||
- --watchNamespace={{ .Values.watchNamespace }}
|
||||
- --ingressClass={{ .Values.global.ingressClass }}
|
||||
{{- if .Values.global.watchNamespace }}
|
||||
- --watchNamespace={{ .Values.global.watchNamespace }}
|
||||
{{- end }}
|
||||
env:
|
||||
- name: POD_NAME
|
||||
@@ -210,7 +210,7 @@ spec:
|
||||
{{- end }}
|
||||
readinessProbe:
|
||||
{{- toYaml .Values.controller.probe | nindent 12 }}
|
||||
{{- if not .Values.global.kind }}
|
||||
{{- if not (or .Values.global.local .Values.global.kind) }}
|
||||
resources:
|
||||
{{- toYaml .Values.controller.resources | nindent 12 }}
|
||||
{{- end }}
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
{{- $unprivilegedPortSupported := true }}
|
||||
{{- range $index, $node := (lookup "v1" "Node" "default" "").items }}
|
||||
{{- $kernelVersion := $node.status.nodeInfo.kernelVersion }}
|
||||
{{- if $kernelVersion }}
|
||||
{{- $kernelVersion = regexFind "^(\\d+\\.\\d+\\.\\d+)" $kernelVersion }}
|
||||
{{- if and $kernelVersion (semverCompare "<4.11.0" $kernelVersion) }}
|
||||
{{- $unprivilegedPortSupported = false }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
@@ -9,7 +19,7 @@ metadata:
|
||||
{{- .Values.gateway.annotations | toYaml | nindent 4 }}
|
||||
spec:
|
||||
{{- if not .Values.gateway.autoscaling.enabled }}
|
||||
{{- if not .Values.global.kind }}
|
||||
{{- if not (or .Values.global.local .Values.global.kind) }}
|
||||
replicas: {{ .Values.gateway.replicas }}
|
||||
{{- else }}
|
||||
replicas: 1
|
||||
@@ -21,7 +31,7 @@ spec:
|
||||
strategy:
|
||||
rollingUpdate:
|
||||
maxSurge: {{ .Values.gateway.rollingMaxSurge }}
|
||||
{{- if .Values.global.kind }}
|
||||
{{- if or .Values.global.local .Values.global.kind }}
|
||||
maxUnavailable: 100%
|
||||
{{- else }}
|
||||
maxUnavailable: {{ .Values.gateway.rollingMaxUnavailable }}
|
||||
@@ -50,7 +60,7 @@ spec:
|
||||
securityContext:
|
||||
{{- if .Values.gateway.securityContext }}
|
||||
{{- toYaml .Values.gateway.securityContext | nindent 8 }}
|
||||
{{- else if and (not .Values.gateway.hostNetwork) (semverCompare ">=1.22-0" .Capabilities.KubeVersion.GitVersion) }}
|
||||
{{- else if and $unprivilegedPortSupported (and (not .Values.gateway.hostNetwork) (semverCompare ">=1.22-0" .Capabilities.KubeVersion.GitVersion)) }}
|
||||
# Safe since 1.22: https://github.com/kubernetes/kubernetes/pull/103326
|
||||
sysctls:
|
||||
- name: net.ipv4.ip_unprivileged_port_start
|
||||
@@ -71,7 +81,7 @@ spec:
|
||||
securityContext:
|
||||
{{- if .Values.gateway.containerSecurityContext }}
|
||||
{{- toYaml .Values.gateway.containerSecurityContext | nindent 12 }}
|
||||
{{- else if and (not .Values.gateway.hostNetwork) (semverCompare ">=1.22-0" .Capabilities.KubeVersion.GitVersion) }}
|
||||
{{- else if and $unprivilegedPortSupported (and (not .Values.gateway.hostNetwork) (semverCompare ">=1.22-0" .Capabilities.KubeVersion.GitVersion)) }}
|
||||
# Safe since 1.22: https://github.com/kubernetes/kubernetes/pull/103326
|
||||
capabilities:
|
||||
drop:
|
||||
@@ -152,7 +162,7 @@ spec:
|
||||
- containerPort: 15090
|
||||
protocol: TCP
|
||||
name: http-envoy-prom
|
||||
{{- if .Values.global.kind }}
|
||||
{{- if or .Values.global.local .Values.global.kind }}
|
||||
- containerPort: 80
|
||||
hostPort: 80
|
||||
name: http
|
||||
@@ -172,7 +182,7 @@ spec:
|
||||
periodSeconds: 2
|
||||
successThreshold: 1
|
||||
timeoutSeconds: 3
|
||||
{{- if not .Values.global.kind }}
|
||||
{{- if not (or .Values.global.local .Values.global.kind) }}
|
||||
resources:
|
||||
{{- toYaml .Values.gateway.resources | nindent 12 }}
|
||||
{{- end }}
|
||||
|
||||
@@ -1,9 +1,21 @@
|
||||
revision: ""
|
||||
global:
|
||||
# IngressClass filters which ingress resources the higress controller watches.
|
||||
# The default ingress class is higress.
|
||||
# There are some special cases for special ingress class.
|
||||
# 1. When the ingress class is set as nginx, the higress controller will watch ingress
|
||||
# resources with the nginx ingress class or without any ingress class.
|
||||
# 2. When the ingress class is set empty, the higress controller will watch all ingress
|
||||
# resources in the k8s cluster.
|
||||
ingressClass: "higress"
|
||||
watchNamespace: ""
|
||||
disableAlpnH2: true
|
||||
enableStatus: true
|
||||
# whether to use autoscaling/v2 template for HPA settings
|
||||
# for internal usage only, not to be configured by users.
|
||||
autoscalingv2API: true
|
||||
kind: false
|
||||
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
|
||||
# Deprecated
|
||||
enableHigressIstio: false
|
||||
@@ -33,7 +45,7 @@ global:
|
||||
# Dev builds from prow are on gcr.io
|
||||
hub: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress
|
||||
# Default tag for Istio images.
|
||||
tag: 0.7.0
|
||||
tag: 1.0.0-rc
|
||||
|
||||
# Specify image pull policy if default behavior isn't desired.
|
||||
# Default behavior: latest images will be Always else IfNotPresent.
|
||||
@@ -322,16 +334,6 @@ global:
|
||||
caName: ""
|
||||
hub: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress
|
||||
|
||||
# IngressClass filters which ingress resources the higress controller watches.
|
||||
# The default ingress class is higress.
|
||||
# There are some special cases for special ingress class.
|
||||
# 1. When the ingress class is set as nginx, the higress controller will watch ingress
|
||||
# resources with the nginx ingress class or without any ingress class.
|
||||
# 2. When the ingress class is set empty, the higress controller will watch all ingress
|
||||
# resources in the k8s cluster.
|
||||
ingressClass: "higress"
|
||||
watchNamespace: ""
|
||||
enableStatus: true
|
||||
clusterName: ""
|
||||
# meshConfig defines runtime configuration of components, including Istiod and istio-agent behavior
|
||||
# See https://istio.io/docs/reference/config/istio.mesh.v1alpha1/ for all available options
|
||||
@@ -367,7 +369,7 @@ gateway:
|
||||
name: "higress-gateway"
|
||||
replicas: 2
|
||||
image: gateway
|
||||
tag: "0.7.0"
|
||||
tag: "1.0.0-rc"
|
||||
# revision declares which revision this gateway is a part of
|
||||
revision: ""
|
||||
|
||||
@@ -455,7 +457,7 @@ controller:
|
||||
name: "higress-controller"
|
||||
replicas: 1
|
||||
image: higress
|
||||
tag: "0.7.1"
|
||||
tag: "1.0.0-rc"
|
||||
env: {}
|
||||
|
||||
labels: {}
|
||||
@@ -545,7 +547,7 @@ pilot:
|
||||
rollingMaxUnavailable: 25%
|
||||
|
||||
hub: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress
|
||||
tag: 0.7.0
|
||||
tag: 1.0.0-rc
|
||||
|
||||
# Can be a full hub/image:tag
|
||||
image: pilot
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
dependencies:
|
||||
- name: higress-core
|
||||
repository: file://../core
|
||||
version: 0.7.1
|
||||
version: 1.0.0-rc
|
||||
- name: higress-console
|
||||
repository: https://higress.io/helm-charts/
|
||||
version: 0.1.1
|
||||
digest: sha256:051fbd7b2916d1d0c26839d0e27653f6e42d20e9294bd9eed9628f24c5a7b228
|
||||
generated: "2023-04-03T13:42:23.705379+08:00"
|
||||
version: 1.0.0-rc
|
||||
digest: sha256:e22d3f977aec759e23f035bad2cdff643e5f670788872316234701f6d0d91418
|
||||
generated: "2023-05-08T09:40:35.6095006+08:00"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
apiVersion: v2
|
||||
appVersion: 0.7.1
|
||||
appVersion: 1.0.0-rc
|
||||
description: Helm chart for deploying higress gateways
|
||||
icon: https://higress.io/img/higress_logo_small.png
|
||||
keywords:
|
||||
@@ -11,9 +11,9 @@ sources:
|
||||
dependencies:
|
||||
- name: higress-core
|
||||
repository: "file://../core"
|
||||
version: 0.7.1
|
||||
version: 1.0.0-rc
|
||||
- name: higress-console
|
||||
repository: "https://higress.io/helm-charts/"
|
||||
version: 0.1.1
|
||||
version: 1.0.0-rc
|
||||
type: application
|
||||
version: 0.7.1
|
||||
version: 1.0.0-rc
|
||||
|
||||
407
helm/higress/LICENSE
Normal file
407
helm/higress/LICENSE
Normal file
@@ -0,0 +1,407 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
========================================================================
|
||||
Higress Subcomponents:
|
||||
|
||||
The Higress project contains subcomponents with separate copyright
|
||||
notices and license terms. Your use of the source code for the these
|
||||
subcomponents is subject to the terms and conditions of the following
|
||||
licenses.
|
||||
========================================================================
|
||||
Apache-2.0 licenses
|
||||
========================================================================
|
||||
|
||||
cloud.google.com/go v0.97.0 Apache-2.0
|
||||
cloud.google.com/go/logging v1.4.2 Apache-2.0
|
||||
contrib.go.opencensus.io/exporter/prometheus v0.4.0 Apache-2.0
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible Apache-2.0
|
||||
github.com/Azure/go-autorest/autorest v0.11.20 Apache-2.0
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.15 Apache-2.0
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0 Apache-2.0
|
||||
github.com/Azure/go-autorest/logger v0.2.1 Apache-2.0
|
||||
github.com/Azure/go-autorest/tracing v0.6.0 Apache-2.0
|
||||
github.com/Masterminds/goutils v1.1.1 Apache-2.0
|
||||
github.com/aws/aws-sdk-go v1.41.7 Apache-2.0
|
||||
github.com/census-instrumentation/opencensus-proto v0.3.0 Apache-2.0
|
||||
github.com/cncf/xds/go v0.0.0-20220520190051-1e77728a1eaa Apache-2.0
|
||||
github.com/containerd/continuity v0.1.0 Apache-2.0
|
||||
github.com/docker/cli v20.10.7+incompatible Apache-2.0
|
||||
github.com/docker/distribution v0.0.0-20191216044856-a8371794149d Apache-2.0
|
||||
github.com/docker/go-units v0.4.0 Apache-2.0
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0 Apache-2.0
|
||||
github.com/go-logr/logr v0.4.0 Apache-2.0
|
||||
github.com/go-openapi/jsonpointer v0.19.5 Apache-2.0
|
||||
github.com/go-openapi/jsonreference v0.19.5 Apache-2.0
|
||||
github.com/go-openapi/swag v0.19.14 Apache-2.0
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da Apache-2.0
|
||||
github.com/google/btree v1.0.1 Apache-2.0
|
||||
github.com/google/go-containerregistry v0.6.0 Apache-2.0
|
||||
github.com/google/gofuzz v1.2.0 Apache-2.0
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 Apache-2.0
|
||||
github.com/googleapis/gnostic v0.5.5 Apache-2.0
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 Apache-2.0
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 Apache-2.0
|
||||
github.com/inconshreveable/mousetrap v1.0.0 Apache-2.0
|
||||
github.com/jmespath/go-jmespath v0.4.0 Apache-2.0
|
||||
github.com/jonboulle/clockwork v0.2.2 Apache-2.0
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 Apache-2.0
|
||||
github.com/moby/moby v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible Apache-2.0
|
||||
github.com/moby/spdystream v0.2.0 Apache-2.0
|
||||
github.com/moby/term v0.0.0-20210610120745-9d4ed1856297 Apache-2.0
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd Apache-2.0
|
||||
github.com/modern-go/reflect2 v1.0.1 Apache-2.0
|
||||
github.com/opencontainers/go-digest v1.0.0 Apache-2.0
|
||||
github.com/opencontainers/image-spec v1.0.1 Apache-2.0
|
||||
github.com/opencontainers/runc v1.0.2 Apache-2.0
|
||||
github.com/openshift/api v0.0.0-20200713203337-b2494ecb17dd Apache-2.0
|
||||
github.com/prometheus/client_golang v1.11.0 Apache-2.0
|
||||
github.com/prometheus/client_model v0.2.0 Apache-2.0
|
||||
github.com/prometheus/common v0.32.1 Apache-2.0
|
||||
github.com/prometheus/procfs v0.6.0 Apache-2.0
|
||||
github.com/prometheus/statsd_exporter v0.21.0 Apache-2.0
|
||||
github.com/spf13/cobra v1.2.1 Apache-2.0
|
||||
go.opencensus.io v0.23.0 Apache-2.0
|
||||
go.opentelemetry.io/proto/otlp v0.7.0 Apache-2.0
|
||||
gomodules.xyz/jsonpatch/v2 v2.2.0 Apache-2.0
|
||||
gomodules.xyz/jsonpatch/v3 v3.0.1 Apache-2.0
|
||||
google.golang.org/appengine v1.6.7 Apache-2.0
|
||||
google.golang.org/genproto v0.0.0-20211020151524-b7c3a969101a Apache-2.0
|
||||
google.golang.org/grpc v1.42.0 Apache-2.0
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 Apache-2.0
|
||||
gopkg.in/yaml.v2 v2.4.0 Apache-2.0
|
||||
istio.io/gogo-genproto v0.0.0-20211115195057-0e34bdd2be67 Apache-2.0
|
||||
k8s.io/api v0.22.2 Apache-2.0
|
||||
k8s.io/apiextensions-apiserver v0.22.2 Apache-2.0
|
||||
k8s.io/apimachinery v0.22.2 Apache-2.0
|
||||
k8s.io/cli-runtime v0.22.2 Apache-2.0
|
||||
k8s.io/client-go v0.22.2 Apache-2.0
|
||||
k8s.io/component-base v0.22.2 Apache-2.0
|
||||
k8s.io/klog/v2 v2.10.0 Apache-2.0
|
||||
k8s.io/kube-openapi v0.0.0-20211020163157-7327e2aaee2b Apache-2.0
|
||||
k8s.io/kubectl v0.22.2 Apache-2.0
|
||||
k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b Apache-2.0
|
||||
sigs.k8s.io/controller-runtime v0.10.2 Apache-2.0
|
||||
sigs.k8s.io/gateway-api v0.4.0 Apache-2.0
|
||||
sigs.k8s.io/kustomize/api v0.8.11 Apache-2.0
|
||||
sigs.k8s.io/kustomize/kyaml v0.11.0 Apache-2.0
|
||||
sigs.k8s.io/mcs-api v0.1.0 Apache-2.0
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.1.2 Apache-2.0
|
||||
|
||||
========================================================================
|
||||
BSD-2-Clause licenses
|
||||
========================================================================
|
||||
|
||||
github.com/pkg/errors v0.9.1 BSD-2-Clause
|
||||
github.com/russross/blackfriday v1.5.2 BSD-2-Clause
|
||||
|
||||
========================================================================
|
||||
BSD-3-Clause licenses
|
||||
========================================================================
|
||||
|
||||
github.com/PuerkitoBio/purell v1.1.1 BSD-3-Clause
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 BSD-3-Clause
|
||||
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 BSD-3-Clause
|
||||
github.com/evanphx/json-patch v4.11.0+incompatible BSD-3-Clause
|
||||
github.com/evanphx/json-patch/v5 v5.6.0 BSD-3-Clause
|
||||
github.com/fsnotify/fsnotify v1.5.1 BSD-3-Clause
|
||||
github.com/gogo/protobuf v1.3.2 BSD-3-Clause
|
||||
github.com/golang/protobuf v1.5.2 BSD-3-Clause
|
||||
github.com/google/go-cmp v0.5.6 BSD-3-Clause
|
||||
github.com/google/uuid v1.3.0 BSD-3-Clause
|
||||
github.com/googleapis/gax-go/v2 v2.1.1 BSD-3-Clause
|
||||
github.com/imdario/mergo v0.3.5 BSD-3-Clause
|
||||
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de BSD-3-Clause
|
||||
github.com/pmezard/go-difflib v1.0.0 BSD-3-Clause
|
||||
github.com/spaolacci/murmur3 v1.1.0 BSD-3-Clause
|
||||
github.com/spf13/pflag v1.0.5 BSD-3-Clause
|
||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 BSD-3-Clause
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 BSD-3-Clause
|
||||
golang.org/x/net v0.0.0-20211020060615-d418f374d309 BSD-3-Clause
|
||||
golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1 BSD-3-Clause
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c BSD-3-Clause
|
||||
golang.org/x/sys v0.0.0-20211020174200-9d6173849985 BSD-3-Clause
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d BSD-3-Clause
|
||||
golang.org/x/text v0.3.6 BSD-3-Clause
|
||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac BSD-3-Clause
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 BSD-3-Clause
|
||||
google.golang.org/api v0.59.0 BSD-3-Clause
|
||||
google.golang.org/protobuf v1.27.1 BSD-3-Clause
|
||||
gopkg.in/inf.v0 v0.9.1 BSD-3-Clause
|
||||
|
||||
========================================================================
|
||||
ISC licenses
|
||||
========================================================================
|
||||
|
||||
github.com/davecgh/go-spew v1.1.1 ISC
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0 ISC
|
||||
|
||||
========================================================================
|
||||
MIT licenses
|
||||
========================================================================
|
||||
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 MIT
|
||||
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd MIT
|
||||
github.com/Masterminds/semver/v3 v3.1.1 MIT
|
||||
github.com/Masterminds/sprig/v3 v3.2.2 MIT
|
||||
github.com/Microsoft/go-winio v0.5.0 MIT
|
||||
github.com/Microsoft/hcsshim v0.8.21 MIT
|
||||
github.com/beorn7/perks v1.0.1 MIT
|
||||
github.com/cenkalti/backoff/v4 v4.1.1 MIT
|
||||
github.com/cespare/xxhash/v2 v2.1.1 MIT
|
||||
github.com/docker/docker-credential-helpers v0.6.3 MIT
|
||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d MIT
|
||||
github.com/fvbommel/sortorder v1.0.1 MIT
|
||||
github.com/go-errors/errors v1.0.1 MIT
|
||||
github.com/go-kit/log v0.1.0 MIT
|
||||
github.com/go-logfmt/logfmt v0.5.0 MIT
|
||||
github.com/goccy/go-json v0.4.8 MIT
|
||||
github.com/golang-jwt/jwt/v4 v4.0.0 MIT
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 MIT
|
||||
github.com/huandu/xstrings v1.3.2 MIT
|
||||
github.com/josharian/intern v1.0.0 MIT
|
||||
github.com/json-iterator/go v1.1.11 MIT
|
||||
github.com/lestrrat-go/backoff/v2 v2.0.7 MIT
|
||||
github.com/lestrrat-go/blackmagic v1.0.0 MIT
|
||||
github.com/lestrrat-go/httpcc v1.0.0 MIT
|
||||
github.com/lestrrat-go/iter v1.0.1 MIT
|
||||
github.com/lestrrat-go/jwx v1.2.0 MIT
|
||||
github.com/lestrrat-go/option v1.0.0 MIT
|
||||
github.com/mailru/easyjson v0.7.6 MIT
|
||||
github.com/mitchellh/copystructure v1.2.0 MIT
|
||||
github.com/mitchellh/go-wordwrap v1.0.0 MIT
|
||||
github.com/mitchellh/reflectwalk v1.0.2 MIT
|
||||
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 MIT
|
||||
github.com/natefinch/lumberjack v2.0.0+incompatible MIT
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible MIT
|
||||
github.com/shopspring/decimal v1.2.0 MIT
|
||||
github.com/sirupsen/logrus v1.8.1 MIT
|
||||
github.com/spf13/cast v1.3.1 MIT
|
||||
github.com/stretchr/testify v1.7.0 MIT
|
||||
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca MIT
|
||||
github.com/yl2chen/cidranger v1.0.2 MIT
|
||||
go.uber.org/atomic v1.9.0 MIT
|
||||
go.uber.org/multierr v1.7.0 MIT
|
||||
go.uber.org/zap v1.19.1 MIT
|
||||
gomodules.xyz/orderedmap v0.1.0 MIT
|
||||
|
||||
========================================================================
|
||||
MIT and Apache-2.0 licenses
|
||||
========================================================================
|
||||
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b MIT and Apache-2.0
|
||||
|
||||
========================================================================
|
||||
MIT and BSD-3-Clause licenses
|
||||
========================================================================
|
||||
|
||||
github.com/ghodss/yaml v1.0.0 MIT and BSD-3-Clause
|
||||
sigs.k8s.io/yaml v1.3.0 MIT and BSD-3-Clause
|
||||
|
||||
========================================================================
|
||||
MPL-2.0 licenses
|
||||
========================================================================
|
||||
|
||||
github.com/hashicorp/errwrap v1.0.0 MPL-2.0
|
||||
github.com/hashicorp/go-multierror v1.1.1 MPL-2.0
|
||||
github.com/hashicorp/go-version v1.3.0 MPL-2.0
|
||||
github.com/hashicorp/golang-lru v0.5.4 MPL-2.0
|
||||
56
helm/higress/README.md
Normal file
56
helm/higress/README.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# Higress Helm Chart
|
||||
|
||||
Installs the cloud-native gateway [Higress](http://higress.io/)
|
||||
|
||||
## Get Repo Info
|
||||
|
||||
```console
|
||||
helm repo add higress.io https://higress.io/helm-charts
|
||||
helm repo update
|
||||
```
|
||||
|
||||
_See [helm repo](https://helm.sh/docs/helm/helm_repo/) for command documentation._
|
||||
|
||||
## Installing the Chart
|
||||
|
||||
To install the chart with the release name `higress`:
|
||||
|
||||
```console
|
||||
helm install higress -n higress-system higress.io/higress --create-namespace --render-subchart-notes
|
||||
```
|
||||
|
||||
## Uninstalling the Chart
|
||||
|
||||
To uninstall/delete the higress deployment:
|
||||
|
||||
```console
|
||||
helm delete higress -n higress-system
|
||||
```
|
||||
|
||||
The command removes all the Kubernetes components associated with the chart and deletes the release.
|
||||
|
||||
## Configuration
|
||||
|
||||
| **Parameter** | **Description** | **Default** |
|
||||
|---|---|---|
|
||||
| **Global Parameters** | | |
|
||||
| global.local | Set to `true` if installing to a local K8s cluster (e.g.: Kind, Rancher Desktop, etc.) | false |
|
||||
| global.ingressClass | [IngressClass](https://kubernetes.io/zh-cn/docs/concepts/services-networking/ingress/#ingress-class) which is used to filter Ingress resources Higress Controller watches.<br />If there are multiple gateway instances deployed in the cluster, this parameter can be used to distinguish the scope of each gateway instance.<br />There are some special cases for special IngressClass values:<br />1. If set to "nginx", Higress Controller will watch Ingress resources with the `nginx` IngressClass or without any Ingress class.<br />2. If set to empty, Higress Controller will watch all Ingress resources in the K8s cluster. | higress |
|
||||
| global.watchNamespace | If not empty, Higress Controller will only watch resources in the specified namespace. When isolating different business systems using K8s namespace, if each namespace requires a standalone gateway instance, this parameter can be used to confine the Ingress watching of Higress within the given namespace. | "" |
|
||||
| global.disableAlpnH2 | Whether to disable HTTP/2 in ALPN | true |
|
||||
| global.enableStatus | If `true`, Higress Controller will update the `status` field of Ingress resources.<br />When migrating from Nginx Ingress, in order to avoid `status` field of Ingress objects being overwritten, this parameter needs to be set to false, so Higress won't write the entry IP to the `status` field of the corresponding Ingress object. | true |
|
||||
| global.enableIstioAPI | If `true`, Higress Controller will monitor istio resources as well | false |
|
||||
| global.istioNamespace | The namespace istio is installed to | istio-system |
|
||||
| **Core Paramters** | | |
|
||||
| higress-core.gateway.replicas | Number of Higress Gateway pods | 2 |
|
||||
| higress-core.controller.replicas | Number of Higress Controller pods | 1 |
|
||||
| **Console Paramters** | | |
|
||||
| higress-console.replicaCount | Number of Higress Console pods | 1 |
|
||||
| higress-console.service.type | K8s service type used by Higress Console | ClusterIP |
|
||||
| higress-console.domain | Domain used to access Higress Console | console.higress.io |
|
||||
| higress-console.tlsSecretName | Name of Secret resource used by TLS connections. | "" |
|
||||
| higress-console.web.login.prompt | Prompt message to be displayed on the login page | "" |
|
||||
| higress-console.admin.password.value | If not empty, the admin password will be configured to the specified value. | "" |
|
||||
| higress-console.admin.password.length | The length of random admin password generated during installation. Only works when `higress-console.admin.password.value` is not set. | 8 |
|
||||
| higress-console.o11y.enabled | If `true`, o11y suite (Grafana + Promethues) will be installed. | false |
|
||||
| higress-console.pvc.rwxSupported | Set to `false` when installing to a standard K8s cluster and the target cluster doesn't support the ReadWriteMany access mode of PersistentVolumeClaim. | true |
|
||||
54
istio/1.12/patches/istio/20230408-ignore-ns.patch
Normal file
54
istio/1.12/patches/istio/20230408-ignore-ns.patch
Normal file
@@ -0,0 +1,54 @@
|
||||
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-04-08 16:02:02.000000000 +0800
|
||||
+++ istio-new/pilot/pkg/networking/core/v1alpha3/route/route.go 2023-04-07 18:19:20.000000000 +0800
|
||||
@@ -1049,6 +1049,10 @@
|
||||
out.QueryParameterMatchSpecifier = &route.QueryParameterMatcher_StringMatch{
|
||||
StringMatch: &matcher.StringMatcher{MatchPattern: &matcher.StringMatcher_Exact{Exact: m.Exact}},
|
||||
}
|
||||
+ case *networking.StringMatch_Prefix:
|
||||
+ out.QueryParameterMatchSpecifier = &route.QueryParameterMatcher_StringMatch{
|
||||
+ StringMatch: &matcher.StringMatcher{MatchPattern: &matcher.StringMatcher_Prefix{Prefix: m.Prefix}},
|
||||
+ }
|
||||
case *networking.StringMatch_Regex:
|
||||
out.QueryParameterMatchSpecifier = &route.QueryParameterMatcher_StringMatch{
|
||||
StringMatch: &matcher.StringMatcher{
|
||||
diff -Naur istio/pilot/pkg/serviceregistry/kube/controller/namespacecontroller.go istio-new/pilot/pkg/serviceregistry/kube/controller/namespacecontroller.go
|
||||
--- istio/pilot/pkg/serviceregistry/kube/controller/namespacecontroller.go 2023-04-08 16:02:02.000000000 +0800
|
||||
+++ istio-new/pilot/pkg/serviceregistry/kube/controller/namespacecontroller.go 2023-04-08 14:35:57.000000000 +0800
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
+ "os"
|
||||
"time"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
@@ -171,9 +172,16 @@
|
||||
return k8s.InsertDataToConfigMap(nc.client, nc.configmapLister, meta, nc.caBundleWatcher.GetCABundle())
|
||||
}
|
||||
|
||||
+var podNs = os.Getenv("POD_NAMESPACE")
|
||||
+
|
||||
// On namespace change, update the config map.
|
||||
// If terminating, this will be skipped
|
||||
func (nc *NamespaceController) namespaceChange(ns *v1.Namespace) {
|
||||
+ // Added by ingress
|
||||
+ if ns.Name != podNs {
|
||||
+ return
|
||||
+ }
|
||||
+ // End added by ingress
|
||||
if ns.Status.Phase != v1.NamespaceTerminating {
|
||||
nc.syncNamespace(ns.Name)
|
||||
}
|
||||
@@ -186,6 +194,11 @@
|
||||
log.Errorf("failed to convert to configmap: %v", err)
|
||||
return
|
||||
}
|
||||
+ // Added by ingress
|
||||
+ if cm.Namespace != podNs {
|
||||
+ return
|
||||
+ }
|
||||
+ // End added by ingress
|
||||
// This is a change to a configmap we don't watch, ignore it
|
||||
if cm.Name != dynamicCACertNamespaceConfigMap {
|
||||
return
|
||||
19
istio/1.12/patches/proxy/20230408-build-extensions.patch
Normal file
19
istio/1.12/patches/proxy/20230408-build-extensions.patch
Normal file
@@ -0,0 +1,19 @@
|
||||
diff -Naur proxy/common/scripts/run.sh proxy-new/common/scripts/run.sh
|
||||
--- proxy/common/scripts/run.sh 2023-04-08 21:12:05.896147208 +0800
|
||||
+++ proxy-new/common/scripts/run.sh 2023-04-08 20:33:51.935437889 +0800
|
||||
@@ -40,6 +40,7 @@
|
||||
MOUNT_ENVOY_SOURCE="${MOUNT_ENVOY_SOURCE:-`cd $MOUNT_SOURCE/../envoy;pwd`}"
|
||||
MOUNT_PACKAGE_SOURCE="${MOUNT_PACKAGE_SOURCE:-`cd $MOUNT_SOURCE/../package;pwd`}"
|
||||
MOUNT_ROOT_SOURCE="${MOUNT_ROOT_SOURCE:-`cd $MOUNT_SOURCE/..;pwd`}"
|
||||
+MOUNT_PLUGINS_SOURCE="${MOUNT_PLUGINS_SOURCE:-`cd $MOUNT_SOURCE/../../plugins/wasm-cpp;pwd`}"
|
||||
|
||||
read -ra DOCKER_RUN_OPTIONS <<< "${DOCKER_RUN_OPTIONS:-}"
|
||||
|
||||
@@ -64,6 +65,7 @@
|
||||
--mount "type=bind,source=${MOUNT_SOURCE},destination=/work" \
|
||||
--mount "type=bind,source=${MOUNT_ROOT_SOURCE}/..,destination=/parent" \
|
||||
--mount "type=bind,source=${MOUNT_ENVOY_SOURCE},destination=/envoy" \
|
||||
+ --mount "type=bind,source=${MOUNT_PLUGINS_SOURCE},destination=/wasm-cpp" \
|
||||
--mount "type=volume,source=go,destination=/go" \
|
||||
--mount "type=volume,source=gocache,destination=/gocache" \
|
||||
--mount "type=volume,source=cache,destination=/home/.cache" \
|
||||
65
pkg/cmd/hgctl/configBootstrap.go
Normal file
65
pkg/cmd/hgctl/configBootstrap.go
Normal file
@@ -0,0 +1,65 @@
|
||||
// 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"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||
)
|
||||
|
||||
func bootstrapConfigCmd() *cobra.Command {
|
||||
configCmd := &cobra.Command{
|
||||
Use: "bootstrap <pod-name>",
|
||||
Aliases: []string{"b"},
|
||||
Short: "Retrieves bootstrap Envoy xDS resources from the specified Higress Gateway Pod",
|
||||
Long: `Retrieves information about bootstrap Envoy xDS resources from the specified Higress Gateway Pod`,
|
||||
Example: ` # Retrieve summary about bootstrap configuration for a given pod from Envoy.
|
||||
hgctl gateway-config bootstrap <pod-name> -n <pod-namespace>
|
||||
|
||||
# Retrieve full configuration dump as YAML
|
||||
hgctl gateway-config bootstrap <pod-name> -n <pod-namespace> -o yaml
|
||||
|
||||
# Retrieve full configuration dump with short syntax
|
||||
hgctl gc b <pod-name> -n <pod-namespace>
|
||||
`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(runBootstrapConfig(c, args))
|
||||
},
|
||||
}
|
||||
|
||||
return configCmd
|
||||
}
|
||||
|
||||
func runBootstrapConfig(c *cobra.Command, args []string) error {
|
||||
configDump, err := retrieveConfigDump(args, false)
|
||||
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))
|
||||
return err
|
||||
}
|
||||
64
pkg/cmd/hgctl/configCluster.go
Normal file
64
pkg/cmd/hgctl/configCluster.go
Normal file
@@ -0,0 +1,64 @@
|
||||
// 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"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||
)
|
||||
|
||||
func clusterConfigCmd() *cobra.Command {
|
||||
configCmd := &cobra.Command{
|
||||
Use: "cluster <pod-name>",
|
||||
Short: "Retrieves cluster Envoy xDS resources from the specified Higress Gateway Pod",
|
||||
Aliases: []string{"c"},
|
||||
Long: `Retrieves information about cluster Envoy xDS resources from the specified Higress Gateway Pod`,
|
||||
Example: ` # Retrieve summary about cluster configuration for a given pod from Envoy.
|
||||
hgctl gateway-config cluster <pod-name> -n <pod-namespace>
|
||||
|
||||
# Retrieve full configuration dump as YAML
|
||||
hgctl gateway-config cluster <pod-name> -n <pod-namespace> -o yaml
|
||||
|
||||
# Retrieve full configuration dump with short syntax
|
||||
hgctl gc c <pod-name> -n <pod-namespace>
|
||||
`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(runClusterConfig(c, args))
|
||||
},
|
||||
}
|
||||
|
||||
return configCmd
|
||||
}
|
||||
|
||||
func runClusterConfig(c *cobra.Command, args []string) error {
|
||||
configDump, err := retrieveConfigDump(args, false)
|
||||
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))
|
||||
return err
|
||||
}
|
||||
79
pkg/cmd/hgctl/configCmd.go
Normal file
79
pkg/cmd/hgctl/configCmd.go
Normal file
@@ -0,0 +1,79 @@
|
||||
// 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"
|
||||
|
||||
"github.com/alibaba/higress/pkg/cmd/options"
|
||||
"github.com/spf13/cobra"
|
||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||
)
|
||||
|
||||
func newConfigCommand() *cobra.Command {
|
||||
cfgCommand := &cobra.Command{
|
||||
Use: "gateway-config",
|
||||
Aliases: []string{"gc"},
|
||||
Short: "Retrieve Higress Gateway configuration.",
|
||||
Long: "Retrieve information about Higress Gateway Configuration.",
|
||||
}
|
||||
|
||||
cfgCommand.AddCommand(allConfigCmd())
|
||||
cfgCommand.AddCommand(bootstrapConfigCmd())
|
||||
cfgCommand.AddCommand(clusterConfigCmd())
|
||||
cfgCommand.AddCommand(endpointConfigCmd())
|
||||
cfgCommand.AddCommand(listenerConfigCmd())
|
||||
cfgCommand.AddCommand(routeConfigCmd())
|
||||
|
||||
flags := cfgCommand.Flags()
|
||||
options.AddKubeConfigFlags(flags)
|
||||
|
||||
cfgCommand.PersistentFlags().StringVarP(&output, "output", "o", "json", "One of 'yaml' or 'json'")
|
||||
cfgCommand.PersistentFlags().StringVarP(&podNamespace, "namespace", "n", "higress-system", "Namespace where envoy proxy pod are installed.")
|
||||
|
||||
return cfgCommand
|
||||
}
|
||||
|
||||
func allConfigCmd() *cobra.Command {
|
||||
configCmd := &cobra.Command{
|
||||
Use: "all <pod-name>",
|
||||
Short: "Retrieves all Envoy xDS resources from the specified Higress Gateway Pod",
|
||||
Long: `Retrieves information about all Envoy xDS resources from the specified Higress Gateway Pod`,
|
||||
Example: ` # Retrieve summary about all configuration for a given pod from Envoy.
|
||||
hgctl gateway-config all <pod-name> -n <pod-namespace>
|
||||
|
||||
# Retrieve full configuration dump as YAML
|
||||
hgctl gateway-config all <pod-name> -n <pod-namespace> -o yaml
|
||||
|
||||
# Retrieve full configuration dump with short syntax
|
||||
hgctl gc all <pod-name> -n <pod-namespace>
|
||||
`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(runAllConfig(c, args))
|
||||
},
|
||||
}
|
||||
|
||||
return configCmd
|
||||
}
|
||||
|
||||
func runAllConfig(c *cobra.Command, args []string) error {
|
||||
configDump, err := retrieveConfigDump(args, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = fmt.Fprintln(c.OutOrStdout(), string(configDump))
|
||||
return err
|
||||
}
|
||||
65
pkg/cmd/hgctl/configEndpoint.go
Normal file
65
pkg/cmd/hgctl/configEndpoint.go
Normal file
@@ -0,0 +1,65 @@
|
||||
// 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"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||
)
|
||||
|
||||
func endpointConfigCmd() *cobra.Command {
|
||||
configCmd := &cobra.Command{
|
||||
Use: "endpoint <pod-name>",
|
||||
Short: "Retrieves endpoint Envoy xDS resources from the specified Higress Gateway Pod",
|
||||
Aliases: []string{"e"},
|
||||
Long: `Retrieves information about endpoint Envoy xDS resources from the specified Higress Gateway Pod`,
|
||||
Example: ` # Retrieve summary about endpoint configuration for a given pod from Envoy.
|
||||
hgctl gateway-config endpoint <pod-name> -n <pod-namespace>
|
||||
|
||||
# Retrieve configuration dump as YAML
|
||||
hgctl gateway-config endpoint <pod-name> -n <pod-namespace> -o yaml
|
||||
|
||||
# Retrieve configuration dump with short syntax
|
||||
hgctl gc e <pod-name> -n <pod-namespace>
|
||||
`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(runEndpointConfig(c, args))
|
||||
},
|
||||
}
|
||||
|
||||
return configCmd
|
||||
}
|
||||
|
||||
func runEndpointConfig(c *cobra.Command, args []string) error {
|
||||
configDump, err := retrieveConfigDump(args, 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))
|
||||
return err
|
||||
}
|
||||
65
pkg/cmd/hgctl/configListener.go
Normal file
65
pkg/cmd/hgctl/configListener.go
Normal file
@@ -0,0 +1,65 @@
|
||||
// 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"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||
)
|
||||
|
||||
func listenerConfigCmd() *cobra.Command {
|
||||
configCmd := &cobra.Command{
|
||||
Use: "listener <pod-name>",
|
||||
Aliases: []string{"l"},
|
||||
Short: "Retrieves listener Envoy xDS resources from the specified Higress Gateway Pod",
|
||||
Long: `Retrieves information about listener Envoy xDS resources from the specified Higress Gateway Pod`,
|
||||
Example: ` # Retrieve summary about listener configuration for a given pod from Envoy.
|
||||
hgctl gateway-config listener <pod-name> -n <pod-namespace>
|
||||
|
||||
# Retrieve full configuration dump as YAML
|
||||
hgctl gateway-config listener <pod-name> -n <pod-namespace> -o yaml
|
||||
|
||||
# Retrieve full configuration dump with short syntax
|
||||
hgctl gc l <pod-name> -n <pod-namespace>
|
||||
`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(runListenerConfig(c, args))
|
||||
},
|
||||
}
|
||||
|
||||
return configCmd
|
||||
}
|
||||
|
||||
func runListenerConfig(c *cobra.Command, args []string) error {
|
||||
configDump, err := retrieveConfigDump(args, false)
|
||||
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))
|
||||
return err
|
||||
}
|
||||
151
pkg/cmd/hgctl/configRetriever.go
Normal file
151
pkg/cmd/hgctl/configRetriever.go
Normal file
@@ -0,0 +1,151 @@
|
||||
// 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)
|
||||
}
|
||||
65
pkg/cmd/hgctl/configRoute.go
Normal file
65
pkg/cmd/hgctl/configRoute.go
Normal file
@@ -0,0 +1,65 @@
|
||||
// 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"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||
)
|
||||
|
||||
func routeConfigCmd() *cobra.Command {
|
||||
configCmd := &cobra.Command{
|
||||
Use: "route <pod-name>",
|
||||
Aliases: []string{"r"},
|
||||
Short: "Retrieves route Envoy xDS resources from the specified Higress Gateway Pod",
|
||||
Long: `Retrieves information about route Envoy xDS resources from the specified Higress Gateway Pod`,
|
||||
Example: ` # Retrieve summary about route configuration for a given pod from Envoy.
|
||||
hgctl gateway-config route <pod-name> -n <pod-namespace>
|
||||
|
||||
# Retrieve full configuration dump as YAML
|
||||
hgctl gateway-config route <pod-name> -n <pod-namespace> -o yaml
|
||||
|
||||
# Retrieve full configuration dump with short syntax
|
||||
hgctl gc r <pod-name> -n <pod-namespace>
|
||||
`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(runRouteConfig(c, args))
|
||||
},
|
||||
}
|
||||
|
||||
return configCmd
|
||||
}
|
||||
|
||||
func runRouteConfig(c *cobra.Command, args []string) error {
|
||||
configDump, err := retrieveConfigDump(args, false)
|
||||
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))
|
||||
return err
|
||||
}
|
||||
220
pkg/cmd/hgctl/config_test.go
Normal file
220
pkg/cmd/hgctl/config_test.go
Normal file
@@ -0,0 +1,220 @@
|
||||
// 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"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/kubernetes"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var _ kubernetes.PortForwarder = &fakePortForwarder{}
|
||||
|
||||
type fakePortForwarder struct {
|
||||
responseBody []byte
|
||||
localPort int
|
||||
l net.Listener
|
||||
mux *http.ServeMux
|
||||
}
|
||||
|
||||
func newFakePortForwarder(b []byte) (kubernetes.PortForwarder, error) {
|
||||
p, err := kubernetes.LocalAvailablePort()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fw := &fakePortForwarder{
|
||||
responseBody: b,
|
||||
localPort: p,
|
||||
mux: http.NewServeMux(),
|
||||
}
|
||||
fw.mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
_, _ = w.Write(fw.responseBody)
|
||||
})
|
||||
|
||||
return fw, nil
|
||||
}
|
||||
|
||||
func (fw *fakePortForwarder) Start() error {
|
||||
l, err := net.Listen("tcp", fw.Address())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fw.l = l
|
||||
|
||||
go func() {
|
||||
if err := http.Serve(l, fw.mux); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fw *fakePortForwarder) Stop() {}
|
||||
|
||||
func (fw *fakePortForwarder) Address() string {
|
||||
return fmt.Sprintf("localhost:%d", fw.localPort)
|
||||
}
|
||||
|
||||
func TestExtractAllConfigDump(t *testing.T) {
|
||||
input, err := readInputConfig("in.all.json")
|
||||
assert.NoError(t, err)
|
||||
fw, err := newFakePortForwarder(input)
|
||||
assert.NoError(t, err)
|
||||
err = fw.Start()
|
||||
assert.NoError(t, err)
|
||||
|
||||
cases := []struct {
|
||||
output string
|
||||
expected string
|
||||
resourceType string
|
||||
}{
|
||||
{
|
||||
output: "json",
|
||||
expected: "out.all.json",
|
||||
},
|
||||
{
|
||||
output: "yaml",
|
||||
expected: "out.all.yaml",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.output, func(t *testing.T) {
|
||||
configDump, err := fetchGatewayConfig(fw, true)
|
||||
assert.NoError(t, err)
|
||||
data, err := GetXDSResource(AllEnvoyConfigType, configDump)
|
||||
assert.NoError(t, err)
|
||||
got, err := formatGatewayConfig(data, tc.output)
|
||||
assert.NoError(t, err)
|
||||
out, err := readOutputConfig(tc.expected)
|
||||
assert.NoError(t, err)
|
||||
if tc.output == "yaml" {
|
||||
assert.YAMLEq(t, string(out), string(got))
|
||||
} else {
|
||||
assert.JSONEq(t, string(out), string(got))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fw.Stop()
|
||||
}
|
||||
|
||||
func TestExtractSubResourcesConfigDump(t *testing.T) {
|
||||
input, err := readInputConfig("in.all.json")
|
||||
assert.NoError(t, err)
|
||||
fw, err := newFakePortForwarder(input)
|
||||
assert.NoError(t, err)
|
||||
err = fw.Start()
|
||||
assert.NoError(t, err)
|
||||
|
||||
cases := []struct {
|
||||
output string
|
||||
expected string
|
||||
resourceType envoyConfigType
|
||||
}{
|
||||
{
|
||||
output: "json",
|
||||
resourceType: BootstrapEnvoyConfigType,
|
||||
expected: "out.bootstrap.json",
|
||||
},
|
||||
{
|
||||
output: "yaml",
|
||||
resourceType: BootstrapEnvoyConfigType,
|
||||
expected: "out.bootstrap.yaml",
|
||||
}, {
|
||||
output: "json",
|
||||
resourceType: ClusterEnvoyConfigType,
|
||||
expected: "out.cluster.json",
|
||||
},
|
||||
{
|
||||
output: "yaml",
|
||||
resourceType: ClusterEnvoyConfigType,
|
||||
expected: "out.cluster.yaml",
|
||||
}, {
|
||||
output: "json",
|
||||
resourceType: ListenerEnvoyConfigType,
|
||||
expected: "out.listener.json",
|
||||
},
|
||||
{
|
||||
output: "yaml",
|
||||
resourceType: ListenerEnvoyConfigType,
|
||||
expected: "out.listener.yaml",
|
||||
}, {
|
||||
output: "json",
|
||||
resourceType: RouteEnvoyConfigType,
|
||||
expected: "out.route.json",
|
||||
},
|
||||
{
|
||||
output: "yaml",
|
||||
resourceType: RouteEnvoyConfigType,
|
||||
expected: "out.route.yaml",
|
||||
},
|
||||
{
|
||||
output: "json",
|
||||
resourceType: EndpointEnvoyConfigType,
|
||||
expected: "out.endpoints.json",
|
||||
},
|
||||
{
|
||||
output: "yaml",
|
||||
resourceType: EndpointEnvoyConfigType,
|
||||
expected: "out.endpoints.yaml",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.output, func(t *testing.T) {
|
||||
configDump, err := fetchGatewayConfig(fw, false)
|
||||
assert.NoError(t, err)
|
||||
resource, err := GetXDSResource(tc.resourceType, configDump)
|
||||
assert.NoError(t, err)
|
||||
got, err := formatGatewayConfig(resource, tc.output)
|
||||
assert.NoError(t, err)
|
||||
out, err := readOutputConfig(tc.expected)
|
||||
assert.NoError(t, err)
|
||||
if tc.output == "yaml" {
|
||||
assert.YAMLEq(t, string(out), string(got))
|
||||
} else {
|
||||
assert.JSONEq(t, string(out), string(got))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fw.Stop()
|
||||
}
|
||||
|
||||
func readInputConfig(filename string) ([]byte, error) {
|
||||
b, err := os.ReadFile(path.Join("testdata", "config", "input", filename))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func readOutputConfig(filename string) ([]byte, error) {
|
||||
b, err := os.ReadFile(path.Join("testdata", "config", "output", filename))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
172
pkg/cmd/hgctl/kubernetes/client.go
Normal file
172
pkg/cmd/hgctl/kubernetes/client.go
Normal file
@@ -0,0 +1,172 @@
|
||||
// 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 (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"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"
|
||||
)
|
||||
|
||||
type CLIClient interface {
|
||||
// RESTConfig returns the Kubernetes rest.Config used to configure the clients.
|
||||
RESTConfig() *rest.Config
|
||||
|
||||
// Pod returns the pod for the given namespaced name.
|
||||
Pod(namespacedName types.NamespacedName) (*corev1.Pod, error)
|
||||
|
||||
// PodsForSelector finds pods matching selector.
|
||||
PodsForSelector(namespace string, labelSelectors ...string) (*corev1.PodList, error)
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
var _ CLIClient = &client{}
|
||||
|
||||
type client struct {
|
||||
config *rest.Config
|
||||
restClient *rest.RESTClient
|
||||
kube kubernetes.Interface
|
||||
}
|
||||
|
||||
func NewCLIClient(clientConfig clientcmd.ClientConfig) (CLIClient, error) {
|
||||
return newClientInternal(clientConfig)
|
||||
}
|
||||
|
||||
func newClientInternal(clientConfig clientcmd.ClientConfig) (*client, error) {
|
||||
var (
|
||||
c client
|
||||
err error
|
||||
)
|
||||
|
||||
c.config, err = clientConfig.ClientConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
setRestDefaults(c.config)
|
||||
|
||||
c.restClient, err = rest.RESTClientFor(c.config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.kube, err = kubernetes.NewForConfig(c.config)
|
||||
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
|
||||
}
|
||||
cpy := *c.config
|
||||
return &cpy
|
||||
}
|
||||
|
||||
func (c *client) PodsForSelector(namespace string, podSelectors ...string) (*corev1.PodList, error) {
|
||||
return c.kube.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{
|
||||
LabelSelector: strings.Join(podSelectors, ","),
|
||||
})
|
||||
}
|
||||
|
||||
func (c *client) Pod(namespacedName types.NamespacedName) (*corev1.Pod, error) {
|
||||
return c.kube.CoreV1().Pods(namespacedName.Namespace).Get(context.TODO(), namespacedName.Name, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
func (c *client) PodExec(namespacedName types.NamespacedName, container string, command string) (stdout string, stderr string, err error) {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if len(stderr) > 0 {
|
||||
err = fmt.Errorf("error exec into %s/%s container %s: %v\n%s",
|
||||
namespacedName.Namespace, namespacedName.Name, container, err, stderr)
|
||||
} else {
|
||||
err = fmt.Errorf("error exec into %s/%s container %s: %v",
|
||||
namespacedName.Namespace, namespacedName.Name, container, err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
req := c.restClient.Post().
|
||||
Resource("pods").
|
||||
Namespace(namespacedName.Namespace).
|
||||
Name(namespacedName.Name).
|
||||
SubResource("exec").
|
||||
Param("container", container).
|
||||
VersionedParams(&corev1.PodExecOptions{
|
||||
Container: container,
|
||||
Command: strings.Fields(command),
|
||||
Stdin: false,
|
||||
Stdout: true,
|
||||
Stderr: true,
|
||||
TTY: false,
|
||||
}, kubescheme.ParameterCodec)
|
||||
|
||||
exec, err := remotecommand.NewSPDYExecutor(c.config, "POST", req.URL())
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
var stdoutBuf, stderrBuf bytes.Buffer
|
||||
err = exec.Stream(remotecommand.StreamOptions{
|
||||
Stdin: nil,
|
||||
Stdout: &stdoutBuf,
|
||||
Stderr: &stderrBuf,
|
||||
Tty: false,
|
||||
})
|
||||
|
||||
stdout = stdoutBuf.String()
|
||||
stderr = stderrBuf.String()
|
||||
return
|
||||
}
|
||||
155
pkg/cmd/hgctl/kubernetes/port-forwarder.go
Normal file
155
pkg/cmd/hgctl/kubernetes/port-forwarder.go
Normal file
@@ -0,0 +1,155 @@
|
||||
// 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"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/portforward"
|
||||
"k8s.io/client-go/transport/spdy"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultLocalAddress = "localhost"
|
||||
)
|
||||
|
||||
func LocalAvailablePort() (int, error) {
|
||||
l, err := net.Listen("tcp", fmt.Sprintf("%s:0", DefaultLocalAddress))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return l.Addr().(*net.TCPAddr).Port, l.Close()
|
||||
}
|
||||
|
||||
type PortForwarder interface {
|
||||
Start() error
|
||||
|
||||
Stop()
|
||||
|
||||
// Address returns the address of the local forwarded address.
|
||||
Address() string
|
||||
}
|
||||
|
||||
var _ PortForwarder = &localForwarder{}
|
||||
|
||||
type localForwarder struct {
|
||||
types.NamespacedName
|
||||
CLIClient
|
||||
|
||||
localPort int
|
||||
podPort int
|
||||
|
||||
stopCh chan struct{}
|
||||
}
|
||||
|
||||
func NewLocalPortForwarder(client CLIClient, namespacedName types.NamespacedName, localPort, podPort int) (PortForwarder, error) {
|
||||
f := &localForwarder{
|
||||
stopCh: make(chan struct{}),
|
||||
CLIClient: client,
|
||||
NamespacedName: namespacedName,
|
||||
localPort: localPort,
|
||||
podPort: podPort,
|
||||
}
|
||||
if f.localPort == 0 {
|
||||
// get a random port
|
||||
p, err := LocalAvailablePort()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get a local available port")
|
||||
}
|
||||
f.localPort = p
|
||||
}
|
||||
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (f *localForwarder) Start() error {
|
||||
errCh := make(chan error, 1)
|
||||
readyCh := make(chan struct{}, 1)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-f.stopCh:
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
fw, err := f.buildKubernetesPortForwarder(readyCh)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
|
||||
if err := fw.ForwardPorts(); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
|
||||
readyCh = nil
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-errCh:
|
||||
return errors.Wrap(err, "failed to start port forwarder")
|
||||
case <-readyCh:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (f *localForwarder) buildKubernetesPortForwarder(readyCh chan struct{}) (*portforward.PortForwarder, error) {
|
||||
restClient, err := rest.RESTClientFor(f.RESTConfig())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req := restClient.Post().Resource("pods").Namespace(f.Namespace).Name(f.Name).SubResource("portforward")
|
||||
serverURL := req.URL()
|
||||
|
||||
roundTripper, upgrader, err := spdy.RoundTripperFor(f.RESTConfig())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failure creating roundtripper: %v", err)
|
||||
}
|
||||
|
||||
dialer := spdy.NewDialer(upgrader, &http.Client{Transport: roundTripper}, http.MethodPost, serverURL)
|
||||
fw, err := portforward.NewOnAddresses(dialer,
|
||||
[]string{DefaultLocalAddress},
|
||||
[]string{fmt.Sprintf("%d:%d", f.localPort, f.podPort)},
|
||||
f.stopCh,
|
||||
readyCh,
|
||||
io.Discard,
|
||||
os.Stderr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed establishing portforward: %v", err)
|
||||
}
|
||||
|
||||
return fw, nil
|
||||
}
|
||||
|
||||
func (f *localForwarder) Stop() {
|
||||
close(f.stopCh)
|
||||
}
|
||||
|
||||
func (f *localForwarder) Address() string {
|
||||
return fmt.Sprintf("%s:%d", DefaultLocalAddress, f.localPort)
|
||||
}
|
||||
33
pkg/cmd/hgctl/root.go
Normal file
33
pkg/cmd/hgctl/root.go
Normal file
@@ -0,0 +1,33 @@
|
||||
// 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 "github.com/spf13/cobra"
|
||||
|
||||
// GetRootCommand returns the root cobra command to be executed
|
||||
// by hgctl main.
|
||||
func GetRootCommand() *cobra.Command {
|
||||
rootCmd := &cobra.Command{
|
||||
Use: "hgctl",
|
||||
Long: "A command line utility for operating Higress",
|
||||
SilenceUsage: true,
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
||||
rootCmd.AddCommand(newVersionCommand())
|
||||
rootCmd.AddCommand(newConfigCommand())
|
||||
|
||||
return rootCmd
|
||||
}
|
||||
2247
pkg/cmd/hgctl/testdata/config/input/in.all.json
vendored
Normal file
2247
pkg/cmd/hgctl/testdata/config/input/in.all.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2247
pkg/cmd/hgctl/testdata/config/output/out.all.json
vendored
Normal file
2247
pkg/cmd/hgctl/testdata/config/output/out.all.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1305
pkg/cmd/hgctl/testdata/config/output/out.all.yaml
vendored
Normal file
1305
pkg/cmd/hgctl/testdata/config/output/out.all.yaml
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1966
pkg/cmd/hgctl/testdata/config/output/out.bootstrap.json
vendored
Normal file
1966
pkg/cmd/hgctl/testdata/config/output/out.bootstrap.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1121
pkg/cmd/hgctl/testdata/config/output/out.bootstrap.yaml
vendored
Normal file
1121
pkg/cmd/hgctl/testdata/config/output/out.bootstrap.yaml
vendored
Normal file
File diff suppressed because it is too large
Load Diff
98
pkg/cmd/hgctl/testdata/config/output/out.cluster.json
vendored
Normal file
98
pkg/cmd/hgctl/testdata/config/output/out.cluster.json
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.admin.v3.ClustersConfigDump",
|
||||
"version_info": "2",
|
||||
"static_clusters": [{
|
||||
"cluster": {
|
||||
"@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
|
||||
"name": "xds_cluster",
|
||||
"type": "STRICT_DNS",
|
||||
"connect_timeout": "1s",
|
||||
"transport_socket": {
|
||||
"name": "envoy.transport_sockets.tls",
|
||||
"typed_config": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext",
|
||||
"common_tls_context": {
|
||||
"tls_params": {
|
||||
"tls_maximum_protocol_version": "TLSv1_3"
|
||||
},
|
||||
"tls_certificate_sds_secret_configs": [{
|
||||
"name": "xds_certificate",
|
||||
"sds_config": {
|
||||
"resource_api_version": "V3",
|
||||
"path_config_source": {
|
||||
"path": "/sds/xds-certificate.json"
|
||||
}
|
||||
}
|
||||
}],
|
||||
"validation_context_sds_secret_config": {
|
||||
"name": "xds_trusted_ca",
|
||||
"sds_config": {
|
||||
"resource_api_version": "V3",
|
||||
"path_config_source": {
|
||||
"path": "/sds/xds-trusted-ca.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"load_assignment": {
|
||||
"cluster_name": "xds_cluster",
|
||||
"endpoints": [{
|
||||
"lb_endpoints": [{
|
||||
"endpoint": {
|
||||
"address": {
|
||||
"socket_address": {
|
||||
"address": "higress",
|
||||
"port_value": 18000
|
||||
}
|
||||
}
|
||||
}
|
||||
}]
|
||||
}]
|
||||
},
|
||||
"typed_extension_protocol_options": {
|
||||
"envoy.extensions.upstreams.http.v3.HttpProtocolOptions": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions",
|
||||
"explicit_http_config": {
|
||||
"http2_protocol_options": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"last_updated": "2023-02-23T09:05:23.436Z"
|
||||
}],
|
||||
"dynamic_active_clusters": [{
|
||||
"version_info": "2a0a1698a9d3e05b802047b0cd36b52a070afa49042e1ba267168c5265c7cabf",
|
||||
"cluster": {
|
||||
"@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
|
||||
"name": "default-backend-rule-0-match-0-www.example.com",
|
||||
"type": "STATIC",
|
||||
"connect_timeout": "5s",
|
||||
"dns_lookup_family": "V4_ONLY",
|
||||
"outlier_detection": {},
|
||||
"common_lb_config": {
|
||||
"locality_weighted_lb_config": {}
|
||||
},
|
||||
"load_assignment": {
|
||||
"cluster_name": "default-backend-rule-0-match-0-www.example.com",
|
||||
"endpoints": [{
|
||||
"locality": {},
|
||||
"lb_endpoints": [{
|
||||
"endpoint": {
|
||||
"address": {
|
||||
"socket_address": {
|
||||
"address": "0.0.0.0",
|
||||
"port_value": 3000
|
||||
}
|
||||
}
|
||||
},
|
||||
"load_balancing_weight": 1
|
||||
}],
|
||||
"load_balancing_weight": 1
|
||||
}]
|
||||
}
|
||||
},
|
||||
"last_updated": "2023-02-23T09:05:38.443Z"
|
||||
}]
|
||||
}
|
||||
67
pkg/cmd/hgctl/testdata/config/output/out.cluster.yaml
vendored
Normal file
67
pkg/cmd/hgctl/testdata/config/output/out.cluster.yaml
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
---
|
||||
"@type": type.googleapis.com/envoy.admin.v3.ClustersConfigDump
|
||||
version_info: '2'
|
||||
static_clusters:
|
||||
- cluster:
|
||||
"@type": type.googleapis.com/envoy.config.cluster.v3.Cluster
|
||||
name: xds_cluster
|
||||
type: STRICT_DNS
|
||||
connect_timeout: 1s
|
||||
transport_socket:
|
||||
name: envoy.transport_sockets.tls
|
||||
typed_config:
|
||||
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
|
||||
common_tls_context:
|
||||
tls_params:
|
||||
tls_maximum_protocol_version: TLSv1_3
|
||||
tls_certificate_sds_secret_configs:
|
||||
- name: xds_certificate
|
||||
sds_config:
|
||||
resource_api_version: V3
|
||||
path_config_source:
|
||||
path: "/sds/xds-certificate.json"
|
||||
validation_context_sds_secret_config:
|
||||
name: xds_trusted_ca
|
||||
sds_config:
|
||||
resource_api_version: V3
|
||||
path_config_source:
|
||||
path: "/sds/xds-trusted-ca.json"
|
||||
load_assignment:
|
||||
cluster_name: xds_cluster
|
||||
endpoints:
|
||||
- lb_endpoints:
|
||||
- endpoint:
|
||||
address:
|
||||
socket_address:
|
||||
address: higress
|
||||
port_value: 18000
|
||||
typed_extension_protocol_options:
|
||||
envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
|
||||
"@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
|
||||
explicit_http_config:
|
||||
http2_protocol_options: {}
|
||||
last_updated: '2023-02-23T09:05:23.436Z'
|
||||
dynamic_active_clusters:
|
||||
- version_info: 2a0a1698a9d3e05b802047b0cd36b52a070afa49042e1ba267168c5265c7cabf
|
||||
cluster:
|
||||
"@type": type.googleapis.com/envoy.config.cluster.v3.Cluster
|
||||
name: default-backend-rule-0-match-0-www.example.com
|
||||
type: STATIC
|
||||
connect_timeout: 5s
|
||||
dns_lookup_family: V4_ONLY
|
||||
outlier_detection: {}
|
||||
common_lb_config:
|
||||
locality_weighted_lb_config: {}
|
||||
load_assignment:
|
||||
cluster_name: default-backend-rule-0-match-0-www.example.com
|
||||
endpoints:
|
||||
- locality: {}
|
||||
lb_endpoints:
|
||||
- endpoint:
|
||||
address:
|
||||
socket_address:
|
||||
address: 0.0.0.0
|
||||
port_value: 3000
|
||||
load_balancing_weight: 1
|
||||
load_balancing_weight: 1
|
||||
last_updated: '2023-02-23T09:05:38.443Z'
|
||||
30
pkg/cmd/hgctl/testdata/config/output/out.endpoints.json
vendored
Normal file
30
pkg/cmd/hgctl/testdata/config/output/out.endpoints.json
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.admin.v3.EndpointsConfigDump",
|
||||
"staticEndpointConfigs": [{
|
||||
"endpointConfig": {
|
||||
"@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment",
|
||||
"clusterName": "xds_cluster",
|
||||
"endpoints": [{
|
||||
"locality": {},
|
||||
"lbEndpoints": [{
|
||||
"endpoint": {
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "0.0.0.0",
|
||||
"portValue": 18000
|
||||
}
|
||||
},
|
||||
"healthCheckConfig": {},
|
||||
"hostname": "higress"
|
||||
},
|
||||
"healthStatus": "HEALTHY",
|
||||
"metadata": {},
|
||||
"loadBalancingWeight": 1
|
||||
}]
|
||||
}],
|
||||
"policy": {
|
||||
"overprovisioningFactor": 140
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
21
pkg/cmd/hgctl/testdata/config/output/out.endpoints.yaml
vendored
Normal file
21
pkg/cmd/hgctl/testdata/config/output/out.endpoints.yaml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
"@type": type.googleapis.com/envoy.admin.v3.EndpointsConfigDump
|
||||
staticEndpointConfigs:
|
||||
- endpointConfig:
|
||||
"@type": type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment
|
||||
clusterName: xds_cluster
|
||||
endpoints:
|
||||
- locality: {}
|
||||
lbEndpoints:
|
||||
- endpoint:
|
||||
address:
|
||||
socketAddress:
|
||||
address: 0.0.0.0
|
||||
portValue: 18000
|
||||
healthCheckConfig: {}
|
||||
hostname: higress
|
||||
healthStatus: HEALTHY
|
||||
metadata: {}
|
||||
loadBalancingWeight: 1
|
||||
policy:
|
||||
overprovisioningFactor: 140
|
||||
77
pkg/cmd/hgctl/testdata/config/output/out.listener.json
vendored
Normal file
77
pkg/cmd/hgctl/testdata/config/output/out.listener.json
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.admin.v3.ListenersConfigDump",
|
||||
"version_info": "2",
|
||||
"dynamic_listeners": [{
|
||||
"name": "default-higress-http",
|
||||
"active_state": {
|
||||
"version_info": "42c71fb50c315ee3a32b327da69f8cc0baf420bc84b747e82d9c38e1b0c33eb2",
|
||||
"listener": {
|
||||
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||
"name": "default-higress-http",
|
||||
"address": {
|
||||
"socket_address": {
|
||||
"address": "0.0.0.0",
|
||||
"port_value": 10080
|
||||
}
|
||||
},
|
||||
"access_log": [{
|
||||
"name": "envoy.access_loggers.file",
|
||||
"filter": {
|
||||
"response_flag_filter": {
|
||||
"flags": [
|
||||
"NR"
|
||||
]
|
||||
}
|
||||
},
|
||||
"typed_config": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog",
|
||||
"path": "/dev/stdout"
|
||||
}
|
||||
}],
|
||||
"default_filter_chain": {
|
||||
"filters": [{
|
||||
"name": "envoy.filters.network.http_connection_manager",
|
||||
"typed_config": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
|
||||
"stat_prefix": "http",
|
||||
"rds": {
|
||||
"config_source": {
|
||||
"api_config_source": {
|
||||
"api_type": "DELTA_GRPC",
|
||||
"grpc_services": [{
|
||||
"envoy_grpc": {
|
||||
"cluster_name": "xds_cluster"
|
||||
}
|
||||
}],
|
||||
"set_node_on_first_message_only": true,
|
||||
"transport_api_version": "V3"
|
||||
},
|
||||
"resource_api_version": "V3"
|
||||
},
|
||||
"route_config_name": "default-higress-http"
|
||||
},
|
||||
"http_filters": [{
|
||||
"name": "envoy.filters.http.router",
|
||||
"typed_config": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router"
|
||||
}
|
||||
}],
|
||||
"access_log": [{
|
||||
"name": "envoy.access_loggers.file",
|
||||
"typed_config": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog",
|
||||
"path": "/dev/stdout"
|
||||
}
|
||||
}],
|
||||
"use_remote_address": true,
|
||||
"upgrade_configs": [{
|
||||
"upgrade_type": "websocket"
|
||||
}]
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
"last_updated": "2023-02-23T09:05:38.446Z"
|
||||
}
|
||||
}]
|
||||
}
|
||||
53
pkg/cmd/hgctl/testdata/config/output/out.listener.yaml
vendored
Normal file
53
pkg/cmd/hgctl/testdata/config/output/out.listener.yaml
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
---
|
||||
"@type": type.googleapis.com/envoy.admin.v3.ListenersConfigDump
|
||||
version_info: '2'
|
||||
dynamic_listeners:
|
||||
- name: default-higress-http
|
||||
active_state:
|
||||
version_info: 42c71fb50c315ee3a32b327da69f8cc0baf420bc84b747e82d9c38e1b0c33eb2
|
||||
listener:
|
||||
"@type": type.googleapis.com/envoy.config.listener.v3.Listener
|
||||
name: default-higress-http
|
||||
address:
|
||||
socket_address:
|
||||
address: 0.0.0.0
|
||||
port_value: 10080
|
||||
access_log:
|
||||
- name: envoy.access_loggers.file
|
||||
filter:
|
||||
response_flag_filter:
|
||||
flags:
|
||||
- NR
|
||||
typed_config:
|
||||
"@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog
|
||||
path: "/dev/stdout"
|
||||
default_filter_chain:
|
||||
filters:
|
||||
- name: envoy.filters.network.http_connection_manager
|
||||
typed_config:
|
||||
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
|
||||
stat_prefix: http
|
||||
rds:
|
||||
config_source:
|
||||
api_config_source:
|
||||
api_type: DELTA_GRPC
|
||||
grpc_services:
|
||||
- envoy_grpc:
|
||||
cluster_name: xds_cluster
|
||||
set_node_on_first_message_only: true
|
||||
transport_api_version: V3
|
||||
resource_api_version: V3
|
||||
route_config_name: default-higress-http
|
||||
http_filters:
|
||||
- name: envoy.filters.http.router
|
||||
typed_config:
|
||||
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
|
||||
access_log:
|
||||
- name: envoy.access_loggers.file
|
||||
typed_config:
|
||||
"@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog
|
||||
path: "/dev/stdout"
|
||||
use_remote_address: true
|
||||
upgrade_configs:
|
||||
- upgrade_type: websocket
|
||||
last_updated: '2023-02-23T09:05:38.446Z'
|
||||
31
pkg/cmd/hgctl/testdata/config/output/out.route.json
vendored
Normal file
31
pkg/cmd/hgctl/testdata/config/output/out.route.json
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.admin.v3.RoutesConfigDump",
|
||||
"dynamic_route_configs": [{
|
||||
"version_info": "cb1e51997a9c3aa6f4d920f39fd5bdbd966e9382b7b6bdf42efca8c22c6c3442",
|
||||
"route_config": {
|
||||
"@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration",
|
||||
"name": "default-higress-http",
|
||||
"virtual_hosts": [{
|
||||
"name": "default-higress-http",
|
||||
"domains": [
|
||||
"*"
|
||||
],
|
||||
"routes": [{
|
||||
"match": {
|
||||
"prefix": "/",
|
||||
"headers": [{
|
||||
"name": ":authority",
|
||||
"string_match": {
|
||||
"exact": "www.example.com"
|
||||
}
|
||||
}]
|
||||
},
|
||||
"route": {
|
||||
"cluster": "default-backend-rule-0-match-0-www.example.com"
|
||||
}
|
||||
}]
|
||||
}]
|
||||
},
|
||||
"last_updated": "2023-02-23T09:05:38.448Z"
|
||||
}]
|
||||
}
|
||||
21
pkg/cmd/hgctl/testdata/config/output/out.route.yaml
vendored
Normal file
21
pkg/cmd/hgctl/testdata/config/output/out.route.yaml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
"@type": type.googleapis.com/envoy.admin.v3.RoutesConfigDump
|
||||
dynamic_route_configs:
|
||||
- version_info: cb1e51997a9c3aa6f4d920f39fd5bdbd966e9382b7b6bdf42efca8c22c6c3442
|
||||
route_config:
|
||||
"@type": type.googleapis.com/envoy.config.route.v3.RouteConfiguration
|
||||
name: default-higress-http
|
||||
virtual_hosts:
|
||||
- name: default-higress-http
|
||||
domains:
|
||||
- "*"
|
||||
routes:
|
||||
- match:
|
||||
prefix: "/"
|
||||
headers:
|
||||
- name: ":authority"
|
||||
string_match:
|
||||
exact: www.example.com
|
||||
route:
|
||||
cluster: default-backend-rule-0-match-0-www.example.com
|
||||
last_updated: '2023-02-23T09:05:38.448Z'
|
||||
81
pkg/cmd/hgctl/utils.go
Normal file
81
pkg/cmd/hgctl/utils.go
Normal file
@@ -0,0 +1,81 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
type envoyConfigType string
|
||||
|
||||
var (
|
||||
BootstrapEnvoyConfigType envoyConfigType = "bootstrap"
|
||||
ClusterEnvoyConfigType envoyConfigType = "cluster"
|
||||
EndpointEnvoyConfigType envoyConfigType = "endpoint"
|
||||
ListenerEnvoyConfigType envoyConfigType = "listener"
|
||||
RouteEnvoyConfigType envoyConfigType = "route"
|
||||
AllEnvoyConfigType envoyConfigType = "all"
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
||||
193
pkg/cmd/hgctl/version.go
Normal file
193
pkg/cmd/hgctl/version.go
Normal file
@@ -0,0 +1,193 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package hgctl
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/kubernetes"
|
||||
"github.com/alibaba/higress/pkg/cmd/options"
|
||||
"github.com/alibaba/higress/pkg/cmd/version"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"gopkg.in/yaml.v2"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||
)
|
||||
|
||||
const (
|
||||
yamlOutput = "yaml"
|
||||
jsonOutput = "json"
|
||||
higressCoreContainerName = "higress-core"
|
||||
higressGatewayContainerName = "higress-gateway"
|
||||
)
|
||||
|
||||
func newVersionCommand() *cobra.Command {
|
||||
var (
|
||||
output string
|
||||
client bool
|
||||
)
|
||||
|
||||
versionCommand := &cobra.Command{
|
||||
Use: "version",
|
||||
Aliases: []string{"versions", "v"},
|
||||
Short: "Show version",
|
||||
Example: ` # Show versions of both client and server.
|
||||
hgctl version
|
||||
|
||||
# Show versions of both client and server in JSON format.
|
||||
hgctl version --output=json
|
||||
|
||||
# Show version of client without server.
|
||||
hgctl version --client
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(versions(cmd.OutOrStdout(), output, client))
|
||||
},
|
||||
}
|
||||
|
||||
flags := versionCommand.Flags()
|
||||
options.AddKubeConfigFlags(flags)
|
||||
|
||||
versionCommand.PersistentFlags().StringVarP(&output, "output", "o", yamlOutput, "One of 'yaml' or 'json'")
|
||||
|
||||
versionCommand.PersistentFlags().BoolVarP(&client, "client", "r", false, "If true, only log client version.")
|
||||
|
||||
return versionCommand
|
||||
}
|
||||
|
||||
type VersionInfo struct {
|
||||
ClientVersion string `json:"client" yaml:"client"`
|
||||
ServerVersions []*ServerVersion `json:"server,omitempty" yaml:"server"`
|
||||
}
|
||||
|
||||
type ServerVersion struct {
|
||||
types.NamespacedName `yaml:"namespacedName"`
|
||||
version.Info `yaml:"versionInfo"`
|
||||
}
|
||||
|
||||
func Get() VersionInfo {
|
||||
return VersionInfo{
|
||||
ClientVersion: version.Get().HigressVersion,
|
||||
ServerVersions: make([]*ServerVersion, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func retrieveVersion(w io.Writer, v *VersionInfo, containerName string, cmd string, labelSelector string, c kubernetes.CLIClient, f versionFunc) error {
|
||||
pods, err := c.PodsForSelector(metav1.NamespaceAll, labelSelector)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "list Higress pods failed")
|
||||
}
|
||||
|
||||
for _, pod := range pods.Items {
|
||||
if pod.Status.Phase != v1.PodRunning {
|
||||
|
||||
fmt.Fprintf(w, "WARN: pod %s/%s is not running, skipping it.", pod.Namespace, pod.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
nn := types.NamespacedName{
|
||||
Namespace: pod.Namespace,
|
||||
Name: pod.Name,
|
||||
}
|
||||
stdout, _, err := c.PodExec(nn, containerName, cmd)
|
||||
if err != nil {
|
||||
return fmt.Errorf("pod exec on %s/%s failed: %w", nn.Namespace, nn.Name, err)
|
||||
}
|
||||
|
||||
info, err := f(stdout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v.ServerVersions = append(v.ServerVersions, &ServerVersion{
|
||||
NamespacedName: nn,
|
||||
Info: *info,
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type versionFunc func(string) (*version.Info, error)
|
||||
|
||||
func versions(w io.Writer, output string, client bool) error {
|
||||
v := Get()
|
||||
|
||||
if client {
|
||||
fmt.Fprintf(w, "clientVersion: %s", v.ClientVersion)
|
||||
return nil
|
||||
}
|
||||
|
||||
c, err := kubernetes.NewCLIClient(options.DefaultConfigFlags.ToRawKubeConfigLoader())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to build kubernetes client: %w", err)
|
||||
}
|
||||
|
||||
if err := retrieveVersion(w, &v, higressCoreContainerName, "higress version -ojson", "app=higress-controller", c, func(s string) (*version.Info, error) {
|
||||
info := &version.Info{}
|
||||
if err := json.Unmarshal([]byte(s), info); err != nil {
|
||||
return nil, fmt.Errorf("unmarshall pod exec result failed: %w", err)
|
||||
}
|
||||
info.Type = "higress-controller"
|
||||
return info, nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := retrieveVersion(w, &v, higressGatewayContainerName, "envoy --version", "app=higress-gateway", c, func(s string) (*version.Info, error) {
|
||||
if len(strings.Split(s, ":")) != 2 {
|
||||
return nil, nil
|
||||
}
|
||||
proxyVersion := strings.TrimSpace(strings.Split(s, ":")[1])
|
||||
return &version.Info{
|
||||
GatewayVersion: proxyVersion,
|
||||
Type: "higress-gateway",
|
||||
}, nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sort.Slice(v.ServerVersions, func(i, j int) bool {
|
||||
if v.ServerVersions[i].Namespace == v.ServerVersions[j].Namespace {
|
||||
return v.ServerVersions[i].Name < v.ServerVersions[j].Name
|
||||
}
|
||||
|
||||
return v.ServerVersions[i].Namespace < v.ServerVersions[j].Namespace
|
||||
})
|
||||
|
||||
var out []byte
|
||||
switch output {
|
||||
case yamlOutput:
|
||||
out, err = yaml.Marshal(v)
|
||||
case jsonOutput:
|
||||
out, err = json.MarshalIndent(v, "", " ")
|
||||
default:
|
||||
out, err = json.MarshalIndent(v, "", " ")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(w, string(out))
|
||||
|
||||
return nil
|
||||
}
|
||||
29
pkg/cmd/options/global.go
Normal file
29
pkg/cmd/options/global.go
Normal file
@@ -0,0 +1,29 @@
|
||||
// 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 options
|
||||
|
||||
import (
|
||||
"github.com/spf13/pflag"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
)
|
||||
|
||||
var DefaultConfigFlags = genericclioptions.NewConfigFlags(true)
|
||||
|
||||
func AddKubeConfigFlags(flags *pflag.FlagSet) {
|
||||
flags.StringVar(DefaultConfigFlags.KubeConfig, "kubeconfig", *DefaultConfigFlags.KubeConfig,
|
||||
"Path to the kubeconfig file to use for CLI requests.")
|
||||
flags.StringVar(DefaultConfigFlags.Context, "context", *DefaultConfigFlags.Context,
|
||||
"The name of the kubeconfig context to use.")
|
||||
}
|
||||
32
pkg/cmd/root.go
Normal file
32
pkg/cmd/root.go
Normal file
@@ -0,0 +1,32 @@
|
||||
// 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 cmd
|
||||
|
||||
import "github.com/spf13/cobra"
|
||||
|
||||
// GetRootCommand returns the root cobra command to be executed
|
||||
// by main.
|
||||
func GetRootCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "higress",
|
||||
Short: "Higress",
|
||||
Long: "Next-generation Cloud Native Gateway",
|
||||
}
|
||||
|
||||
cmd.AddCommand(getServerCommand())
|
||||
cmd.AddCommand(getVersionCommand())
|
||||
|
||||
return cmd
|
||||
}
|
||||
120
pkg/cmd/server.go
Normal file
120
pkg/cmd/server.go
Normal file
@@ -0,0 +1,120 @@
|
||||
// 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 cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/alibaba/higress/pkg/bootstrap"
|
||||
innerconstants "github.com/alibaba/higress/pkg/config/constants"
|
||||
"github.com/spf13/cobra"
|
||||
"istio.io/istio/pilot/pkg/features"
|
||||
"istio.io/istio/pkg/cmd"
|
||||
"istio.io/istio/pkg/config/constants"
|
||||
"istio.io/istio/pkg/keepalive"
|
||||
"istio.io/pkg/log"
|
||||
)
|
||||
|
||||
var (
|
||||
serverArgs *bootstrap.ServerArgs
|
||||
loggingOptions = log.DefaultOptions()
|
||||
|
||||
serverProvider = func(args *bootstrap.ServerArgs) (bootstrap.ServerInterface, error) {
|
||||
return bootstrap.NewServer(serverArgs)
|
||||
}
|
||||
|
||||
waitForMonitorSignal = func(stop chan struct{}) {
|
||||
cmd.WaitSignal(stop)
|
||||
}
|
||||
)
|
||||
|
||||
// getServerCommand returns the server cobra command to be executed.
|
||||
func getServerCommand() *cobra.Command {
|
||||
serveCmd := &cobra.Command{
|
||||
Use: "serve",
|
||||
Aliases: []string{"serve"},
|
||||
Short: "Starts the higress ingress controller",
|
||||
Example: "higress serve",
|
||||
PreRunE: func(c *cobra.Command, args []string) error {
|
||||
return log.Configure(loggingOptions)
|
||||
},
|
||||
RunE: func(c *cobra.Command, args []string) error {
|
||||
cmd.PrintFlags(c.Flags())
|
||||
|
||||
stop := make(chan struct{})
|
||||
|
||||
server, err := serverProvider(serverArgs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("fail to create higress server: %v", err)
|
||||
}
|
||||
|
||||
if err := server.Start(stop); err != nil {
|
||||
return fmt.Errorf("fail to start higress server: %v", err)
|
||||
}
|
||||
|
||||
waitForMonitorSignal(stop)
|
||||
|
||||
server.WaitUntilCompletion()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
serverArgs = &bootstrap.ServerArgs{
|
||||
Debug: true,
|
||||
NativeIstio: true,
|
||||
HttpAddress: ":8888",
|
||||
GrpcAddress: ":15051",
|
||||
GrpcKeepAliveOptions: keepalive.DefaultOption(),
|
||||
XdsOptions: bootstrap.XdsOptions{
|
||||
DebounceAfter: features.DebounceAfter,
|
||||
DebounceMax: features.DebounceMax,
|
||||
EnableEDSDebounce: features.EnableEDSDebounce,
|
||||
},
|
||||
}
|
||||
|
||||
serveCmd.PersistentFlags().StringVar(&serverArgs.GatewaySelectorKey, "gatewaySelectorKey", "higress", "gateway resource selector label key")
|
||||
serveCmd.PersistentFlags().StringVar(&serverArgs.GatewaySelectorValue, "gatewaySelectorValue", "higress-gateway", "gateway resource selector label value")
|
||||
serveCmd.PersistentFlags().BoolVar(&serverArgs.EnableStatus, "enableStatus", true, "enable the ingress status syncer which use to update the ip in ingress's status")
|
||||
serveCmd.PersistentFlags().StringVar(&serverArgs.IngressClass, "ingressClass", innerconstants.DefaultIngressClass, "if not empty, only watch the ingresses have the specified class, otherwise watch all ingresses")
|
||||
serveCmd.PersistentFlags().StringVar(&serverArgs.WatchNamespace, "watchNamespace", "", "if not empty, only wath the ingresses in the specified namespace, otherwise watch in all namespacees")
|
||||
serveCmd.PersistentFlags().BoolVar(&serverArgs.Debug, "debug", serverArgs.Debug, "if true, enables more debug http api")
|
||||
serveCmd.PersistentFlags().StringVar(&serverArgs.HttpAddress, "httpAddress", serverArgs.HttpAddress, "the http address")
|
||||
serveCmd.PersistentFlags().StringVar(&serverArgs.GrpcAddress, "grpcAddress", serverArgs.GrpcAddress, "the grpc address")
|
||||
serveCmd.PersistentFlags().BoolVar(&serverArgs.KeepStaleWhenEmpty, "keepStaleWhenEmpty", false, "keep the stale service entry when there are no endpoints in the service")
|
||||
serveCmd.PersistentFlags().StringVar(&serverArgs.RegistryOptions.ClusterRegistriesNamespace, "clusterRegistriesNamespace",
|
||||
serverArgs.RegistryOptions.ClusterRegistriesNamespace, "Namespace for ConfigMap which stores clusters configs")
|
||||
serveCmd.PersistentFlags().StringVar(&serverArgs.RegistryOptions.KubeConfig, "kubeconfig", "",
|
||||
"Use a Kubernetes configuration file instead of in-cluster configuration")
|
||||
// RegistryOptions Controller options
|
||||
serveCmd.PersistentFlags().DurationVar(&serverArgs.RegistryOptions.KubeOptions.ResyncPeriod, "resync", 60*time.Second,
|
||||
"Controller resync interval")
|
||||
serveCmd.PersistentFlags().StringVar(&serverArgs.RegistryOptions.KubeOptions.DomainSuffix, "domain", constants.DefaultKubernetesDomain,
|
||||
"DNS domain suffix")
|
||||
serveCmd.PersistentFlags().StringVar((*string)(&serverArgs.RegistryOptions.KubeOptions.ClusterID), "clusterID", "Kubernetes",
|
||||
"The ID of the cluster that this instance resides")
|
||||
serveCmd.PersistentFlags().StringToStringVar(&serverArgs.RegistryOptions.KubeOptions.ClusterAliases, "clusterAliases", map[string]string{},
|
||||
"Alias names for clusters")
|
||||
serveCmd.PersistentFlags().Float32Var(&serverArgs.RegistryOptions.KubeOptions.KubernetesAPIQPS, "kubernetesApiQPS", 80.0,
|
||||
"Maximum QPS when communicating with the kubernetes API")
|
||||
|
||||
serveCmd.PersistentFlags().IntVar(&serverArgs.RegistryOptions.KubeOptions.KubernetesAPIBurst, "kubernetesApiBurst", 160,
|
||||
"Maximum burst for throttle when communicating with the kubernetes API")
|
||||
|
||||
loggingOptions.AttachCobraFlags(serveCmd)
|
||||
serverArgs.GrpcKeepAliveOptions.AttachCobraFlags(serveCmd)
|
||||
|
||||
return serveCmd
|
||||
}
|
||||
@@ -12,18 +12,20 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/alibaba/higress/pkg/bootstrap"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/alibaba/higress/pkg/bootstrap"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestServe(t *testing.T) {
|
||||
serveCmd := getServerCommand()
|
||||
runEBackup := serveCmd.RunE
|
||||
argsBackup := os.Args
|
||||
serverProviderBackup := serverProvider
|
||||
@@ -53,7 +55,9 @@ func TestServe(t *testing.T) {
|
||||
time.Sleep(delay)
|
||||
close(stop)
|
||||
}
|
||||
main()
|
||||
|
||||
serveCmd.Execute()
|
||||
|
||||
end := time.Now()
|
||||
|
||||
cost := end.Sub(start)
|
||||
38
pkg/cmd/version.go
Normal file
38
pkg/cmd/version.go
Normal file
@@ -0,0 +1,38 @@
|
||||
// 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 cmd
|
||||
|
||||
import (
|
||||
"github.com/alibaba/higress/pkg/cmd/version"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// getVersionCommand returns the version cobra command to be executed.
|
||||
func getVersionCommand() *cobra.Command {
|
||||
var output string
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "version",
|
||||
Aliases: []string{"versions", "v"},
|
||||
Short: "Show versions",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return version.Print(cmd.OutOrStdout(), output)
|
||||
},
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().StringVarP(&output, "output", "o", "", "One of 'yaml' or 'json'")
|
||||
|
||||
return cmd
|
||||
}
|
||||
62
pkg/cmd/version/version.go
Normal file
62
pkg/cmd/version/version.go
Normal file
@@ -0,0 +1,62 @@
|
||||
// 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 version
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
type Info struct {
|
||||
Type string `json:"type,omitempty" yaml:"type,omitempty"`
|
||||
HigressVersion string `json:"higressVersion,omitempty" yaml:"higressVersion,omitempty"`
|
||||
GitCommitID string `json:"gitCommitID,omitempty" yaml:"gitCommitID,omitempty"`
|
||||
GatewayVersion string `json:"gatewayVersion,omitempty" yaml:"gatewayVersion,omitempty"`
|
||||
}
|
||||
|
||||
func Get() Info {
|
||||
return Info{
|
||||
HigressVersion: higressVersion,
|
||||
GitCommitID: gitCommitID,
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
higressVersion string
|
||||
gitCommitID string
|
||||
)
|
||||
|
||||
// Print shows the versions of the Envoy Gateway.
|
||||
func Print(w io.Writer, format string) error {
|
||||
v := Get()
|
||||
switch format {
|
||||
case "json":
|
||||
if marshalled, err := json.MarshalIndent(v, "", " "); err == nil {
|
||||
_, _ = fmt.Fprintln(w, string(marshalled))
|
||||
}
|
||||
case "yaml":
|
||||
if marshalled, err := yaml.Marshal(v); err == nil {
|
||||
_, _ = fmt.Fprintln(w, string(marshalled))
|
||||
}
|
||||
default:
|
||||
_, _ = fmt.Fprintf(w, "HIGRESS_VERSION: %s\n", v.HigressVersion)
|
||||
_, _ = fmt.Fprintf(w, "GIT_COMMIT_ID: %s\n", v.GitCommitID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -559,27 +559,36 @@ func (m *IngressConfig) convertDestinationRule(configs []common.WrapperConfig) [
|
||||
IngressLog.Debugf("traffic policy number %d", len(convertOptions.Service2TrafficPolicy))
|
||||
|
||||
for _, wrapperTrafficPolicy := range convertOptions.Service2TrafficPolicy {
|
||||
m.annotationHandler.ApplyTrafficPolicy(wrapperTrafficPolicy.TrafficPolicy, wrapperTrafficPolicy.WrapperConfig.AnnotationsConfig)
|
||||
m.annotationHandler.ApplyTrafficPolicy(wrapperTrafficPolicy.TrafficPolicy, wrapperTrafficPolicy.PortTrafficPolicy, wrapperTrafficPolicy.WrapperConfig.AnnotationsConfig)
|
||||
}
|
||||
|
||||
// Merge multi-port traffic policy per service into one destination rule.
|
||||
destinationRules := map[string]*common.WrapperDestinationRule{}
|
||||
for key, wrapperTrafficPolicy := range convertOptions.Service2TrafficPolicy {
|
||||
serviceName := util.CreateServiceFQDN(key.Namespace, key.Name)
|
||||
var serviceName string
|
||||
if key.ServiceFQDN != "" {
|
||||
serviceName = key.ServiceFQDN
|
||||
} else {
|
||||
serviceName = util.CreateServiceFQDN(key.Namespace, key.Name)
|
||||
}
|
||||
dr, exist := destinationRules[serviceName]
|
||||
if !exist {
|
||||
trafficPolicy := &networking.TrafficPolicy{}
|
||||
if wrapperTrafficPolicy.PortTrafficPolicy != nil {
|
||||
trafficPolicy.PortLevelSettings = []*networking.TrafficPolicy_PortTrafficPolicy{wrapperTrafficPolicy.PortTrafficPolicy}
|
||||
} else if wrapperTrafficPolicy.TrafficPolicy != nil {
|
||||
trafficPolicy = wrapperTrafficPolicy.TrafficPolicy
|
||||
}
|
||||
dr = &common.WrapperDestinationRule{
|
||||
DestinationRule: &networking.DestinationRule{
|
||||
Host: serviceName,
|
||||
TrafficPolicy: &networking.TrafficPolicy{
|
||||
PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{wrapperTrafficPolicy.TrafficPolicy},
|
||||
},
|
||||
Host: serviceName,
|
||||
TrafficPolicy: trafficPolicy,
|
||||
},
|
||||
WrapperConfig: wrapperTrafficPolicy.WrapperConfig,
|
||||
ServiceKey: key,
|
||||
}
|
||||
} else {
|
||||
dr.DestinationRule.TrafficPolicy.PortLevelSettings = append(dr.DestinationRule.TrafficPolicy.PortLevelSettings, wrapperTrafficPolicy.TrafficPolicy)
|
||||
} else if wrapperTrafficPolicy.PortTrafficPolicy != nil {
|
||||
dr.DestinationRule.TrafficPolicy.PortLevelSettings = append(dr.DestinationRule.TrafficPolicy.PortLevelSettings, wrapperTrafficPolicy.PortTrafficPolicy)
|
||||
}
|
||||
|
||||
destinationRules[serviceName] = dr
|
||||
|
||||
@@ -192,8 +192,8 @@ func (h *AnnotationHandlerManager) ApplyRoute(route *networking.HTTPRoute, confi
|
||||
}
|
||||
}
|
||||
|
||||
func (h *AnnotationHandlerManager) ApplyTrafficPolicy(trafficPolicy *networking.TrafficPolicy_PortTrafficPolicy, config *Ingress) {
|
||||
func (h *AnnotationHandlerManager) ApplyTrafficPolicy(trafficPolicy *networking.TrafficPolicy, portTrafficPolicy *networking.TrafficPolicy_PortTrafficPolicy, config *Ingress) {
|
||||
for _, handler := range h.trafficPolicyHandlers {
|
||||
handler.ApplyTrafficPolicy(trafficPolicy, config)
|
||||
handler.ApplyTrafficPolicy(trafficPolicy, portTrafficPolicy, config)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,6 +135,9 @@ func convertCredentials(secretType authSecretType, secret *corev1.Secret) ([]str
|
||||
}
|
||||
userList := strings.Split(string(users), "\n")
|
||||
for _, item := range userList {
|
||||
if !strings.Contains(item, ":") {
|
||||
continue
|
||||
}
|
||||
result = append(result, item)
|
||||
}
|
||||
case authMapAuthSecretType:
|
||||
|
||||
@@ -134,37 +134,41 @@ func ApplyByHeader(canary, route *networking.HTTPRoute, canaryIngress *Ingress)
|
||||
|
||||
// Modified match base on by header
|
||||
if canaryConfig.Header != "" {
|
||||
canary.Match[0].Headers = map[string]*networking.StringMatch{
|
||||
canaryConfig.Header: {
|
||||
MatchType: &networking.StringMatch_Exact{
|
||||
Exact: "always",
|
||||
},
|
||||
},
|
||||
}
|
||||
if canaryConfig.HeaderValue != "" {
|
||||
canary.Match[0].Headers = map[string]*networking.StringMatch{
|
||||
for _, match := range canary.Match {
|
||||
match.Headers = map[string]*networking.StringMatch{
|
||||
canaryConfig.Header: {
|
||||
MatchType: &networking.StringMatch_Regex{
|
||||
Regex: "always|" + canaryConfig.HeaderValue,
|
||||
MatchType: &networking.StringMatch_Exact{
|
||||
Exact: "always",
|
||||
},
|
||||
},
|
||||
}
|
||||
} else if canaryConfig.HeaderPattern != "" {
|
||||
canary.Match[0].Headers = map[string]*networking.StringMatch{
|
||||
canaryConfig.Header: {
|
||||
MatchType: &networking.StringMatch_Regex{
|
||||
Regex: canaryConfig.HeaderPattern,
|
||||
if canaryConfig.HeaderValue != "" {
|
||||
match.Headers = map[string]*networking.StringMatch{
|
||||
canaryConfig.Header: {
|
||||
MatchType: &networking.StringMatch_Regex{
|
||||
Regex: "always|" + canaryConfig.HeaderValue,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
} else if canaryConfig.HeaderPattern != "" {
|
||||
match.Headers = map[string]*networking.StringMatch{
|
||||
canaryConfig.Header: {
|
||||
MatchType: &networking.StringMatch_Regex{
|
||||
Regex: canaryConfig.HeaderPattern,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if canaryConfig.Cookie != "" {
|
||||
canary.Match[0].Headers = map[string]*networking.StringMatch{
|
||||
"cookie": {
|
||||
MatchType: &networking.StringMatch_Regex{
|
||||
Regex: "^(.\\*?;)?(" + canaryConfig.Cookie + "=always)(;.\\*)?$",
|
||||
for _, match := range canary.Match {
|
||||
match.Headers = map[string]*networking.StringMatch{
|
||||
"cookie": {
|
||||
MatchType: &networking.StringMatch_Regex{
|
||||
Regex: "^(.\\*?;)?(" + canaryConfig.Cookie + "=always)(;.\\*)?$",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,5 +38,5 @@ type RouteHandler interface {
|
||||
|
||||
type TrafficPolicyHandler interface {
|
||||
// ApplyTrafficPolicy parsed ingress annotation config reflected on traffic policy
|
||||
ApplyTrafficPolicy(trafficPolicy *networking.TrafficPolicy_PortTrafficPolicy, config *Ingress)
|
||||
ApplyTrafficPolicy(trafficPolicy *networking.TrafficPolicy, portTrafficPolicy *networking.TrafficPolicy_PortTrafficPolicy, config *Ingress)
|
||||
}
|
||||
|
||||
@@ -136,14 +136,16 @@ func (l loadBalance) Parse(annotations Annotations, config *Ingress, _ *GlobalCo
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l loadBalance) ApplyTrafficPolicy(trafficPolicy *networking.TrafficPolicy_PortTrafficPolicy, config *Ingress) {
|
||||
func (l loadBalance) ApplyTrafficPolicy(trafficPolicy *networking.TrafficPolicy, portTrafficPolicy *networking.TrafficPolicy_PortTrafficPolicy, config *Ingress) {
|
||||
loadBalanceConfig := config.LoadBalance
|
||||
if loadBalanceConfig == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var loadBalancer *networking.LoadBalancerSettings
|
||||
|
||||
if loadBalanceConfig.cookie != nil {
|
||||
trafficPolicy.LoadBalancer = &networking.LoadBalancerSettings{
|
||||
loadBalancer = &networking.LoadBalancerSettings{
|
||||
LbPolicy: &networking.LoadBalancerSettings_ConsistentHash{
|
||||
ConsistentHash: &networking.LoadBalancerSettings_ConsistentHashLB{
|
||||
HashKey: &networking.LoadBalancerSettings_ConsistentHashLB_HttpCookie{
|
||||
@@ -171,18 +173,25 @@ func (l loadBalance) ApplyTrafficPolicy(trafficPolicy *networking.TrafficPolicy_
|
||||
},
|
||||
}
|
||||
}
|
||||
trafficPolicy.LoadBalancer = &networking.LoadBalancerSettings{
|
||||
loadBalancer = &networking.LoadBalancerSettings{
|
||||
LbPolicy: &networking.LoadBalancerSettings_ConsistentHash{
|
||||
ConsistentHash: consistentHash,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
trafficPolicy.LoadBalancer = &networking.LoadBalancerSettings{
|
||||
loadBalancer = &networking.LoadBalancerSettings{
|
||||
LbPolicy: &networking.LoadBalancerSettings_Simple{
|
||||
Simple: loadBalanceConfig.simple,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if trafficPolicy != nil {
|
||||
trafficPolicy.LoadBalancer = loadBalancer
|
||||
}
|
||||
if portTrafficPolicy != nil {
|
||||
portTrafficPolicy.LoadBalancer = loadBalancer
|
||||
}
|
||||
}
|
||||
|
||||
func isCookieAffinity(annotations Annotations) bool {
|
||||
|
||||
@@ -229,7 +229,7 @@ func TestLoadBalanceApplyTrafficPolicy(t *testing.T) {
|
||||
|
||||
for _, inputCase := range inputCases {
|
||||
t.Run("", func(t *testing.T) {
|
||||
loadBalance.ApplyTrafficPolicy(inputCase.input, inputCase.config)
|
||||
loadBalance.ApplyTrafficPolicy(nil, inputCase.input, inputCase.config)
|
||||
if !reflect.DeepEqual(inputCase.input, inputCase.expect) {
|
||||
t.Fatal("Should be equal")
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
rewritePath = "rewrite-path"
|
||||
rewriteTarget = "rewrite-target"
|
||||
useRegex = "use-regex"
|
||||
upstreamVhost = "upstream-vhost"
|
||||
@@ -38,6 +39,7 @@ type RewriteConfig struct {
|
||||
RewriteTarget string
|
||||
UseRegex bool
|
||||
RewriteHost string
|
||||
RewritePath string
|
||||
}
|
||||
|
||||
type rewrite struct{}
|
||||
@@ -51,8 +53,9 @@ func (r rewrite) Parse(annotations Annotations, config *Ingress, _ *GlobalContex
|
||||
rewriteConfig.RewriteTarget, _ = annotations.ParseStringASAP(rewriteTarget)
|
||||
rewriteConfig.UseRegex, _ = annotations.ParseBoolASAP(useRegex)
|
||||
rewriteConfig.RewriteHost, _ = annotations.ParseStringASAP(upstreamVhost)
|
||||
rewriteConfig.RewritePath, _ = annotations.ParseStringForHigress(rewritePath)
|
||||
|
||||
if rewriteConfig.RewriteTarget != "" {
|
||||
if rewriteConfig.RewritePath == "" && rewriteConfig.RewriteTarget != "" {
|
||||
// When rewrite target is present and not empty,
|
||||
// we will enforce regex match on all rules in this ingress.
|
||||
rewriteConfig.UseRegex = true
|
||||
@@ -68,12 +71,22 @@ func (r rewrite) Parse(annotations Annotations, config *Ingress, _ *GlobalContex
|
||||
func (r rewrite) ApplyRoute(route *networking.HTTPRoute, config *Ingress) {
|
||||
rewriteConfig := config.Rewrite
|
||||
if rewriteConfig == nil || (rewriteConfig.RewriteTarget == "" &&
|
||||
rewriteConfig.RewriteHost == "") {
|
||||
rewriteConfig.RewriteHost == "" && rewriteConfig.RewritePath == "") {
|
||||
return
|
||||
}
|
||||
|
||||
route.Rewrite = &networking.HTTPRewrite{}
|
||||
if rewriteConfig.RewriteTarget != "" {
|
||||
if rewriteConfig.RewritePath != "" {
|
||||
route.Rewrite.Uri = rewriteConfig.RewritePath
|
||||
for _, match := range route.Match {
|
||||
if strings.HasSuffix(match.Uri.GetPrefix(), "/") {
|
||||
if !strings.HasSuffix(route.Rewrite.Uri, "/") {
|
||||
route.Rewrite.Uri += "/"
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
} else if rewriteConfig.RewriteTarget != "" {
|
||||
route.Rewrite.UriRegex = &networking.RegexMatchAndSubstitute{
|
||||
Pattern: route.Match[0].Uri.GetRegex(),
|
||||
Substitution: rewriteConfig.RewriteTarget,
|
||||
@@ -102,5 +115,5 @@ func NeedRegexMatch(annotations map[string]string) bool {
|
||||
|
||||
func needRewriteConfig(annotations Annotations) bool {
|
||||
return annotations.HasASAP(rewriteTarget) || annotations.HasASAP(useRegex) ||
|
||||
annotations.HasASAP(upstreamVhost)
|
||||
annotations.HasASAP(upstreamVhost) || annotations.HasHigress(rewritePath)
|
||||
}
|
||||
|
||||
@@ -124,6 +124,14 @@ func TestRewriteParse(t *testing.T) {
|
||||
RewriteHost: "test.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: Annotations{
|
||||
buildHigressAnnotationKey(rewritePath): "/test",
|
||||
},
|
||||
expect: &RewriteConfig{
|
||||
RewritePath: "/test",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
@@ -241,6 +249,87 @@ func TestRewriteApplyRoute(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
config: &Ingress{
|
||||
Rewrite: &RewriteConfig{
|
||||
RewriteTarget: "/test",
|
||||
RewritePath: "/test",
|
||||
RewriteHost: "test.com",
|
||||
},
|
||||
},
|
||||
input: &networking.HTTPRoute{
|
||||
Match: []*networking.HTTPMatchRequest{
|
||||
{
|
||||
Uri: &networking.StringMatch{
|
||||
MatchType: &networking.StringMatch_Regex{
|
||||
Regex: "/hello",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expect: &networking.HTTPRoute{
|
||||
Match: []*networking.HTTPMatchRequest{
|
||||
{
|
||||
Uri: &networking.StringMatch{
|
||||
MatchType: &networking.StringMatch_Regex{
|
||||
Regex: "/hello",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Rewrite: &networking.HTTPRewrite{
|
||||
Uri: "/test",
|
||||
Authority: "test.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
config: &Ingress{
|
||||
Rewrite: &RewriteConfig{
|
||||
RewritePath: "/test",
|
||||
},
|
||||
},
|
||||
input: &networking.HTTPRoute{
|
||||
Match: []*networking.HTTPMatchRequest{
|
||||
{
|
||||
Uri: &networking.StringMatch{
|
||||
MatchType: &networking.StringMatch_Prefix{
|
||||
Prefix: "/hello/",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Uri: &networking.StringMatch{
|
||||
MatchType: &networking.StringMatch_Exact{
|
||||
Exact: "/hello",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expect: &networking.HTTPRoute{
|
||||
Match: []*networking.HTTPMatchRequest{
|
||||
{
|
||||
Uri: &networking.StringMatch{
|
||||
MatchType: &networking.StringMatch_Prefix{
|
||||
Prefix: "/hello/",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Uri: &networking.StringMatch{
|
||||
MatchType: &networking.StringMatch_Exact{
|
||||
Exact: "/hello",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Rewrite: &networking.HTTPRewrite{
|
||||
Uri: "/test/",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, inputCase := range inputCases {
|
||||
|
||||
@@ -75,6 +75,20 @@ func (u upstreamTLS) Parse(annotations Annotations, config *Ingress, _ *GlobalCo
|
||||
}
|
||||
}
|
||||
|
||||
if sslVerify, err := annotations.ParseStringASAP(proxySSLVerify); err == nil {
|
||||
if OnOffRegex.MatchString(sslVerify) {
|
||||
upstreamTLSConfig.SSLVerify = onOffToBool(sslVerify)
|
||||
}
|
||||
}
|
||||
|
||||
upstreamTLSConfig.SNI, _ = annotations.ParseStringASAP(proxySSLName)
|
||||
|
||||
if enableSNI, err := annotations.ParseStringASAP(proxySSLServerName); err == nil {
|
||||
if OnOffRegex.MatchString(enableSNI) {
|
||||
upstreamTLSConfig.EnableSNI = onOffToBool(enableSNI)
|
||||
}
|
||||
}
|
||||
|
||||
secretName, _ := annotations.ParseStringASAP(proxySSLSecret)
|
||||
namespacedName := util.SplitNamespacedName(secretName)
|
||||
if namespacedName.Name == "" {
|
||||
@@ -86,32 +100,19 @@ func (u upstreamTLS) Parse(annotations Annotations, config *Ingress, _ *GlobalCo
|
||||
}
|
||||
upstreamTLSConfig.SecretName = namespacedName.String()
|
||||
|
||||
if sslVerify, err := annotations.ParseStringASAP(proxySSLVerify); err == nil {
|
||||
if OnOffRegex.MatchString(sslVerify) {
|
||||
upstreamTLSConfig.SSLVerify = onOffToBool(sslVerify)
|
||||
}
|
||||
}
|
||||
|
||||
upstreamTLSConfig.SNI, _ = annotations.ParseStringASAP(proxySSLName)
|
||||
|
||||
if enableSNI, err := annotations.ParseStringASAP(proxySSLServerName); err == nil {
|
||||
if OnOffRegex.MatchString(enableSNI) {
|
||||
upstreamTLSConfig.SSLVerify = onOffToBool(enableSNI)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u upstreamTLS) ApplyTrafficPolicy(trafficPolicy *networking.TrafficPolicy_PortTrafficPolicy, config *Ingress) {
|
||||
func (u upstreamTLS) ApplyTrafficPolicy(trafficPolicy *networking.TrafficPolicy, portTrafficPolicy *networking.TrafficPolicy_PortTrafficPolicy, config *Ingress) {
|
||||
if config.UpstreamTLS == nil {
|
||||
return
|
||||
}
|
||||
|
||||
upstreamTLSConfig := config.UpstreamTLS
|
||||
|
||||
var connectionPool *networking.ConnectionPoolSettings
|
||||
if isH2(upstreamTLSConfig.BackendProtocol) {
|
||||
trafficPolicy.ConnectionPool = &networking.ConnectionPoolSettings{
|
||||
connectionPool = &networking.ConnectionPoolSettings{
|
||||
Http: &networking.ConnectionPoolSettings_HTTPSettings{
|
||||
H2UpgradePolicy: networking.ConnectionPoolSettings_HTTPSettings_UPGRADE,
|
||||
},
|
||||
@@ -125,8 +126,14 @@ func (u upstreamTLS) ApplyTrafficPolicy(trafficPolicy *networking.TrafficPolicy_
|
||||
} else if isHTTPS(upstreamTLSConfig.BackendProtocol) {
|
||||
tls = processSimple(config)
|
||||
}
|
||||
|
||||
trafficPolicy.Tls = tls
|
||||
if trafficPolicy != nil {
|
||||
trafficPolicy.ConnectionPool = connectionPool
|
||||
trafficPolicy.Tls = tls
|
||||
}
|
||||
if portTrafficPolicy != nil {
|
||||
portTrafficPolicy.ConnectionPool = connectionPool
|
||||
portTrafficPolicy.Tls = tls
|
||||
}
|
||||
}
|
||||
|
||||
func processMTLS(config *Ingress) *networking.ClientTLSSettings {
|
||||
|
||||
@@ -47,6 +47,7 @@ func TestUpstreamTLSParse(t *testing.T) {
|
||||
SSLVerify: true,
|
||||
SNI: "SSLName",
|
||||
SecretName: "namespace/SSLSecret",
|
||||
EnableSNI: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -60,9 +61,10 @@ func TestUpstreamTLSParse(t *testing.T) {
|
||||
},
|
||||
expect: &UpstreamTLSConfig{
|
||||
BackendProtocol: "HTTP2",
|
||||
SSLVerify: false,
|
||||
SNI: "",
|
||||
SSLVerify: true,
|
||||
SNI: "SSLName",
|
||||
SecretName: "",
|
||||
EnableSNI: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -143,7 +145,7 @@ func TestApplyTrafficPolicy(t *testing.T) {
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run("", func(t *testing.T) {
|
||||
parser.ApplyTrafficPolicy(testCase.input, testCase.config)
|
||||
parser.ApplyTrafficPolicy(nil, testCase.input, testCase.config)
|
||||
if diff := cmp.Diff(testCase.expect, testCase.input); diff != "" {
|
||||
t.Fatalf("TestApplyTrafficPolicy() mismatch (-want +got): \n%s", diff)
|
||||
}
|
||||
|
||||
@@ -28,9 +28,10 @@ import (
|
||||
)
|
||||
|
||||
type ServiceKey struct {
|
||||
Namespace string
|
||||
Name string
|
||||
Port int32
|
||||
Namespace string
|
||||
Name string
|
||||
ServiceFQDN string
|
||||
Port int32
|
||||
}
|
||||
|
||||
type WrapperConfig struct {
|
||||
@@ -98,8 +99,9 @@ type WrapperVirtualService struct {
|
||||
}
|
||||
|
||||
type WrapperTrafficPolicy struct {
|
||||
TrafficPolicy *networking.TrafficPolicy_PortTrafficPolicy
|
||||
WrapperConfig *WrapperConfig
|
||||
TrafficPolicy *networking.TrafficPolicy
|
||||
PortTrafficPolicy *networking.TrafficPolicy_PortTrafficPolicy
|
||||
WrapperConfig *WrapperConfig
|
||||
}
|
||||
|
||||
type WrapperDestinationRule struct {
|
||||
|
||||
@@ -19,7 +19,6 @@ import (
|
||||
"fmt"
|
||||
"path"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -532,40 +531,25 @@ func (c *controller) ConvertHTTPRoute(convertOptions *common.ConvertOptions, wra
|
||||
Host: rule.Host,
|
||||
ClusterId: c.options.ClusterId,
|
||||
}
|
||||
httpMatch := &networking.HTTPMatchRequest{}
|
||||
|
||||
path := httpPath.Path
|
||||
var pathType common.PathType
|
||||
originPath := httpPath.Path
|
||||
if wrapper.AnnotationsConfig.NeedRegexMatch() {
|
||||
wrapperHttpRoute.OriginPathType = common.Regex
|
||||
httpMatch.Uri = &networking.StringMatch{
|
||||
MatchType: &networking.StringMatch_Regex{Regex: httpPath.Path + ".*"},
|
||||
}
|
||||
pathType = common.Regex
|
||||
} else {
|
||||
switch *httpPath.PathType {
|
||||
case ingress.PathTypeExact:
|
||||
wrapperHttpRoute.OriginPathType = common.Exact
|
||||
httpMatch.Uri = &networking.StringMatch{
|
||||
MatchType: &networking.StringMatch_Exact{Exact: httpPath.Path},
|
||||
}
|
||||
pathType = common.Exact
|
||||
case ingress.PathTypePrefix:
|
||||
wrapperHttpRoute.OriginPathType = common.Prefix
|
||||
// borrow from implement of official istio code.
|
||||
if path == "/" {
|
||||
wrapperVS.ConfiguredDefaultBackend = true
|
||||
// Optimize common case of / to not needed regex
|
||||
httpMatch.Uri = &networking.StringMatch{
|
||||
MatchType: &networking.StringMatch_Prefix{Prefix: path},
|
||||
}
|
||||
} else {
|
||||
path = strings.TrimSuffix(path, "/")
|
||||
httpMatch.Uri = &networking.StringMatch{
|
||||
MatchType: &networking.StringMatch_Regex{Regex: regexp.QuoteMeta(path) + common.PrefixMatchRegex},
|
||||
}
|
||||
pathType = common.Prefix
|
||||
if httpPath.Path != "/" {
|
||||
originPath = strings.TrimSuffix(httpPath.Path, "/")
|
||||
}
|
||||
}
|
||||
}
|
||||
wrapperHttpRoute.OriginPath = path
|
||||
wrapperHttpRoute.HTTPRoute.Match = []*networking.HTTPMatchRequest{httpMatch}
|
||||
wrapperHttpRoute.OriginPath = originPath
|
||||
wrapperHttpRoute.OriginPathType = pathType
|
||||
wrapperHttpRoute.HTTPRoute.Match = c.generateHttpMatches(pathType, httpPath.Path, wrapperVS)
|
||||
wrapperHttpRoute.HTTPRoute.Name = common.GenerateUniqueRouteName(c.options.SystemNamespace, wrapperHttpRoute)
|
||||
|
||||
ingressRouteBuilder := convertOptions.IngressRouteCache.New(wrapperHttpRoute)
|
||||
@@ -748,46 +732,31 @@ func (c *controller) ApplyCanaryIngress(convertOptions *common.ConvertOptions, w
|
||||
}
|
||||
|
||||
for _, httpPath := range rule.HTTP.Paths {
|
||||
path := httpPath.Path
|
||||
|
||||
canary := &common.WrapperHTTPRoute{
|
||||
HTTPRoute: &networking.HTTPRoute{},
|
||||
WrapperConfig: wrapper,
|
||||
Host: rule.Host,
|
||||
ClusterId: c.options.ClusterId,
|
||||
}
|
||||
httpMatch := &networking.HTTPMatchRequest{}
|
||||
|
||||
var pathType common.PathType
|
||||
originPath := httpPath.Path
|
||||
if wrapper.AnnotationsConfig.NeedRegexMatch() {
|
||||
canary.OriginPathType = common.Regex
|
||||
httpMatch.Uri = &networking.StringMatch{
|
||||
MatchType: &networking.StringMatch_Regex{Regex: httpPath.Path + ".*"},
|
||||
}
|
||||
pathType = common.Regex
|
||||
} else {
|
||||
switch *httpPath.PathType {
|
||||
case ingress.PathTypeExact:
|
||||
canary.OriginPathType = common.Exact
|
||||
httpMatch.Uri = &networking.StringMatch{
|
||||
MatchType: &networking.StringMatch_Exact{Exact: httpPath.Path},
|
||||
}
|
||||
pathType = common.Exact
|
||||
case ingress.PathTypePrefix:
|
||||
canary.OriginPathType = common.Prefix
|
||||
// borrow from implement of official istio code.
|
||||
if path == "/" {
|
||||
// Optimize common case of / to not needed regex
|
||||
httpMatch.Uri = &networking.StringMatch{
|
||||
MatchType: &networking.StringMatch_Prefix{Prefix: path},
|
||||
}
|
||||
} else {
|
||||
path = strings.TrimSuffix(path, "/")
|
||||
httpMatch.Uri = &networking.StringMatch{
|
||||
MatchType: &networking.StringMatch_Regex{Regex: regexp.QuoteMeta(path) + common.PrefixMatchRegex},
|
||||
}
|
||||
pathType = common.Prefix
|
||||
if httpPath.Path != "/" {
|
||||
originPath = strings.TrimSuffix(httpPath.Path, "/")
|
||||
}
|
||||
}
|
||||
}
|
||||
canary.OriginPath = path
|
||||
canary.HTTPRoute.Match = []*networking.HTTPMatchRequest{httpMatch}
|
||||
canary.OriginPath = originPath
|
||||
canary.OriginPathType = pathType
|
||||
canary.HTTPRoute.Match = c.generateHttpMatches(pathType, httpPath.Path, nil)
|
||||
canary.HTTPRoute.Name = common.GenerateUniqueRouteName(c.options.SystemNamespace, canary)
|
||||
|
||||
ingressRouteBuilder := convertOptions.IngressRouteCache.New(canary)
|
||||
@@ -879,20 +848,9 @@ func (c *controller) ConvertTrafficPolicy(convertOptions *common.ConvertOptions,
|
||||
}
|
||||
|
||||
if ingressV1Beta.Backend != nil {
|
||||
serviceKey, err := c.createServiceKey(ingressV1Beta.Backend, cfg.Namespace)
|
||||
err := c.storeBackendTrafficPolicy(wrapper, ingressV1Beta.Backend, convertOptions.Service2TrafficPolicy)
|
||||
if err != nil {
|
||||
IngressLog.Errorf("ignore default service %s within ingress %s/%s", serviceKey.Name, cfg.Namespace, cfg.Name)
|
||||
} else {
|
||||
if _, exist := convertOptions.Service2TrafficPolicy[serviceKey]; !exist {
|
||||
convertOptions.Service2TrafficPolicy[serviceKey] = &common.WrapperTrafficPolicy{
|
||||
TrafficPolicy: &networking.TrafficPolicy_PortTrafficPolicy{
|
||||
Port: &networking.PortSelector{
|
||||
Number: uint32(serviceKey.Port),
|
||||
},
|
||||
},
|
||||
WrapperConfig: wrapper,
|
||||
}
|
||||
}
|
||||
IngressLog.Errorf("ignore default service within ingress %s/%s, since error:%v", cfg.Namespace, cfg.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -902,22 +860,46 @@ func (c *controller) ConvertTrafficPolicy(convertOptions *common.ConvertOptions,
|
||||
}
|
||||
|
||||
for _, httpPath := range rule.HTTP.Paths {
|
||||
if httpPath.Backend.ServiceName == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
serviceKey, err := c.createServiceKey(&httpPath.Backend, cfg.Namespace)
|
||||
err := c.storeBackendTrafficPolicy(wrapper, &httpPath.Backend, convertOptions.Service2TrafficPolicy)
|
||||
if err != nil {
|
||||
IngressLog.Errorf("ignore service %s within ingress %s/%s", serviceKey.Name, cfg.Namespace, cfg.Name)
|
||||
continue
|
||||
IngressLog.Errorf("ignore service within ingress %s/%s, since error:%v", cfg.Namespace, cfg.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if _, exist := convertOptions.Service2TrafficPolicy[serviceKey]; exist {
|
||||
continue
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *controller) storeBackendTrafficPolicy(wrapper *common.WrapperConfig, backend *ingress.IngressBackend, store map[common.ServiceKey]*common.WrapperTrafficPolicy) error {
|
||||
if backend == nil {
|
||||
return errors.New("invalid empty backend")
|
||||
}
|
||||
if common.ValidateBackendResource(backend.Resource) && wrapper.AnnotationsConfig.Destination != nil {
|
||||
for _, dest := range wrapper.AnnotationsConfig.Destination.McpDestination {
|
||||
serviceKey := common.ServiceKey{
|
||||
Namespace: "mcp",
|
||||
Name: dest.Destination.Host,
|
||||
ServiceFQDN: dest.Destination.Host,
|
||||
}
|
||||
if _, exist := store[serviceKey]; !exist {
|
||||
store[serviceKey] = &common.WrapperTrafficPolicy{
|
||||
TrafficPolicy: &networking.TrafficPolicy{},
|
||||
WrapperConfig: wrapper,
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if backend.ServiceName == "" {
|
||||
return nil
|
||||
}
|
||||
serviceKey, err := c.createServiceKey(backend, wrapper.Config.Namespace)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ignore service %s within ingress %s/%s", serviceKey.Name, wrapper.Config.Namespace, wrapper.Config.Name)
|
||||
}
|
||||
|
||||
convertOptions.Service2TrafficPolicy[serviceKey] = &common.WrapperTrafficPolicy{
|
||||
TrafficPolicy: &networking.TrafficPolicy_PortTrafficPolicy{
|
||||
if _, exist := store[serviceKey]; !exist {
|
||||
store[serviceKey] = &common.WrapperTrafficPolicy{
|
||||
PortTrafficPolicy: &networking.TrafficPolicy_PortTrafficPolicy{
|
||||
Port: &networking.PortSelector{
|
||||
Number: uint32(serviceKey.Port),
|
||||
},
|
||||
@@ -926,7 +908,6 @@ func (c *controller) ConvertTrafficPolicy(convertOptions *common.ConvertOptions,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1180,6 +1161,42 @@ func (c *controller) shouldProcessIngressUpdate(ing *ingress.Ingress) (bool, err
|
||||
return preProcessed, nil
|
||||
}
|
||||
|
||||
func (c *controller) generateHttpMatches(pathType common.PathType, path string, wrapperVS *common.WrapperVirtualService) []*networking.HTTPMatchRequest {
|
||||
var httpMatches []*networking.HTTPMatchRequest
|
||||
|
||||
httpMatch := &networking.HTTPMatchRequest{}
|
||||
switch pathType {
|
||||
case common.Regex:
|
||||
httpMatch.Uri = &networking.StringMatch{
|
||||
MatchType: &networking.StringMatch_Regex{Regex: path + ".*"},
|
||||
}
|
||||
case common.Exact:
|
||||
httpMatch.Uri = &networking.StringMatch{
|
||||
MatchType: &networking.StringMatch_Exact{Exact: path},
|
||||
}
|
||||
case common.Prefix:
|
||||
if path == "/" {
|
||||
if wrapperVS != nil {
|
||||
wrapperVS.ConfiguredDefaultBackend = true
|
||||
}
|
||||
// Optimize common case of / to not needed regex
|
||||
httpMatch.Uri = &networking.StringMatch{
|
||||
MatchType: &networking.StringMatch_Prefix{Prefix: path},
|
||||
}
|
||||
} else {
|
||||
newPath := strings.TrimSuffix(path, "/")
|
||||
httpMatches = append(httpMatches, c.generateHttpMatches(common.Exact, newPath, wrapperVS)...)
|
||||
httpMatch.Uri = &networking.StringMatch{
|
||||
MatchType: &networking.StringMatch_Prefix{Prefix: newPath + "/"},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
httpMatches = append(httpMatches, httpMatch)
|
||||
|
||||
return httpMatches
|
||||
}
|
||||
|
||||
// setDefaultMSEIngressOptionalField sets a default value for optional fields when is not defined.
|
||||
func setDefaultMSEIngressOptionalField(ing *ingress.Ingress) {
|
||||
if ing == nil {
|
||||
|
||||
@@ -19,7 +19,6 @@ import (
|
||||
"fmt"
|
||||
"path"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -507,6 +506,7 @@ func (c *controller) ConvertHTTPRoute(convertOptions *common.ConvertOptions, wra
|
||||
}
|
||||
|
||||
wrapperHttpRoutes := make([]*common.WrapperHTTPRoute, 0, len(rule.HTTP.Paths))
|
||||
|
||||
for _, httpPath := range rule.HTTP.Paths {
|
||||
wrapperHttpRoute := &common.WrapperHTTPRoute{
|
||||
HTTPRoute: &networking.HTTPRoute{},
|
||||
@@ -514,40 +514,25 @@ func (c *controller) ConvertHTTPRoute(convertOptions *common.ConvertOptions, wra
|
||||
Host: rule.Host,
|
||||
ClusterId: c.options.ClusterId,
|
||||
}
|
||||
httpMatch := &networking.HTTPMatchRequest{}
|
||||
|
||||
path := httpPath.Path
|
||||
var pathType common.PathType
|
||||
originPath := httpPath.Path
|
||||
if wrapper.AnnotationsConfig.NeedRegexMatch() {
|
||||
wrapperHttpRoute.OriginPathType = common.Regex
|
||||
httpMatch.Uri = &networking.StringMatch{
|
||||
MatchType: &networking.StringMatch_Regex{Regex: httpPath.Path + ".*"},
|
||||
}
|
||||
pathType = common.Regex
|
||||
} else {
|
||||
switch *httpPath.PathType {
|
||||
case ingress.PathTypeExact:
|
||||
wrapperHttpRoute.OriginPathType = common.Exact
|
||||
httpMatch.Uri = &networking.StringMatch{
|
||||
MatchType: &networking.StringMatch_Exact{Exact: httpPath.Path},
|
||||
}
|
||||
pathType = common.Exact
|
||||
case ingress.PathTypePrefix:
|
||||
wrapperHttpRoute.OriginPathType = common.Prefix
|
||||
// borrow from implement of official istio code.
|
||||
if path == "/" {
|
||||
wrapperVS.ConfiguredDefaultBackend = true
|
||||
// Optimize common case of / to not needed regex
|
||||
httpMatch.Uri = &networking.StringMatch{
|
||||
MatchType: &networking.StringMatch_Prefix{Prefix: path},
|
||||
}
|
||||
} else {
|
||||
path = strings.TrimSuffix(path, "/")
|
||||
httpMatch.Uri = &networking.StringMatch{
|
||||
MatchType: &networking.StringMatch_Regex{Regex: regexp.QuoteMeta(path) + common.PrefixMatchRegex},
|
||||
}
|
||||
pathType = common.Prefix
|
||||
if httpPath.Path != "/" {
|
||||
originPath = strings.TrimSuffix(httpPath.Path, "/")
|
||||
}
|
||||
}
|
||||
}
|
||||
wrapperHttpRoute.OriginPath = path
|
||||
wrapperHttpRoute.HTTPRoute.Match = []*networking.HTTPMatchRequest{httpMatch}
|
||||
wrapperHttpRoute.OriginPath = originPath
|
||||
wrapperHttpRoute.OriginPathType = pathType
|
||||
wrapperHttpRoute.HTTPRoute.Match = c.generateHttpMatches(pathType, httpPath.Path, wrapperVS)
|
||||
wrapperHttpRoute.HTTPRoute.Name = common.GenerateUniqueRouteName(c.options.SystemNamespace, wrapperHttpRoute)
|
||||
|
||||
ingressRouteBuilder := convertOptions.IngressRouteCache.New(wrapperHttpRoute)
|
||||
@@ -620,6 +605,42 @@ func (c *controller) ConvertHTTPRoute(convertOptions *common.ConvertOptions, wra
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *controller) generateHttpMatches(pathType common.PathType, path string, wrapperVS *common.WrapperVirtualService) []*networking.HTTPMatchRequest {
|
||||
var httpMatches []*networking.HTTPMatchRequest
|
||||
|
||||
httpMatch := &networking.HTTPMatchRequest{}
|
||||
switch pathType {
|
||||
case common.Regex:
|
||||
httpMatch.Uri = &networking.StringMatch{
|
||||
MatchType: &networking.StringMatch_Regex{Regex: path + ".*"},
|
||||
}
|
||||
case common.Exact:
|
||||
httpMatch.Uri = &networking.StringMatch{
|
||||
MatchType: &networking.StringMatch_Exact{Exact: path},
|
||||
}
|
||||
case common.Prefix:
|
||||
if path == "/" {
|
||||
if wrapperVS != nil {
|
||||
wrapperVS.ConfiguredDefaultBackend = true
|
||||
}
|
||||
// Optimize common case of / to not needed regex
|
||||
httpMatch.Uri = &networking.StringMatch{
|
||||
MatchType: &networking.StringMatch_Prefix{Prefix: path},
|
||||
}
|
||||
} else {
|
||||
newPath := strings.TrimSuffix(path, "/")
|
||||
httpMatches = append(httpMatches, c.generateHttpMatches(common.Exact, newPath, wrapperVS)...)
|
||||
httpMatch.Uri = &networking.StringMatch{
|
||||
MatchType: &networking.StringMatch_Prefix{Prefix: newPath + "/"},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
httpMatches = append(httpMatches, httpMatch)
|
||||
|
||||
return httpMatches
|
||||
}
|
||||
|
||||
func (c *controller) ApplyDefaultBackend(convertOptions *common.ConvertOptions, wrapper *common.WrapperConfig) error {
|
||||
if wrapper.AnnotationsConfig.IsCanary() {
|
||||
return nil
|
||||
@@ -717,46 +738,31 @@ func (c *controller) ApplyCanaryIngress(convertOptions *common.ConvertOptions, w
|
||||
}
|
||||
|
||||
for _, httpPath := range rule.HTTP.Paths {
|
||||
path := httpPath.Path
|
||||
|
||||
canary := &common.WrapperHTTPRoute{
|
||||
HTTPRoute: &networking.HTTPRoute{},
|
||||
WrapperConfig: wrapper,
|
||||
Host: rule.Host,
|
||||
ClusterId: c.options.ClusterId,
|
||||
}
|
||||
httpMatch := &networking.HTTPMatchRequest{}
|
||||
|
||||
var pathType common.PathType
|
||||
originPath := httpPath.Path
|
||||
if wrapper.AnnotationsConfig.NeedRegexMatch() {
|
||||
canary.OriginPathType = common.Regex
|
||||
httpMatch.Uri = &networking.StringMatch{
|
||||
MatchType: &networking.StringMatch_Regex{Regex: httpPath.Path + ".*"},
|
||||
}
|
||||
pathType = common.Regex
|
||||
} else {
|
||||
switch *httpPath.PathType {
|
||||
case ingress.PathTypeExact:
|
||||
canary.OriginPathType = common.Exact
|
||||
httpMatch.Uri = &networking.StringMatch{
|
||||
MatchType: &networking.StringMatch_Exact{Exact: httpPath.Path},
|
||||
}
|
||||
pathType = common.Exact
|
||||
case ingress.PathTypePrefix:
|
||||
canary.OriginPathType = common.Prefix
|
||||
// borrow from implement of official istio code.
|
||||
if path == "/" {
|
||||
// Optimize common case of / to not needed regex
|
||||
httpMatch.Uri = &networking.StringMatch{
|
||||
MatchType: &networking.StringMatch_Prefix{Prefix: path},
|
||||
}
|
||||
} else {
|
||||
path = strings.TrimSuffix(path, "/")
|
||||
httpMatch.Uri = &networking.StringMatch{
|
||||
MatchType: &networking.StringMatch_Regex{Regex: regexp.QuoteMeta(path) + common.PrefixMatchRegex},
|
||||
}
|
||||
pathType = common.Prefix
|
||||
if httpPath.Path != "/" {
|
||||
originPath = strings.TrimSuffix(httpPath.Path, "/")
|
||||
}
|
||||
}
|
||||
}
|
||||
canary.OriginPath = path
|
||||
canary.HTTPRoute.Match = []*networking.HTTPMatchRequest{httpMatch}
|
||||
canary.OriginPath = originPath
|
||||
canary.OriginPathType = pathType
|
||||
canary.HTTPRoute.Match = c.generateHttpMatches(pathType, httpPath.Path, nil)
|
||||
canary.HTTPRoute.Name = common.GenerateUniqueRouteName(c.options.SystemNamespace, canary)
|
||||
|
||||
ingressRouteBuilder := convertOptions.IngressRouteCache.New(canary)
|
||||
@@ -819,6 +825,7 @@ func (c *controller) ApplyCanaryIngress(convertOptions *common.ConvertOptions, w
|
||||
} else {
|
||||
convertOptions.IngressRouteCache.Update(targetRoute)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -841,20 +848,9 @@ func (c *controller) ConvertTrafficPolicy(convertOptions *common.ConvertOptions,
|
||||
}
|
||||
|
||||
if ingressV1.DefaultBackend != nil {
|
||||
serviceKey, err := c.createServiceKey(ingressV1.DefaultBackend.Service, cfg.Namespace)
|
||||
err := c.storeBackendTrafficPolicy(wrapper, ingressV1.DefaultBackend, convertOptions.Service2TrafficPolicy)
|
||||
if err != nil {
|
||||
IngressLog.Errorf("ignore default service %s within ingress %s/%s", serviceKey.Name, cfg.Namespace, cfg.Name)
|
||||
} else {
|
||||
if _, exist := convertOptions.Service2TrafficPolicy[serviceKey]; !exist {
|
||||
convertOptions.Service2TrafficPolicy[serviceKey] = &common.WrapperTrafficPolicy{
|
||||
TrafficPolicy: &networking.TrafficPolicy_PortTrafficPolicy{
|
||||
Port: &networking.PortSelector{
|
||||
Number: uint32(serviceKey.Port),
|
||||
},
|
||||
},
|
||||
WrapperConfig: wrapper,
|
||||
}
|
||||
}
|
||||
IngressLog.Errorf("ignore default service within ingress %s/%s, since error:%v", cfg.Namespace, cfg.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -864,22 +860,46 @@ func (c *controller) ConvertTrafficPolicy(convertOptions *common.ConvertOptions,
|
||||
}
|
||||
|
||||
for _, httpPath := range rule.HTTP.Paths {
|
||||
if httpPath.Backend.Service == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
serviceKey, err := c.createServiceKey(httpPath.Backend.Service, cfg.Namespace)
|
||||
err := c.storeBackendTrafficPolicy(wrapper, &httpPath.Backend, convertOptions.Service2TrafficPolicy)
|
||||
if err != nil {
|
||||
IngressLog.Errorf("ignore service %s within ingress %s/%s", serviceKey.Name, cfg.Namespace, cfg.Name)
|
||||
continue
|
||||
IngressLog.Errorf("ignore service within ingress %s/%s, since error:%v", cfg.Namespace, cfg.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if _, exist := convertOptions.Service2TrafficPolicy[serviceKey]; exist {
|
||||
continue
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *controller) storeBackendTrafficPolicy(wrapper *common.WrapperConfig, backend *ingress.IngressBackend, store map[common.ServiceKey]*common.WrapperTrafficPolicy) error {
|
||||
if backend == nil {
|
||||
return errors.New("invalid empty backend")
|
||||
}
|
||||
if common.ValidateBackendResource(backend.Resource) && wrapper.AnnotationsConfig.Destination != nil {
|
||||
for _, dest := range wrapper.AnnotationsConfig.Destination.McpDestination {
|
||||
serviceKey := common.ServiceKey{
|
||||
Namespace: "mcp",
|
||||
Name: dest.Destination.Host,
|
||||
ServiceFQDN: dest.Destination.Host,
|
||||
}
|
||||
if _, exist := store[serviceKey]; !exist {
|
||||
store[serviceKey] = &common.WrapperTrafficPolicy{
|
||||
TrafficPolicy: &networking.TrafficPolicy{},
|
||||
WrapperConfig: wrapper,
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if backend.Service == nil {
|
||||
return nil
|
||||
}
|
||||
serviceKey, err := c.createServiceKey(backend.Service, wrapper.Config.Namespace)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ignore service %s within ingress %s/%s", serviceKey.Name, wrapper.Config.Namespace, wrapper.Config.Name)
|
||||
}
|
||||
|
||||
convertOptions.Service2TrafficPolicy[serviceKey] = &common.WrapperTrafficPolicy{
|
||||
TrafficPolicy: &networking.TrafficPolicy_PortTrafficPolicy{
|
||||
if _, exist := store[serviceKey]; !exist {
|
||||
store[serviceKey] = &common.WrapperTrafficPolicy{
|
||||
PortTrafficPolicy: &networking.TrafficPolicy_PortTrafficPolicy{
|
||||
Port: &networking.PortSelector{
|
||||
Number: uint32(serviceKey.Port),
|
||||
},
|
||||
@@ -888,7 +908,6 @@ func (c *controller) ConvertTrafficPolicy(convertOptions *common.ConvertOptions,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ package ingressv1
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
networking "istio.io/api/networking/v1alpha3"
|
||||
v1 "k8s.io/api/networking/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
@@ -76,3 +78,38 @@ func TestShouldProcessIngressUpdate(t *testing.T) {
|
||||
t.Fatal("should be true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateHttpMatches(t *testing.T) {
|
||||
c := controller{}
|
||||
|
||||
tt := []struct {
|
||||
pathType common.PathType
|
||||
path string
|
||||
expect []*networking.HTTPMatchRequest
|
||||
}{
|
||||
{
|
||||
pathType: common.Prefix,
|
||||
path: "/foo",
|
||||
expect: []*networking.HTTPMatchRequest{
|
||||
{
|
||||
Uri: &networking.StringMatch{
|
||||
MatchType: &networking.StringMatch_Exact{Exact: "/foo"},
|
||||
},
|
||||
}, {
|
||||
Uri: &networking.StringMatch{
|
||||
MatchType: &networking.StringMatch_Prefix{Prefix: "/foo/"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testcase := range tt {
|
||||
httpMatches := c.generateHttpMatches(testcase.pathType, testcase.path, nil)
|
||||
for idx, httpMatch := range httpMatches {
|
||||
if diff := cmp.Diff(httpMatch, testcase.expect[idx]); diff != "" {
|
||||
t.Errorf("generateHttpMatches() mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
7
plugins/wasm-cpp/.bazelrc
Normal file
7
plugins/wasm-cpp/.bazelrc
Normal file
@@ -0,0 +1,7 @@
|
||||
build --config=clang
|
||||
build:gcc --cxxopt=-std=c++17
|
||||
|
||||
build:clang --action_env=CC=clang --action_env=CXX=clang++
|
||||
build:clang --action_env=BAZEL_COMPILER=clang
|
||||
build:clang --linkopt=-fuse-ld=lld
|
||||
build:clang --cxxopt=-std=c++17
|
||||
@@ -1,3 +1,17 @@
|
||||
# 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.
|
||||
|
||||
cc_library(
|
||||
name = "common_util",
|
||||
hdrs = [
|
||||
@@ -38,7 +52,7 @@ cc_library(
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_absl//absl/time",
|
||||
"@com_google_absl//absl/strings:str_format",
|
||||
"@proxy_wasm_cpp_host//:lib",
|
||||
"@proxy_wasm_cpp_host//:null_lib",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/strings/str_split.h"
|
||||
|
||||
#include "common/common_util.h"
|
||||
|
||||
namespace Wasm::Common::Http {
|
||||
|
||||
std::string_view stripPortFromHost(std::string_view request_host) {
|
||||
@@ -188,7 +190,7 @@ std::vector<std::string> getAllOfHeader(std::string_view key) {
|
||||
std::vector<std::string> result;
|
||||
auto headers = getRequestHeaderPairs()->pairs();
|
||||
for (auto& header : headers) {
|
||||
if (absl::EqualsIgnoreCase(header.first, key)) {
|
||||
if (absl::EqualsIgnoreCase(Wasm::Common::stdToAbsl(header.first), Wasm::Common::stdToAbsl(key))) {
|
||||
result.push_back(std::string(header.second));
|
||||
}
|
||||
}
|
||||
@@ -197,7 +199,7 @@ std::vector<std::string> getAllOfHeader(std::string_view key) {
|
||||
|
||||
void forEachCookie(
|
||||
std::string_view cookie_header,
|
||||
const std::function<bool(absl::string_view, absl::string_view)>&
|
||||
const std::function<bool(std::string_view, std::string_view)>&
|
||||
cookie_consumer) {
|
||||
auto cookie_headers = getAllOfHeader(cookie_header);
|
||||
|
||||
@@ -223,7 +225,7 @@ void forEachCookie(
|
||||
v = v.substr(1, v.size() - 2);
|
||||
}
|
||||
|
||||
if (!cookie_consumer(k, v)) {
|
||||
if (!cookie_consumer(Wasm::Common::abslToStd(k), Wasm::Common::abslToStd(v))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -263,7 +265,7 @@ std::string buildOriginalUri(std::optional<uint32_t> max_path_length) {
|
||||
auto scheme = scheme_ptr->view();
|
||||
auto host_ptr = getRequestHeader(Header::Host);
|
||||
auto host = host_ptr->view();
|
||||
return absl::StrCat(scheme, "://", host, final_path);
|
||||
return absl::StrCat(Wasm::Common::stdToAbsl(scheme), "://", Wasm::Common::stdToAbsl(host), Wasm::Common::stdToAbsl(final_path));
|
||||
}
|
||||
|
||||
} // namespace Wasm::Common::Http
|
||||
|
||||
@@ -29,17 +29,20 @@ class CompiledGoogleReMatcher {
|
||||
bool do_program_size_check = true)
|
||||
: regex_(regex, re2::RE2::Quiet) {
|
||||
if (!regex_.ok()) {
|
||||
throw std::runtime_error(regex_.error());
|
||||
error_ = regex_.error();
|
||||
return;
|
||||
}
|
||||
if (do_program_size_check) {
|
||||
const auto regex_program_size =
|
||||
static_cast<uint32_t>(regex_.ProgramSize());
|
||||
if (regex_program_size > 100) {
|
||||
throw std::runtime_error("too complex regex: " + regex);
|
||||
error_ = "too complex regex: " + regex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& error() const { return error_; }
|
||||
|
||||
bool match(std::string_view value) const {
|
||||
return re2::RE2::FullMatch(re2::StringPiece(value.data(), value.size()),
|
||||
regex_);
|
||||
@@ -56,6 +59,7 @@ class CompiledGoogleReMatcher {
|
||||
|
||||
private:
|
||||
const re2::RE2 regex_;
|
||||
std::string error_;
|
||||
};
|
||||
|
||||
} // namespace Wasm::Common::Regex
|
||||
|
||||
@@ -126,6 +126,12 @@ class RouteRuleMatcher {
|
||||
LOG_DEBUG("no match config");
|
||||
return true;
|
||||
}
|
||||
if (!config.second && global_auth_ && !global_auth_.value()) {
|
||||
// No allow set, means no need to check auth if global_auth is false
|
||||
LOG_DEBUG(
|
||||
"no allow set found, and global auth is false, no need to auth");
|
||||
return true;
|
||||
}
|
||||
return checkPlugin(config.first.value(), config.second);
|
||||
}
|
||||
|
||||
@@ -220,8 +226,9 @@ class RouteRuleMatcher {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return is_matched ? std::make_pair(match_config, allow_set)
|
||||
: std::make_pair(std::nullopt, std::nullopt);
|
||||
return is_matched || (global_auth_ && global_auth_.value())
|
||||
? std::make_pair(match_config, allow_set)
|
||||
: std::make_pair(std::nullopt, std::nullopt);
|
||||
}
|
||||
|
||||
void setEmptyGlobalConfig() { global_config_ = PluginConfig{}; }
|
||||
@@ -288,6 +295,18 @@ class RouteRuleMatcher {
|
||||
has_rules = true;
|
||||
key_count--;
|
||||
}
|
||||
auto auth_it = config.find("global_auth");
|
||||
if (auth_it != config.end()) {
|
||||
auto global_auth_value = JsonValueAs<bool>(auth_it.value());
|
||||
if (global_auth_value.second !=
|
||||
Wasm::Common::JsonParserResultDetail::OK ||
|
||||
!global_auth_value.first) {
|
||||
LOG_WARN(
|
||||
"failed to parse 'global_auth' field in filter configuration.");
|
||||
return false;
|
||||
}
|
||||
global_auth_ = global_auth_value.first.value();
|
||||
}
|
||||
PluginConfig plugin_config;
|
||||
// has other config fields
|
||||
if (key_count > 0 && parsePluginConfig(config, plugin_config)) {
|
||||
@@ -455,6 +474,7 @@ class RouteRuleMatcher {
|
||||
}
|
||||
|
||||
bool invalid_config_ = false;
|
||||
std::optional<bool> global_auth_ = std::nullopt;
|
||||
std::vector<RuleConfig> rule_config_;
|
||||
std::vector<AuthRuleConfig> auth_rule_config_;
|
||||
std::optional<PluginConfig> global_config_ = std::nullopt;
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
# 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.
|
||||
|
||||
load("@proxy_wasm_cpp_sdk//bazel/wasm:wasm.bzl", "wasm_cc_binary")
|
||||
load("//bazel:wasm.bzl", "declare_wasm_image_targets")
|
||||
|
||||
@@ -31,7 +45,7 @@ cc_library(
|
||||
visibility = ["//visibility:public"],
|
||||
alwayslink = 1,
|
||||
deps = [
|
||||
"//common:rule_util",
|
||||
"//common:rule_util_nullvm",
|
||||
"//common:json_util",
|
||||
"//common:crypto_util",
|
||||
"@com_google_absl//absl/strings",
|
||||
|
||||
@@ -308,8 +308,7 @@ bool PluginRootContext::onConfigure(size_t size) {
|
||||
// Parse configuration JSON string.
|
||||
if (size > 0 && !configure(size)) {
|
||||
LOG_WARN("configuration has errors initialization will not continue.");
|
||||
setInvalidConfig();
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -909,7 +909,8 @@ TEST_F(BasicAuthTest, GlobalDeny) {
|
||||
}
|
||||
|
||||
TEST_F(BasicAuthTest, GlobalWithConsumerDeny) {
|
||||
std::string configuration = R"(
|
||||
{
|
||||
std::string configuration = R"(
|
||||
{
|
||||
"consumers" : [
|
||||
{"credential" : "ok:test", "name" : "consumer_ok"},
|
||||
@@ -932,36 +933,171 @@ TEST_F(BasicAuthTest, GlobalWithConsumerDeny) {
|
||||
}
|
||||
]
|
||||
})";
|
||||
BufferBase buffer;
|
||||
buffer.set({configuration.data(), configuration.size()});
|
||||
BufferBase buffer;
|
||||
buffer.set({configuration.data(), configuration.size()});
|
||||
|
||||
EXPECT_CALL(*mock_context_, getBuffer(WasmBufferType::PluginConfiguration))
|
||||
.WillOnce([&buffer](WasmBufferType) { return &buffer; });
|
||||
EXPECT_TRUE(root_context_->configure(configuration.size()));
|
||||
EXPECT_CALL(*mock_context_, getBuffer(WasmBufferType::PluginConfiguration))
|
||||
.WillOnce([&buffer](WasmBufferType) { return &buffer; });
|
||||
EXPECT_TRUE(root_context_->configure(configuration.size()));
|
||||
|
||||
cred_ = "wrong-cred";
|
||||
route_name_ = "config";
|
||||
authorization_header_ = "Basic " + Base64::encode(cred_.data(), cred_.size());
|
||||
EXPECT_CALL(*mock_context_, sendLocalResponse(401, testing::_, testing::_,
|
||||
testing::_, testing::_));
|
||||
EXPECT_EQ(context_->onRequestHeaders(0, false),
|
||||
FilterHeadersStatus::StopIteration);
|
||||
cred_ = "wrong-cred";
|
||||
route_name_ = "not match";
|
||||
authorization_header_ =
|
||||
"Basic " + Base64::encode(cred_.data(), cred_.size());
|
||||
EXPECT_EQ(context_->onRequestHeaders(0, false),
|
||||
FilterHeadersStatus::Continue);
|
||||
|
||||
authority_ = "www.example.com";
|
||||
cred_ = "admin2:admin2";
|
||||
authorization_header_ = "Basic " + Base64::encode(cred_.data(), cred_.size());
|
||||
EXPECT_CALL(*mock_context_, sendLocalResponse(401, testing::_, testing::_,
|
||||
testing::_, testing::_));
|
||||
EXPECT_EQ(context_->onRequestHeaders(0, false),
|
||||
FilterHeadersStatus::StopIteration);
|
||||
cred_ = "wrong-cred";
|
||||
route_name_ = "config";
|
||||
authorization_header_ =
|
||||
"Basic " + Base64::encode(cred_.data(), cred_.size());
|
||||
EXPECT_CALL(*mock_context_, sendLocalResponse(401, testing::_, testing::_,
|
||||
testing::_, testing::_));
|
||||
EXPECT_EQ(context_->onRequestHeaders(0, false),
|
||||
FilterHeadersStatus::StopIteration);
|
||||
|
||||
route_name_ = "config";
|
||||
cred_ = "admin4:admin4";
|
||||
authorization_header_ = "Basic " + Base64::encode(cred_.data(), cred_.size());
|
||||
EXPECT_CALL(*mock_context_, sendLocalResponse(401, testing::_, testing::_,
|
||||
testing::_, testing::_));
|
||||
EXPECT_EQ(context_->onRequestHeaders(0, false),
|
||||
FilterHeadersStatus::StopIteration);
|
||||
authority_ = "www.example.com";
|
||||
cred_ = "admin2:admin2";
|
||||
authorization_header_ =
|
||||
"Basic " + Base64::encode(cred_.data(), cred_.size());
|
||||
EXPECT_CALL(*mock_context_, sendLocalResponse(401, testing::_, testing::_,
|
||||
testing::_, testing::_));
|
||||
EXPECT_EQ(context_->onRequestHeaders(0, false),
|
||||
FilterHeadersStatus::StopIteration);
|
||||
|
||||
route_name_ = "config";
|
||||
cred_ = "admin4:admin4";
|
||||
authorization_header_ =
|
||||
"Basic " + Base64::encode(cred_.data(), cred_.size());
|
||||
EXPECT_CALL(*mock_context_, sendLocalResponse(401, testing::_, testing::_,
|
||||
testing::_, testing::_));
|
||||
EXPECT_EQ(context_->onRequestHeaders(0, false),
|
||||
FilterHeadersStatus::StopIteration);
|
||||
}
|
||||
{
|
||||
std::string configuration = R"(
|
||||
{
|
||||
"global_auth": true,
|
||||
"consumers" : [
|
||||
{"credential" : "ok:test", "name" : "consumer_ok"},
|
||||
{"credential" : "admin2:admin2", "name" : "consumer2"},
|
||||
{"credential" : "admin:admin", "name" : "consumer"}
|
||||
],
|
||||
"_rules_" : [
|
||||
{
|
||||
"_match_route_" : ["test", "config"],
|
||||
"consumers" : [
|
||||
{"credential" : "admin3:admin3", "name" : "consumer3"},
|
||||
{"credential" : "YWRtaW41OmFkbWluNQ==", "name" : "consumer5"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"_match_domain_" : ["test.com", "*.example.com"],
|
||||
"consumers" : [
|
||||
{"credential" : "admin4:admin4", "name" : "consumer4"}
|
||||
]
|
||||
}
|
||||
]
|
||||
})";
|
||||
BufferBase buffer;
|
||||
buffer.set({configuration.data(), configuration.size()});
|
||||
|
||||
EXPECT_CALL(*mock_context_, getBuffer(WasmBufferType::PluginConfiguration))
|
||||
.WillOnce([&buffer](WasmBufferType) { return &buffer; });
|
||||
EXPECT_TRUE(root_context_->configure(configuration.size()));
|
||||
|
||||
cred_ = "wrong-cred";
|
||||
route_name_ = "not match";
|
||||
authorization_header_ =
|
||||
"Basic " + Base64::encode(cred_.data(), cred_.size());
|
||||
EXPECT_CALL(*mock_context_, sendLocalResponse(401, testing::_, testing::_,
|
||||
testing::_, testing::_));
|
||||
EXPECT_EQ(context_->onRequestHeaders(0, false),
|
||||
FilterHeadersStatus::StopIteration);
|
||||
|
||||
cred_ = "wrong-cred";
|
||||
route_name_ = "config";
|
||||
authorization_header_ =
|
||||
"Basic " + Base64::encode(cred_.data(), cred_.size());
|
||||
EXPECT_CALL(*mock_context_, sendLocalResponse(401, testing::_, testing::_,
|
||||
testing::_, testing::_));
|
||||
EXPECT_EQ(context_->onRequestHeaders(0, false),
|
||||
FilterHeadersStatus::StopIteration);
|
||||
|
||||
authority_ = "www.example.com";
|
||||
cred_ = "admin2:admin2";
|
||||
authorization_header_ =
|
||||
"Basic " + Base64::encode(cred_.data(), cred_.size());
|
||||
EXPECT_CALL(*mock_context_, sendLocalResponse(401, testing::_, testing::_,
|
||||
testing::_, testing::_));
|
||||
EXPECT_EQ(context_->onRequestHeaders(0, false),
|
||||
FilterHeadersStatus::StopIteration);
|
||||
|
||||
route_name_ = "config";
|
||||
cred_ = "admin4:admin4";
|
||||
authorization_header_ =
|
||||
"Basic " + Base64::encode(cred_.data(), cred_.size());
|
||||
EXPECT_CALL(*mock_context_, sendLocalResponse(401, testing::_, testing::_,
|
||||
testing::_, testing::_));
|
||||
EXPECT_EQ(context_->onRequestHeaders(0, false),
|
||||
FilterHeadersStatus::StopIteration);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(BasicAuthTest, OnConfigureNoRulesAuth) {
|
||||
// enable global auth
|
||||
{
|
||||
std::string configuration = R"(
|
||||
{
|
||||
"consumers" : [
|
||||
{"credential" : "getuser1:123456", "name" : "consumer1"}
|
||||
]
|
||||
})";
|
||||
BufferBase buffer;
|
||||
buffer.set({configuration.data(), configuration.size()});
|
||||
|
||||
EXPECT_CALL(*mock_context_, getBuffer(WasmBufferType::PluginConfiguration))
|
||||
.WillOnce([&buffer](WasmBufferType) { return &buffer; });
|
||||
EXPECT_TRUE(root_context_->configure(configuration.size()));
|
||||
cred_ = "admin:admin";
|
||||
authorization_header_ =
|
||||
"Basic " + Base64::encode(cred_.data(), cred_.size());
|
||||
EXPECT_CALL(*mock_context_, sendLocalResponse(401, testing::_, testing::_,
|
||||
testing::_, testing::_));
|
||||
EXPECT_EQ(context_->onRequestHeaders(0, false),
|
||||
FilterHeadersStatus::StopIteration);
|
||||
cred_ = "getuser1:123456";
|
||||
authorization_header_ =
|
||||
"Basic " + Base64::encode(cred_.data(), cred_.size());
|
||||
EXPECT_EQ(context_->onRequestHeaders(0, false),
|
||||
FilterHeadersStatus::Continue);
|
||||
}
|
||||
// disable global auth
|
||||
{
|
||||
std::string configuration = R"(
|
||||
{
|
||||
"consumers" : [
|
||||
{"credential" : "getuser1:123456", "name" : "consumer1"}
|
||||
],
|
||||
"global_auth": false
|
||||
})";
|
||||
BufferBase buffer;
|
||||
buffer.set({configuration.data(), configuration.size()});
|
||||
|
||||
EXPECT_CALL(*mock_context_, getBuffer(WasmBufferType::PluginConfiguration))
|
||||
.WillOnce([&buffer](WasmBufferType) { return &buffer; });
|
||||
EXPECT_TRUE(root_context_->configure(configuration.size()));
|
||||
cred_ = "admin:admin";
|
||||
authorization_header_ =
|
||||
"Basic " + Base64::encode(cred_.data(), cred_.size());
|
||||
EXPECT_EQ(context_->onRequestHeaders(0, false),
|
||||
FilterHeadersStatus::Continue);
|
||||
cred_ = "getuser1:123456";
|
||||
authorization_header_ =
|
||||
"Basic " + Base64::encode(cred_.data(), cred_.size());
|
||||
EXPECT_EQ(context_->onRequestHeaders(0, false),
|
||||
FilterHeadersStatus::Continue);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace basic_auth
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
# 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.
|
||||
|
||||
load("@proxy_wasm_cpp_sdk//bazel/wasm:wasm.bzl", "wasm_cc_binary")
|
||||
load("//bazel:wasm.bzl", "declare_wasm_image_targets")
|
||||
|
||||
@@ -11,10 +25,10 @@ wasm_cc_binary(
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_absl//absl/time",
|
||||
"//common:json_util",
|
||||
"@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics",
|
||||
"//common:http_util",
|
||||
"//common:regex_util",
|
||||
"//common:rule_util",
|
||||
"@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -31,9 +45,9 @@ cc_library(
|
||||
"@com_google_absl//absl/strings",
|
||||
"//common:json_util",
|
||||
"@proxy_wasm_cpp_host//:lib",
|
||||
"//common:http_util",
|
||||
"//common:http_util_nullvm",
|
||||
"//common:regex_util",
|
||||
"//common:rule_util",
|
||||
"//common:rule_util_nullvm",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -78,13 +78,13 @@ bool PluginRootContext::parsePluginConfig(const json& configuration,
|
||||
LOG_WARN("cannot parse allow");
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
rule.allow.push_back(
|
||||
std::make_unique<ReMatcher>(regex.first.value()));
|
||||
} catch (const std::runtime_error& e) {
|
||||
LOG_WARN(e.what());
|
||||
auto re = std::make_unique<ReMatcher>(regex.first.value());
|
||||
if (!re->error().empty()) {
|
||||
LOG_WARN(re->error());
|
||||
return false;
|
||||
}
|
||||
rule.allow.push_back(std::move(re));
|
||||
|
||||
return true;
|
||||
})) {
|
||||
LOG_WARN("failed to parse configuration for allow.");
|
||||
@@ -96,12 +96,13 @@ bool PluginRootContext::parsePluginConfig(const json& configuration,
|
||||
LOG_WARN("cannot parse deny");
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
rule.deny.push_back(std::make_unique<ReMatcher>(regex.first.value()));
|
||||
} catch (const std::runtime_error& e) {
|
||||
LOG_WARN(e.what());
|
||||
auto re = std::make_unique<ReMatcher>(regex.first.value());
|
||||
if (!re->error().empty()) {
|
||||
LOG_WARN(re->error());
|
||||
return false;
|
||||
}
|
||||
rule.deny.push_back(std::move(re));
|
||||
|
||||
return true;
|
||||
})) {
|
||||
LOG_WARN("failed to parse configuration for deny.");
|
||||
@@ -114,8 +115,7 @@ bool PluginRootContext::onConfigure(size_t size) {
|
||||
// Parse configuration JSON string.
|
||||
if (size > 0 && !configure(size)) {
|
||||
LOG_WARN("configuration has errors initialization will not continue.");
|
||||
setInvalidConfig();
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
if (size == 0) {
|
||||
// support empty config
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
# 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.
|
||||
|
||||
load("@proxy_wasm_cpp_sdk//bazel/wasm:wasm.bzl", "wasm_cc_binary")
|
||||
load("//bazel:wasm.bzl", "declare_wasm_image_targets")
|
||||
|
||||
@@ -11,9 +25,9 @@ wasm_cc_binary(
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_absl//absl/time",
|
||||
"//common:json_util",
|
||||
"@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics",
|
||||
"//common:http_util",
|
||||
"//common:rule_util",
|
||||
"@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -149,8 +149,7 @@ bool PluginRootContext::onConfigure(size_t size) {
|
||||
// Parse configuration JSON string.
|
||||
if (size > 0 && !configure(size)) {
|
||||
LOG_WARN("configuration has errors initialization will not continue.");
|
||||
setInvalidConfig();
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
# 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.
|
||||
|
||||
load("@proxy_wasm_cpp_sdk//bazel/wasm:wasm.bzl", "wasm_cc_binary")
|
||||
load("//bazel:wasm.bzl", "declare_wasm_image_targets")
|
||||
|
||||
@@ -13,10 +27,10 @@ wasm_cc_binary(
|
||||
"@com_google_absl//absl/strings:str_format",
|
||||
"@com_google_absl//absl/time",
|
||||
"//common:json_util",
|
||||
"@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics",
|
||||
"//common:crypto_util",
|
||||
"//common:http_util",
|
||||
"//common:rule_util",
|
||||
"@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -37,8 +51,8 @@ cc_library(
|
||||
"//common:json_util",
|
||||
"@proxy_wasm_cpp_host//:lib",
|
||||
"//common:crypto_util",
|
||||
"//common:http_util",
|
||||
"//common:rule_util",
|
||||
"//common:http_util_nullvm",
|
||||
"//common:rule_util_nullvm",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
286
plugins/wasm-cpp/extensions/hmac_auth/README_EN.md
Normal file
286
plugins/wasm-cpp/extensions/hmac_auth/README_EN.md
Normal file
@@ -0,0 +1,286 @@
|
||||
# Function Description
|
||||
The `hmac-auth` plugin implements the generation of tamper-proof signatures for HTTP requests based on HMAC algorithm, and uses the signature for identity authentication and authorization.
|
||||
|
||||
# Configuration Fields
|
||||
|
||||
| Name | Data Type | Required | Default | Description |
|
||||
| ------------- | --------------- | -------------| ------ | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `consumers` | array of object | Required | - | Configures the caller of the service to authenticate the request. |
|
||||
| `date_offset` | number | Optional | - | Configures the maximum allowed time deviation of the client, in seconds. It is used to parse the client's UTC time from `the Date` header of the request, and can be used to prevent replay attacks. If not configured, no validation is performed. |
|
||||
| `_rules_` | array of object | Optional | - | Configures the access control list for specific routes or domains, used for authorization of requests. |
|
||||
|
||||
The configuration fields for each item in `consumers` are as follows :
|
||||
|
||||
| Name | Data Type| Required | Default| Description |
|
||||
| -------- | -------- | ------------ | ------ | ----------------------------------------------------------------------- |
|
||||
| `key` | string | Required | - | Configures the key extracted from the `x-ca-key` header of the request. |
|
||||
| `secret` | string | Required | - | Configures the secret used to generate the signature. |
|
||||
| `name` | string | Required | - | Configures the name of the consumer. |
|
||||
|
||||
The configuration fields for each item in `_rules_` are as follows:
|
||||
|
||||
| Name | Data Type | Required | Default | Description |
|
||||
| ---------------- | --------------- | ------------------------------------------------- | ---------------------------- | -------------------------------------------------- |
|
||||
| `_match_route_` | array of string | Optional, either `_match_route_` or `_match_domain_` must be provided | - | Configures the name of the route to match. |
|
||||
| `_match_domain_` | array of string | Optional, either `_match_route_` or `_match_domain_` must be provided | - | Configures the name of the domain to match. |
|
||||
| `allow` | array of string | Required | - | Configures the name of the consumer to allow for requests that match the specified route or domain. |
|
||||
|
||||
**Note:**
|
||||
- If `_rules_` is not configured, authentication is enabled for all routes on the current gateway instance by default ;
|
||||
- For requests that pass authentication and authorization, a `X-Mse-Consumer` header will be added to the request headers to identify the name of the consumer.
|
||||
|
||||
# Configuration Example
|
||||
|
||||
The following configuration enables Hmac Auth authentication and authorization for specific routes or domains on the gateway. Note that the `key` field should not be duplicated.
|
||||
|
||||
## Enabling for specific routes or domains
|
||||
```yaml
|
||||
consumers:
|
||||
- key: appKey-example-1
|
||||
secret: appSecret-example-1
|
||||
name: consumer-1
|
||||
- key: appKey-example-2
|
||||
secret: appSecret-example-2
|
||||
name: consumer-2
|
||||
# Configuring Fine-Grained Rules using _rules_ Field
|
||||
_rules_:
|
||||
# Rule 1: Matching by route name.
|
||||
- _match_route_:
|
||||
- route-a
|
||||
- route-b
|
||||
allow:
|
||||
- consumer-1
|
||||
# Rule 2: Applies based on domain name matching.
|
||||
- _match_domain_:
|
||||
- "*.example.com"
|
||||
- test.com
|
||||
allow:
|
||||
- consumer-2
|
||||
```
|
||||
The `allow` field under each matching rule specifies the list of callers allowed to access under that matching condition;
|
||||
|
||||
In this example, `route-a` and `route-b` specified in `_match_route_` are the route names filled in when creating the gateway route. When either of these routes is matched, it will allow access to the caller named `consumer-1`, while denying access to other callers;
|
||||
|
||||
In` _match_domain_`, `*.example.com` and `test.com` are used to match the requested domain name. When a match is found, it will allow access to the caller named `consumer-2`, while denying access to other callers;
|
||||
|
||||
Upon successful authentication, the `X-Mse-Consumer` field will be added to the request header with the value set to the caller's name, such as `consumer-1`.。
|
||||
|
||||
## Enable at the Gateway Instance Level
|
||||
|
||||
The following configuration enables HMAC authentication at the gateway instance level.
|
||||
|
||||
```yaml
|
||||
consumers:
|
||||
- key: appKey-example-1
|
||||
secret: appSecret-example-1
|
||||
name: consumer-1
|
||||
- key: appKey-example-2
|
||||
secret: appSecret-example-2
|
||||
name: consumer-2
|
||||
```
|
||||
|
||||
|
||||
# Description of Signing Mechanism
|
||||
|
||||
## Configuration Preparation
|
||||
|
||||
As mentioned in the guide above, configure the credential settings required for generating and validating signatures in the plugin configuration.
|
||||
|
||||
- key: Used for setting in the request header `x-ca-key`.
|
||||
- secret: Used for generating the request signature.
|
||||
|
||||
## Client Signature Generation Method
|
||||
### Overview of the Process
|
||||
|
||||
The process for generating a signature on the client side consists of three steps:
|
||||
|
||||
1. Extracting key data from the original request to obtain a string to be signed.
|
||||
|
||||
2. Using encryption algorithms and the configured `secret` to encrypt the key data signing string and obtain a signature.
|
||||
|
||||
3. Adding all headers related to the signature to the original HTTP request to obtain the final HTTP request.
|
||||
|
||||
As shown below :
|
||||

|
||||
|
||||
### Process for Extracting Signing String
|
||||
|
||||
To generate a signature, the client needs to extract key data from the HTTP request and combine it into a signing string. The format of the generated signing string is as follows:
|
||||
|
||||
```text
|
||||
HTTPMethod
|
||||
Accept
|
||||
Content-MD5
|
||||
Content-Type
|
||||
Date
|
||||
Headers
|
||||
PathAndParameters
|
||||
```
|
||||
|
||||
The signing string consists of the above 7 fields separated by \n. If Headers is empty, no \n is needed. If other fields are empty, the \n should still be retained. The signature is case-sensitive. Below are the rules for extracting each field:
|
||||
|
||||
- HTTPMethod: The HTTP method used in the request, in all capital letters, such as POST.
|
||||
|
||||
- Accept: The value of the Accept header in the request, which can be empty. It is recommended to explicitly set the Accept header. When Accept is empty, some HTTP clients will set the default value of `*/*`, which may cause signature verification to fail.
|
||||
|
||||
- Content-MD5: The value of the Content-MD5 header in the request, which can be empty. It is only calculated when there is a non-form body in the request. The following is a reference calculation method for Content-MD5 values in :
|
||||
|
||||
|
||||
```java
|
||||
String content-MD5 = Base64.encodeBase64(MD5(bodyStream.getbytes("UTF-8")));
|
||||
```
|
||||
|
||||
- Content-Type: The value of the Content-Type header in the request, which can be empty.
|
||||
|
||||
- Date: The value of the Date header in the request. When the` date_offset` configuration is not enabled, it can be empty. Otherwise, it will be used for time offset verification.
|
||||
|
||||
- Headers: Users can select specific headers to participate in the signature. There are the following rules for concatenating the signature string with headers:
|
||||
- The keys of the headers participating in the signature calculation are sorted in alphabetical order and concatenated as follows:
|
||||
```text
|
||||
HeaderKey1 + ":" + HeaderValue1 + "\n"\+
|
||||
HeaderKey2 + ":" + HeaderValue2 + "\n"\+
|
||||
...
|
||||
HeaderKeyN + ":" + HeaderValueN + "\n"
|
||||
```
|
||||
- If the value of a header is empty, it will participate in the signature with the `HeaderKey+":"+"\n"` only, and the key and english colon should be retained.
|
||||
- The set of keys for all headers participating in the signature is separated by a comma and placed in the `X-Ca-Signature-Headers header`.
|
||||
- The following headers are not included in the header signature calculation: X-Ca-Signature, X-Ca-Signature-Headers, Accept, Content-MD5, Content-Type, Date.
|
||||
|
||||
- PathAndParameters: This field contains all parameters in the path, query, and form. The specific format is as follows:
|
||||
|
||||
```text
|
||||
Path + "?" + Key1 + "=" + Value1 + "&" + Key2 + "=" + Value2 + ... "&" + KeyN + "=" + ValueN
|
||||
```
|
||||
|
||||
Notes:
|
||||
1. The keys of the query and form parameter pairs are sorted alphabetically, and the same format as above is used for concatenation.
|
||||
|
||||
2. If there are no query and form parameters, use the path directly without adding `?` .
|
||||
|
||||
3. If the value of a parameter is empty, only the key will be included in the signature. The equal sign should not be included in the signature.
|
||||
|
||||
4. If there are array parameters in the query or form (parameters with the same key but different values), only the first value should be included in the signature calculation.
|
||||
|
||||
### Example of Extracting Signing String
|
||||
|
||||
The initial HTTP request :
|
||||
```text
|
||||
POST /http2test/test?param1=test HTTP/1.1
|
||||
host:api.aliyun.com
|
||||
accept:application/json; charset=utf-8
|
||||
ca_version:1
|
||||
content-type:application/x-www-form-urlencoded; charset=utf-8
|
||||
x-ca-timestamp:1525872629832
|
||||
date:Wed, 09 May 2018 13:30:29 GMT+00:00
|
||||
user-agent:ALIYUN-ANDROID-DEMO
|
||||
x-ca-nonce:c9f15cbf-f4ac-4a6c-b54d-f51abf4b5b44
|
||||
content-length:33
|
||||
username=xiaoming&password=123456789
|
||||
```
|
||||
|
||||
The correct generated signature string is :
|
||||
```text
|
||||
POST
|
||||
application/json; charset=utf-8
|
||||
application/x-www-form-urlencoded; charset=utf-8
|
||||
Wed, 09 May 2018 13:30:29 GMT+00:00
|
||||
x-ca-key:203753385
|
||||
x-ca-nonce:c9f15cbf-f4ac-4a6c-b54d-f51abf4b5b44
|
||||
x-ca-signature-method:HmacSHA256
|
||||
x-ca-timestamp:1525872629832
|
||||
/http2test/test?param1=test&password=123456789&username=xiaoming
|
||||
```
|
||||
|
||||
### Signature Calculation Process
|
||||
|
||||
After extracting the key data from the HTTP request and assembling it into a signature string, the client needs to encrypt and encode the signature string to form the final signature.
|
||||
|
||||
The specific encryption format is as follows, where `stringToSign` is the extracted signature string, `secret` is the one filled in the plugin configuration, and `sign` is the final generated signature:
|
||||
|
||||
```java
|
||||
Mac hmacSha256 = Mac.getInstance("HmacSHA256");
|
||||
byte[] secretBytes = secret.getBytes("UTF-8");
|
||||
hmacSha256.init(new SecretKeySpec(secretBytes, 0, secretBytes.length, "HmacSHA256"));
|
||||
byte[] result = hmacSha256.doFinal(stringToSign.getBytes("UTF-8"));
|
||||
String sign = Base64.encodeBase64String(result);
|
||||
```
|
||||
|
||||
In summary, the `stringToSign` is decoded using UTF-8 to obtain a Byte array. Then, an encryption algorithm is used to encrypt the Byte array, and finally, the Base64 algorithm is used to encode the encrypted data, resulting in the final signature.
|
||||
|
||||
### The Process of Adding a Signature
|
||||
|
||||
The client needs to include the following four headers in the HTTP request to be transmitted to the API gateway for signature verification:
|
||||
|
||||
- x-ca-key: The value is the APP Key and is required.
|
||||
|
||||
- x-ca-signature-method: The signature algorithm, the value can be HmacSHA256 or HmacSHA1, optional. The default value is HmacSHA256.
|
||||
|
||||
- x-ca-signature-headers: The collection of keys for all signature headers, separated by commas. Optional.
|
||||
|
||||
- x-ca-signature: The signature and it is required.
|
||||
|
||||
Here is an example of a complete HTTP request with a signature :
|
||||
|
||||
```text
|
||||
POST /http2test/test?param1=test HTTP/1.1
|
||||
host:api.aliyun.com
|
||||
accept:application/json; charset=utf-8
|
||||
ca_version:1
|
||||
content-type:application/x-www-form-urlencoded; charset=utf-8
|
||||
x-ca-timestamp:1525872629832
|
||||
date:Wed, 09 May 2018 13:30:29 GMT+00:00
|
||||
user-agent:ALIYUN-ANDROID-DEMO
|
||||
x-ca-nonce:c9f15cbf-f4ac-4a6c-b54d-f51abf4b5b44
|
||||
x-ca-key:203753385
|
||||
x-ca-signature-method:HmacSHA256
|
||||
x-ca-signature-headers:x-ca-timestamp,x-ca-key,x-ca-nonce,x-ca-signature-method
|
||||
x-ca-signature:xfX+bZxY2yl7EB/qdoDy9v/uscw3Nnj1pgoU+Bm6xdM=
|
||||
content-length:33
|
||||
username=xiaoming&password=123456789
|
||||
```
|
||||
|
||||
## Server-side Signature Verification Method
|
||||
|
||||
### Overview of the Process
|
||||
|
||||
The server-side signature verification of the client's request involves four steps :
|
||||
|
||||
1. Extract crucial data from the received request to obtain a string for signing.
|
||||
|
||||
2. Retrieve the `key` from the received request and use it to query its corresponding `secret`.
|
||||
|
||||
3. Encrypt the string for signing using the encryption algorithm and `secret`.
|
||||
|
||||
4. Retrieve the client's signature from the received request, and compare the consistency of the server-side signature with the client's signature.
|
||||
|
||||
As shown below :
|
||||

|
||||
|
||||
|
||||
## Troubleshooting Signature Errors
|
||||
|
||||
When the gateway signature verification fails, the server-side signing string (StringToSign) will be returned to the client in the HTTP Response Header. The key is X-Ca-Error-Message. Users only need to compare the locally calculated signing string with the server-side signing string returned to locate the problem;
|
||||
|
||||
If the StringToSign on the server side is consistent with that on the client side, please check whether the APP Secret used for signature calculation is correct;
|
||||
|
||||
Because line breaks cannot be represented in HTTP headers, all line breaks in the StringToSign are replaced with #, as shown below:
|
||||
|
||||
```text
|
||||
X-Ca-Error-Message: Server StringToSign:`GET#application/json##application/json##X-Ca-Key:200000#X-Ca-Timestamp:1589458000000#/app/v1/config/keys?keys=TEST`
|
||||
|
||||
```
|
||||
|
||||
# Related Error Codes
|
||||
|
||||
| HTTP Status Code | Error Message | Reason |
|
||||
| ----------- | ---------------------- | -------------------------------------------------------------------------------- |
|
||||
| 401 | Invalid Key | The x-ca-key request header is not provided or is invalid. |
|
||||
| 401 | Empty Signature | The x-ca-signature request header does not contain a signature. |
|
||||
| 400 | Invalid Signature | The x-ca-signature request header contains a signature that does not match the server-calculated signature. |
|
||||
| 400 | Invalid Content-MD5 | The content-md5 request header is incorrect. |
|
||||
| 400 | Invalid Date | The time offset calculated based on the date request header exceeds the configured date_offset. |
|
||||
| 413 | Request Body Too Large | The request body exceeds the size limit of 32 MB. |
|
||||
| 413 | Payload Too Large | The request body exceeds the DownstreamConnectionBufferLimits global configuration. |
|
||||
| 403 | Unauthorized Consumer | The requesting party does not have access permission. |
|
||||
|
||||
|
||||
@@ -389,8 +389,7 @@ bool PluginRootContext::onConfigure(size_t size) {
|
||||
// Parse configuration JSON string.
|
||||
if (size > 0 && !configure(size)) {
|
||||
LOG_WARN("configuration has errors initialization will not continue.");
|
||||
setInvalidConfig();
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
# 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.
|
||||
|
||||
load("@proxy_wasm_cpp_sdk//bazel/wasm:wasm.bzl", "wasm_cc_binary")
|
||||
load("//bazel:wasm.bzl", "declare_wasm_image_targets")
|
||||
|
||||
@@ -17,9 +31,9 @@ wasm_cc_binary(
|
||||
"@com_google_absl//absl/strings:str_format",
|
||||
"@com_google_absl//absl/time",
|
||||
"//common:json_util",
|
||||
"@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics",
|
||||
"//common:http_util",
|
||||
"//common:rule_util",
|
||||
"@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -135,11 +135,16 @@ bool PluginRootContext::parsePluginConfig(const json& configuration,
|
||||
[&](const json& from_header) -> bool {
|
||||
JSON_FIND_FIELD(from_header, name);
|
||||
JSON_FIELD_VALUE_AS(std::string, from_header, name);
|
||||
JSON_FIND_FIELD(from_header, value_prefix);
|
||||
JSON_FIELD_VALUE_AS(std::string, from_header,
|
||||
value_prefix);
|
||||
from_headers.push_back(FromHeader{
|
||||
from_header_name, from_header_value_prefix});
|
||||
std::string header_value_prefix;
|
||||
auto from_header_value_prefix_json =
|
||||
from_header.find("value_prefix");
|
||||
if (from_header_value_prefix_json != from_header.end()) {
|
||||
JSON_FIELD_VALUE_AS(std::string, from_header,
|
||||
value_prefix);
|
||||
header_value_prefix = from_header_value_prefix;
|
||||
}
|
||||
from_headers.push_back(
|
||||
FromHeader{from_header_name, header_value_prefix});
|
||||
return true;
|
||||
})) {
|
||||
LOG_WARN("failed to parse 'from_headers' in consumer: " +
|
||||
@@ -229,6 +234,18 @@ bool PluginRootContext::parsePluginConfig(const json& configuration,
|
||||
LOG_INFO("at least one consumer has to be configured for a rule.");
|
||||
return false;
|
||||
}
|
||||
std::vector<std::string> enable_headers;
|
||||
if (!JsonArrayIterate(configuration, "enable_headers",
|
||||
[&](const json& enable_header_json) -> bool {
|
||||
JSON_VALUE_AS(std::string, enable_header_json,
|
||||
enable_header, "invalid item");
|
||||
enable_headers.push_back(enable_header);
|
||||
return true;
|
||||
})) {
|
||||
LOG_WARN("failed to parse 'enable_headers'");
|
||||
return false;
|
||||
}
|
||||
rule.enable_headers = std::move(enable_headers);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -307,6 +324,20 @@ Status PluginRootContext::consumerVerify(
|
||||
bool PluginRootContext::checkPlugin(
|
||||
const JwtAuthConfigRule& rule,
|
||||
const std::optional<std::unordered_set<std::string>>& allow_set) {
|
||||
if (!rule.enable_headers.empty()) {
|
||||
bool skip_auth = true;
|
||||
for (const auto& enable_header : rule.enable_headers) {
|
||||
auto header_ptr = getRequestHeader(enable_header);
|
||||
if (header_ptr->size() > 0) {
|
||||
LOG_DEBUG("enable by header: " + header_ptr->toString());
|
||||
skip_auth = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (skip_auth) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
std::optional<Status> err_status;
|
||||
bool verified = false;
|
||||
uint64_t now = getCurrentTimeNanoseconds() / 1e9;
|
||||
@@ -354,8 +385,7 @@ bool PluginRootContext::onConfigure(size_t size) {
|
||||
// Parse configuration JSON string.
|
||||
if (size > 0 && !configure(size)) {
|
||||
LOG_WARN("configuration has errors initialization will not continue.");
|
||||
setInvalidConfig();
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -73,6 +73,7 @@ struct Consumer {
|
||||
|
||||
struct JwtAuthConfigRule {
|
||||
std::vector<Consumer> consumers;
|
||||
std::vector<std::string> enable_headers;
|
||||
};
|
||||
|
||||
// PluginRootContext is the root context for all streams processed by the
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user