diff --git a/README.md b/README.md index a40d10b1..eda6e6b6 100644 --- a/README.md +++ b/README.md @@ -31,11 +31,11 @@ In order to solving the current shortcomings of Istio, we make Slime project. It Slime adopts a modular architecture inside. It contains three useful modules now. -[Configuration Lazy Loading](https://github.com/slime-io/lazyload): No need to configure SidecarScope, automatically load configuration on demand, solving full push problem. The source of the service relationship supports Prometheus or Accesslog. +[Configuration Lazy Loading](./staging/src/slime.io/slime/modules/lazyload): No need to configure SidecarScope, automatically load configuration on demand, solving full push problem. The source of the service relationship supports Prometheus or Accesslog. -[Http Plugin Management](https://github.com/slime-io/plugin): Use the new CRD pluginmanager/envoyplugin to wrap readability , The poor maintainability of envoyfilter makes plug-in extension more convenient. +[Http Plugin Management](./staging/src/slime.io/slime/modules/plugin): Use the new CRD pluginmanager/envoyplugin to wrap readability , The poor maintainability of envoyfilter makes plug-in extension more convenient. -[Adaptive Ratelimit](https://github.com/slime-io/limiter): It can be automatically combined with adaptive ratelimit strategy based on metrics, solving rate limit problem. +[Adaptive Ratelimit](./staging/src/slime.io/slime/modules/limiter): It can be automatically combined with adaptive ratelimit strategy based on metrics, solving rate limit problem. @@ -65,9 +65,9 @@ Slime supports aggregated packaging, allowing any module to be aggregated into a Slime-module -- [Lazyload Usage](https://github.com/slime-io/lazyload/blob/master/README.md) -- [PluginManager Usage](https://github.com/slime-io/plugin/blob/master/README.md) -- [SmartLimiter Usage](https://github.com/slime-io/limiter/blob/master/README.md) +- [Lazyload Usage](./staging/src/slime.io/slime/modules/lazyload/README.md) +- [PluginManager Usage](./staging/src/slime.io/slime/modules/plugin/README.md) +- [SmartLimiter Usage](./staging/src/slime.io/slime/modules/limiter/README.md) [E2E Test Tutorials](./doc/en/slime_e2e_test.md) diff --git a/README_ZH.md b/README_ZH.md index d7ed38c2..292af3fe 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -32,11 +32,11 @@ Istio 可以实现版本分流、灰度发布、负载均衡等功能,但是 Slime 内部采用了模块化的架构。目前包含了三个非常实用的子模块。 -[配置懒加载](https://github.com/slime-io/lazyload):无须配置SidecarScope,自动按需加载配置和服务发现信息 ,解决了全量推送的问题。服务调用关系的来源支持Prometheus或者Accesslog。 +[配置懒加载](./staging/src/slime.io/slime/modules/lazyload):无须配置SidecarScope,自动按需加载配置和服务发现信息 ,解决了全量推送的问题。服务调用关系的来源支持Prometheus或者Accesslog。 -[Http插件管理](https://github.com/slime-io/plugin):使用新的的CRD pluginmanager/envoyplugin包装了可读性及可维护性差的envoyfilter,使得插件扩展更为便捷。 +[Http插件管理](./staging/src/slime.io/slime/modules/plugin):使用新的的CRD pluginmanager/envoyplugin包装了可读性及可维护性差的envoyfilter,使得插件扩展更为便捷。 -[自适应限流](https://github.com/slime-io/limiter):实现了本地限流,同时可以结合监控信息自动调整限流策略,填补了 Istio 限流功能的短板。 +[自适应限流](./staging/src/slime.io/slime/modules/limiter):实现了本地限流,同时可以结合监控信息自动调整限流策略,填补了 Istio 限流功能的短板。 后续我们会开源更多的功能模块。 @@ -67,9 +67,9 @@ Slime支持聚合打包,可以将任意模块聚合成一个镜像。所以, Slime-module -- [懒加载使用](https://github.com/slime-io/lazyload/blob/master/README_zh.md) -- [插件管理使用](https://github.com/slime-io/plugin/blob/master/README_zh.md) -- [自适应限流使用](https://github.com/slime-io/limiter/blob/master/README_ZH.md) +- [懒加载使用](./staging/src/slime.io/slime/modules/lazyload/README_zh.md) +- [插件管理使用](./staging/src/slime.io/slime/modules/plugin/README_zh.md) +- [自适应限流使用](./staging/src/slime.io/slime/modules/limiter/README_ZH.md) [E2E测试教程](./doc/zh/slime_e2e_test_zh.md) diff --git a/boot/publish.sh b/boot/publish.sh index 55d9f36d..e80c1d7d 100644 --- a/boot/publish.sh +++ b/boot/publish.sh @@ -3,7 +3,7 @@ MODS=${MODS:-"lazyload limiter plugin"} for m in $MODS; do rm -rf "./helm-charts/slimeboot/templates/modules/$m" - cp -r "../../$m/charts/" "./helm-charts/slimeboot/templates/modules/$m" + cp -r "../staging/src/slime.io/slime/modules/$m/charts/" "./helm-charts/slimeboot/templates/modules/$m" done export MOD=boot diff --git a/doc/zh/slime_e2e_test_zh.md b/doc/zh/slime_e2e_test_zh.md index de942680..75f5b461 100755 --- a/doc/zh/slime_e2e_test_zh.md +++ b/doc/zh/slime_e2e_test_zh.md @@ -19,7 +19,7 @@ 为了更好的进行模块化测试,Slime引入了E2E(End to End)测试框架。主要函数实现参考了Kubernetes的test/e2e。由于Kubernetes的E2E测试太过庞大,我们摘取了一个子集,满足基本需求。 -测试框架位于[Slime项目](https://github.com/slime-io/slime)的slime-framework/test/e2e路径下,测试文件则位于各个modules项目中。以[lazyload项目](https://github.com/slime-io/lazyload)为例,测试文件路径为test/e2e/lazyload_test.go。 +测试框架位于[Slime项目](https://github.com/slime-io/slime)的slime-framework/test/e2e路径下,测试文件则位于各个modules项目中。以[lazyload项目](../../staging/src/slime.io/slime/modules/lazyload)为例,测试文件路径为test/e2e/lazyload_test.go。 diff --git a/modules/bundle_example/go.mod b/modules/bundle_example/go.mod index e943cc86..8fad6d32 100644 --- a/modules/bundle_example/go.mod +++ b/modules/bundle_example/go.mod @@ -14,7 +14,7 @@ replace ( k8s.io/apimachinery => k8s.io/apimachinery v0.17.2 slime.io/slime/framework => ../../framework - slime.io/slime/modules/lazyload => ../../../lazyload - slime.io/slime/modules/limiter => ../../../limiter - slime.io/slime/modules/plugin => ../../../plugin + slime.io/slime/modules/lazyload => ../../staging/src/slime.io/slime/modules/lazyload + slime.io/slime/modules/limiter => ../../staging/src/slime.io/slime/modules/limiter + slime.io/slime/modules/plugin => ../../staging/src/slime.io/slime/modules/plugin ) diff --git a/modules/example/go.mod b/modules/example/go.mod index 3afbb34e..7583fd1c 100644 --- a/modules/example/go.mod +++ b/modules/example/go.mod @@ -3,13 +3,9 @@ module slime.io/slime/modules/example go 1.13 require ( - github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad github.com/gogo/protobuf v1.3.2 github.com/golang/protobuf v1.4.2 - github.com/onsi/ginkgo v1.11.0 - github.com/onsi/gomega v1.8.1 github.com/sirupsen/logrus v1.4.2 - istio.io/api v0.0.0-20210322145030-ec7ef4cd6eaf k8s.io/apimachinery v0.20.2 k8s.io/client-go v0.17.2 sigs.k8s.io/controller-runtime v0.5.0 @@ -20,6 +16,5 @@ replace ( k8s.io/api => k8s.io/api v0.17.2 k8s.io/apimachinery => k8s.io/apimachinery v0.17.2 - slime.io/slime/framework v0.0.0 => github.com/slime-io/slime/framework v0.3.7 - //slime.io/slime/framework => ../slime/framework + slime.io/slime/framework => ../../framework ) diff --git a/modules/example/go.sum b/modules/example/go.sum index 1ad6212b..beb737a9 100644 --- a/modules/example/go.sum +++ b/modules/example/go.sum @@ -1,29 +1,18 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs= github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= -github.com/Azure/go-autorest/autorest/adal v0.5.0 h1:q2gDruN08/guU9vAjuPWff0+QIrpH6ediguzdAzXAUU= github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= -github.com/Azure/go-autorest/autorest/date v0.1.0 h1:YGrhWfrgtFs84+h0o46rJrlmsZtyZRg470CqAXTZaGM= github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.2.0 h1:Ww5g4zThfD/6cLb4z6xxgeyDa7QDkizMkJKe0ysZXp0= github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY= github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= -github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k= github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd h1:sjQovDkwrZp8u+gxLtPgKGjk5hCxuy2hrRejBTA9xFU= github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46 h1:lsxEuwrXEAokXB9qhlbKWPpo3KMLZQ5WB5WLQRW1uq0= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= @@ -32,96 +21,61 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/agnivade/levenshtein v1.0.1 h1:3oJU7J3FGFmyhn8KHjmVaZCN5hxTr7GxgRue+sxIXdQ= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 h1:G1bPvciwNyF7IUmKXNt9Ak3m6u9DE1rF+RmtIkBpVdA= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/blang/semver v3.5.0+incompatible h1:CGxCgetQ64DKk7rdZ++Vfnb1+ogGNnB17OJKJXD2Cfs= github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 h1:7aWHqerlJ41y6FOsEUvknqgXnGmJyJSbjhAWq5pO4F8= github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= -github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 h1:cqQfy1jclcSy/FwLjemeg3SR1yaINm74aQyupQ0Bl8M= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-etcd v2.0.0+incompatible h1:bXhRBIXoTm9BYHS3gE0TtQuyNZyeEMux2sDi4oo5YOo= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= -github.com/coreos/go-oidc v2.1.0+incompatible h1:sdJrfw8akMnCuUlaZU3tE/uYXFgfqom8DBE9so9EBsM= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea h1:n2Ltr3SrfQlf/9nOna1DoGKxLx3qTSI8Ttl6Xrqp6mw= github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= -github.com/creack/pty v1.1.7 h1:6pwm8kMQKCmgUg0ZHTm5+/YvRK0s3THD/28+T6/kk4A= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd h1:uVsMphB1eRx7xB1njzL3fuMdWRN8HtVzoUOItHMwv5c= github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0 h1:w3NnFcKR5241cfmQU5ZZAsf0xcpId6mWOupTvJlUX2U= github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QLSV/BsnenAOcDXdX4cMv4wP0B/5QbPg= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e h1:p1yVGRW3nmb85p1Sh1ZJSDm4A4iKLS5QNbvUHMgGu/M= github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad h1:EmNYJhPYy0pOFjCx2PrgtaBXmee0iUX9hLlxE1xHOJE= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M= github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= -github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= -github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -129,11 +83,8 @@ github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2H github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2ic= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= @@ -143,11 +94,9 @@ github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70t github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= -github.com/go-openapi/analysis v0.19.5 h1:8b2ZgKfKIUTVQpTb77MoRDIMEIwvDVw40o3aOXdfYzI= github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/errors v0.19.2 h1:a2kIyV3w+OS3S97zxUndRVD46+FhGOUBDFY7nmu4CsY= github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= @@ -165,11 +114,9 @@ github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= -github.com/go-openapi/loads v0.19.4 h1:5I4CCSqoWzT+82bBkNIvmLc0UOsoKKQ4Fz+3VxOB7SY= github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk= github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= -github.com/go-openapi/runtime v0.19.4 h1:csnOgcgAiuGoM/Po7PEpKDoNulCcF3FGbSnbHfxgjMI= github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= @@ -180,7 +127,6 @@ github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8 github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= -github.com/go-openapi/strfmt v0.19.3 h1:eRfyY5SkaNJCAwmmMcADjY31ow9+N7MCLW7oRkbsINA= github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= @@ -190,22 +136,18 @@ github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tF github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= -github.com/go-openapi/validate v0.19.5 h1:QhCBKRYqZR+SKo4gl1lPhPahope8/RLt6EVgY8X80w0= github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= -github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7 h1:u4bArs140e9+AfE52mFHOXVFnOSBJBRlzTHrOPLOIhE= github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -219,14 +161,10 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450 h1:7xqw01UYS+KCI25bMrPxwNYkSns2Db1ziQPpVq99FpE= github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho= -github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995 h1:f5gsjBiF9tRRVomCvrkGMMWI8W1f2OBFar2c5oakAP0= github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995/go.mod h1:lJgMEyOkYFkPcDKwRXegd+iM6E7matEszMG5HhwytU8= -github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e h1:KhcknUwkWHKZPbFy2P7jH5LKJ3La+0ZeknkkmrSgqb0= github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -237,31 +175,22 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57 h1:eqyIo2HjKhKe/mJzTG8n4VqvLXIOEG+SLdDqX7xGtkY= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4 h1:hU4mGcQI4DaAYW+IbTun+2qEZVFxK0ySjQLTbS0VQKc= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk= github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= -github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 h1:z53tR0945TRRQO/fLEVPI6SMv7ZflF0TEaTAoU7tOzg= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.5 h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1QAp/SlnNrZhI= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -270,48 +199,36 @@ github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024 h1:rBMNdlhTLzJjJSDIjNEXX1Pz3Hmwmz91v+zycvx9PJc= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= -github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.5 h1:hyz3dwM5QLc1Rfoz4FuWJQG5BN7tc6K1MndAUnGpQr4= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= -github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY= github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= -github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -320,19 +237,13 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-runewidth v0.0.2 h1:UnlwIPBGaTZfPQ6T1IGzPI0EkYAQmT9fAEJ/poFC63o= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= @@ -342,13 +253,9 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223 h1:F9x/1yl3T2AeKLr2AMdilSD8+f9bvMnNN8VS5iDtovc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5 h1:58+kh9C6jJVXYjt8IE48G2eWl6BjwU5Gj0gqY84fy78= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -359,15 +266,11 @@ github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGV github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.8.1 h1:C5Dqfs/LeauYDX0jJXIe2SWmwCbGzx9yF8C8xy3Lh34= github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= -github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/orcaman/concurrent-map v0.0.0-20210106121528-16402b402231 h1:fa50YL1pzKW+1SsBnJDOHppJN9stOEwS+CRWyUtyYGU= github.com/orcaman/concurrent-map v0.0.0-20210106121528-16402b402231/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= -github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= @@ -375,7 +278,6 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021 h1:0XM1XL/OFFJjXsYXlG30spTkV/E9+gmd5GD1w2HE8xM= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM= @@ -389,41 +291,28 @@ github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446 h1:/NRJ5vAYoqz+7sG51ubIDHXeWO8DlTSrToPu6q11ziA= github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af h1:gu+uRPtBe88sKxUCEXRoeCvVG90TJmwhiqRpvdhQFng= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/slime-io/slime/framework v0.3.2 h1:fnEshZEwvJ79ZErGt/WPF3BlvYOfHAxrgWGCL3cB1JI= -github.com/slime-io/slime/framework v0.3.2/go.mod h1:Arv+tpfmdWWzxEBYHnhj9FZEgcLH+mYo5KumxMKrHqg= -github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -431,34 +320,21 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8 h1:ndzgwNDnKIqyCvHTXaCqh9KlOWKvBry6nuXMJmonVsE= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8 h1:3SVOIvH7Ae1KRYyQWRjXWJEA9sS/c/pjvH++55Gr648= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/vektah/gqlparser v1.1.2 h1:ZsyLGn7/7jDNI+y4SEhI4yAxRChlv15pUHMjijT+e68= github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1 h1:j2hhcujLRHAg872RWAV5yaUrEjHEObwDv3aImCaNLek= github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 h1:ESFSdwYZvkeru3RtdrYueztKhOBCSAAzS4Gf+k0tEow= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1 h1:ruQGxdhGHe7FWOJPT0mKs5+pD2Xs1Bm/kdGlHO04FmM= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738 h1:VcrIfasaLFkyjk6KNlXQSzO+B0fZcnECiDrKJsfxka0= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.mongodb.org/mongo-driver v1.1.2 h1:jxcFYjlkl8xaERsgLo+RNquI0epW6zuy/ZRQs6jnrFA= go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -479,19 +355,14 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnk golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495 h1:I6A9Ag9FpEKOjcKrRNjQkPHawoXIhKyTGfvvjFAiiAk= golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067 h1:KYGJGHOQy8oSi1fDlSpcZF0+juKwk/hEMv5SiwHogR0= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 h1:XQyxROzUlZH+WIQwySDgnISgOivlhjIEwaQaJEJrrN0= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6 h1:Tus/Y4w3V77xDsGwKUC8a/QrV7jScpU557J77lFffNs= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -523,7 +394,6 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -569,7 +439,6 @@ golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a h1:CB3a9Nez8M13wwlr/E2YtwoU+qYHKfC+JrDa45RXXoQ= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -578,12 +447,9 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1N golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.0.1 h1:xyiBuvkD2g5n7cYzx6u2sxQvsAy4QJsZFCzGVdzOXZ0= gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= -gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485 h1:OB/uP/Puiu5vS5QMRPrXCDWUPb+kt8f1KW8oQzFejQw= gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e h1:jRyg0XfpwWlhEV8mDfdNGBeSJM2fuyh9Yjrnd8kF2Ts= gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= -google.golang.org/api v0.4.0 h1:KKgc1aqhV8wDPbDzlDtpvyjZFY3vjz85FP7p4wcQUyI= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -594,14 +460,12 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.35.0 h1:TwIQcH3es+MojMVojxxfQ3l3OF2KzlRxML2xZq0kRo8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -613,12 +477,10 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/cheggaaa/pb.v1 v1.0.25 h1:Ev7yu1/f6+d+b3pi5vPdRPc6nNtP1umSfcWiEfRqv6I= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= @@ -626,9 +488,7 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.2.2 h1:orlkJ3myw8CN1nVQHBFfloD+L3egixIa4FvUP6RosSA= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= @@ -638,15 +498,11 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc h1:/hemPrYIhOhy8zYrNj+069zDB68us2sMGsfkFJO0iZs= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -istio.io/api v0.0.0-20210322145030-ec7ef4cd6eaf h1:wAkMg4dDRWkas2Gzkln5926OvHw3g/XbKsjICF/msQw= istio.io/api v0.0.0-20210322145030-ec7ef4cd6eaf/go.mod h1:nsSFw1LIMmGL7r/+6fJI6FxeG/UGlLxRK8bkojIvBVs= -istio.io/gogo-genproto v0.0.0-20210113155706-4daf5697332f h1:9710FpGLvIJ1GGEbpuTh1smVBv+r8cJfR3G82ouSxIQ= istio.io/gogo-genproto v0.0.0-20210113155706-4daf5697332f/go.mod h1:6BwTZRNbWS570wHX/uR1Wqk5e0157TofTAUMzT7N4+s= k8s.io/api v0.17.2 h1:NF1UFXcKN7/OOv1uxdRz3qfra8AHsPav5M93hlV9+Dc= k8s.io/api v0.17.2/go.mod h1:BS9fjjLc4CMuqfSO8vgbHPKMt5+SF0ET6u/RVDihTo4= @@ -654,18 +510,13 @@ k8s.io/apiextensions-apiserver v0.17.2 h1:cP579D2hSZNuO/rZj9XFRzwJNYb41DbNANJb6K k8s.io/apiextensions-apiserver v0.17.2/go.mod h1:4KdMpjkEjjDI2pPfBA15OscyNldHWdBCfsWMDWAmSTs= k8s.io/apimachinery v0.17.2 h1:hwDQQFbdRlpnnsR64Asdi55GyCaIP/3WQpMmbNBeWr4= k8s.io/apimachinery v0.17.2/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= -k8s.io/apiserver v0.17.2 h1:NssVvPALll6SSeNgo1Wk1h2myU1UHNwmhxV0Oxbcl8Y= k8s.io/apiserver v0.17.2/go.mod h1:lBmw/TtQdtxvrTk0e2cgtOxHizXI+d0mmGQURIHQZlo= -k8s.io/cli-runtime v0.17.2 h1:YH4txSplyGudvxjhAJeHEtXc7Tr/16clKGfN076ydGk= k8s.io/cli-runtime v0.17.2/go.mod h1:aa8t9ziyQdbkuizkNLAw3qe3srSyWh9zlSB7zTqRNPI= k8s.io/client-go v0.17.2 h1:ndIfkfXEGrNhLIgkr0+qhRguSD3u6DCmonepn1O6NYc= k8s.io/client-go v0.17.2/go.mod h1:QAzRgsa0C2xl4/eVpeVAZMvikCn8Nm81yqVx3Kk9XYI= -k8s.io/code-generator v0.17.2 h1:pTwl3rLB1fUyxmvEzmVPMM0tBSdUehd7z+bDzpj4lPE= k8s.io/code-generator v0.17.2/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s= -k8s.io/component-base v0.17.2 h1:0XHf+cerTvL9I5Xwn9v+0jmqzGAZI7zNydv4tL6Cw6A= k8s.io/component-base v0.17.2/go.mod h1:zMPW3g5aH7cHJpKYQ/ZsGMcgbsA/VyhEugF3QT1awLs= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20190822140433-26a664648505 h1:ZY6yclUKVbZ+SdWnkfY+Je5vrMpKOxmGeKRbsXVmqYM= k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= @@ -673,30 +524,20 @@ k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLyy7rfbeuf1PYyBf973pgyU= k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= -k8s.io/kubectl v0.17.2 h1:QZR8Q6lWiVRjwKslekdbN5WPMp53dS/17j5e+oi5XVU= k8s.io/kubectl v0.17.2/go.mod h1:y4rfLV0n6aPmvbRCqZQjvOp3ezxsFgpqL+zF5jH/lxk= -k8s.io/metrics v0.17.2 h1:cuN1ScyUS9/tj4YFI8d0/7yO0BveFHhyQpPNWS8uLr8= k8s.io/metrics v0.17.2/go.mod h1:3TkNHET4ROd+NfzNxkjoVfQ0Ob4iZnaHmSEA4vYpwLw= k8s.io/utils v0.0.0-20191114184206-e782cd3c129f h1:GiPwtSzdP43eI1hpPCbROQCCIgCuiMMNF8YUVLF3vJo= k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -modernc.org/cc v1.0.0 h1:nPibNuDEx6tvYrUAtvDTTw98rx5juGsa5zuDnKwEEQQ= modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= -modernc.org/golex v1.0.0 h1:wWpDlbK8ejRfSyi0frMyhilD3JBvtcx2AdGDnU+JtsE= modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= -modernc.org/mathutil v1.0.0 h1:93vKjrJopTPrtTNpZ8XIovER7iCIH1QU7wNbOQXC60I= modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= -modernc.org/strutil v1.0.0 h1:XVFtQwFVwc02Wk+0L/Z/zDDXO81r5Lhe6iMKmGX3KhE= modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= -modernc.org/xc v1.0.0 h1:7ccXrupWZIS3twbUGrtKmHS2DXY6xegFua+6O3xgAFU= modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= sigs.k8s.io/controller-runtime v0.5.0 h1:CbqIy5fbUX+4E9bpnBFd204YAzRYlM9SWW77BbrcDQo= sigs.k8s.io/controller-runtime v0.5.0/go.mod h1:REiJzC7Y00U+2YkMbT8wxgrsX5USpXKGhb2sCtAXiT8= -sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0= sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= -sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06 h1:zD2IemQ4LmOcAumeiyDWXKUI2SO0NYDe3H6QGvPOVgU= sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc h1:MksmcCZQWAQJCTA5T0jgI/0sJ51AVm4Z41MrmfczEoc= vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI= diff --git a/modules/example/module/module.go b/modules/example/module/module.go index ab8b2443..157b9a7a 100644 --- a/modules/example/module/module.go +++ b/modules/example/module/module.go @@ -23,15 +23,15 @@ type Module struct { config v1alpha1.General } -func (m *Module) Name() string { +func (mo *Module) Kind() string { return model.ModuleName } -func (m *Module) Config() proto.Message { - return &m.config +func (mo *Module) Config() proto.Message { + return &mo.config } -func (m *Module) InitScheme(scheme *runtime.Scheme) error { +func (mo *Module) InitScheme(scheme *runtime.Scheme) error { for _, f := range []func(*runtime.Scheme) error{ clientgoscheme.AddToScheme, v1alpha1.AddToScheme, @@ -44,8 +44,13 @@ func (m *Module) InitScheme(scheme *runtime.Scheme) error { return nil } -func (m *Module) InitManager(mgr manager.Manager, env bootstrap.Environment, cbs module.InitCallbacks) error { - cfg := &m.config +func (mo *Module) Clone() module.Module { + ret := *mo + return &ret +} + +func (mo *Module) InitManager(mgr manager.Manager, env bootstrap.Environment, cbs module.InitCallbacks) error { + cfg := &mo.config var err error if err = (&controllers.ExampleReconciler{ diff --git a/staging/src/slime.io/slime/modules/lazyload/.idea/.gitignore b/staging/src/slime.io/slime/modules/lazyload/.idea/.gitignore new file mode 100644 index 00000000..e69de29b diff --git a/staging/src/slime.io/slime/modules/lazyload/.idea/lazyload.iml b/staging/src/slime.io/slime/modules/lazyload/.idea/lazyload.iml new file mode 100644 index 00000000..5e764c4f --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/.idea/lazyload.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/staging/src/slime.io/slime/modules/lazyload/.idea/modules.xml b/staging/src/slime.io/slime/modules/lazyload/.idea/modules.xml new file mode 100644 index 00000000..27ff402e --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/staging/src/slime.io/slime/modules/lazyload/.idea/vcs.xml b/staging/src/slime.io/slime/modules/lazyload/.idea/vcs.xml new file mode 100644 index 00000000..94a25f7f --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/staging/src/slime.io/slime/modules/lazyload/.idea/workspace.xml b/staging/src/slime.io/slime/modules/lazyload/.idea/workspace.xml new file mode 100644 index 00000000..b7fee4d8 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/.idea/workspace.xml @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + + + + + + file:///usr/local/go/src/reflect/value.go + 291 + + + + + \ No newline at end of file diff --git a/staging/src/slime.io/slime/modules/lazyload/Dockerfile b/staging/src/slime.io/slime/modules/lazyload/Dockerfile new file mode 100644 index 00000000..65546b6a --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/Dockerfile @@ -0,0 +1,7 @@ +# Use distroless as minimal base image to package the manager binary +# Refer to https://github.com/GoogleContainerTools/distroless for more details +FROM ubuntu:bionic +WORKDIR / +COPY manager.exe ./manager + +ENTRYPOINT ["/manager"] diff --git a/staging/src/slime.io/slime/modules/lazyload/PROJECT b/staging/src/slime.io/slime/modules/lazyload/PROJECT new file mode 100644 index 00000000..a14d5c74 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/PROJECT @@ -0,0 +1,7 @@ +domain: my.domain +repo: slime.io/slime +resources: +- group: microservice.slime.io + kind: Servicefence + version: v1alpha1 +version: "2" diff --git a/staging/src/slime.io/slime/modules/lazyload/README.md b/staging/src/slime.io/slime/modules/lazyload/README.md new file mode 100644 index 00000000..e6749cef --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/README.md @@ -0,0 +1,116 @@ +- [Lazyload Overview](#lazyload-overview) + - [Features](#features) + - [Backgroud](#backgroud) + - [Thinking](#thinking) + - [Architecture](#architecture) + - [Install & Use](#install--use) + - [Introduction of features](#introduction-of-features) + - [Example](#example) + - [E2E Test Introduction](#e2e-test-introduction) + - [ServiceFence Instruction](#servicefence-instruction) + - [FAQ](#faq) + +# Lazyload Overview + +[中文](./README_zh.md) + +## Features + +1. Supports 1.8+ versions of Istio, without invasiveness, details at [recommended versions of istio](https://github.com/slime-io/slime/issues/145) +2. all namespaces in mesh can use Lazyload +3. Forwarding process supports all Istio traffic capabilities +4. Independent of the number of services, no performance issues +5. Support for enabling lazyload for services manually or automatically +6. Support for dynamic service dependency acquisition methods such as Accesslog and Prometheus +7. Support for adding static service dependencies, a combination of dynamic and static dependencies, comprehensive functionality + + + + + +## Backgroud + +When there are too many services in cluster, envoy configuration is too much, and the new application will be in NotReady state for a long time. Configuring Custom Resource `Sidecar` for new application and automatically fetching the service dependencies and updating Sidecar can solve this problem. + + + +## Thinking + +Introduce a service `global-sidecar`, which is used for request last matching. It will be injected sidecar container by Istiod. with a full configuration and service discovery information. The pass through route is replaced with a new one to the global-sidecar. + +Bring new Custom Resource `ServiceFence`. Details at [ServiceFence Instruction](#ServiceFence-Instruction) + +Finally, the control logic is included in the lazyload controller component. It will create ServiceFence and Sidecar for the lazyload enabled services, and update ServiceFence and Sidecar based on the service invocation relationship obtained from the configuration. + + + +## Architecture + +The module consists of a Lazyload controller, which does not need to inject a sidecar, and a global-sidecar, which does. + + + + + +Details at [Architecture](./lazyload_tutorials.md#Architecture) + + + + + +## Install & Use + +- Depending on the deployment mode of global-sidecar, the module is currently divided into two modes. + - Clusrter mode: Using global-sidecar at the cluster level: the only global-sidecar application in the cluster + + - Namespace mode: global-sidecar at namespace level: one global-sidecar application per namespace using lazyload +2. Depending on the source of the service dependency metrics, the module is divided into two modes. + + - Accesslog mode: global-sidecar generates an accesslog with service dependencies through incoming traffic interception + - Prometheus mode: the business application generates a metric after completing an access, which is reported to prometheus; this mode requires the cluster to interface with prometheus + +In summary, there are four usage modes for the Lazyload module, with the cluster+accesslog mode being the most recommended. + +Details at [Install&Use](./lazyload_tutorials.md#install-and-use) + + + +## Introduction of features +- Support for using lazyload based on accesslog +- Support for enabling lazyload for services manually or automatically +- Custom undefined traffic dispatch +- Support for adding static service dependencies +- Support for custom service dependency aliases +- Log output to local file and rotate + +Details at [Introduction of features](./lazyload_tutorials.md#Introduction-of-features) + + + +## Example + +Details at [Example](./lazyload_tutorials.md#Example) + + + +## E2E Test Introduction + +When developmenting, the module function can be verified correctly through E2E testing. + +Details at [E2E Test Tutorials](https://github.com/slime-io/slime/blob/master/doc/en/slime_e2e_test.md) + + + +## ServiceFence Instruction + +ServiceFence can be seen as a Sidecar resource for a service. The difference is that ServiceFence not only generates Sidecar resources based on dependencies, but also determines the real backend of the service based on VirtualService rules and automatically expands the scope of Fence. + +For example, c.default.svc.cluster.local is in servicefence. Now a route has a host of c.default.svc.cluster.local, the destination chagnes to d.default.svc.cluster.local. Then service d will be included in servicefence. + +服务围栏 + + + +## FAQ + +详见 [FAQ](./lazyload_tutorials.md#FAQ) diff --git a/staging/src/slime.io/slime/modules/lazyload/README_zh.md b/staging/src/slime.io/slime/modules/lazyload/README_zh.md new file mode 100644 index 00000000..7caf048d --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/README_zh.md @@ -0,0 +1,121 @@ +- [懒加载概述](#懒加载概述) + - [特点](#特点) + - [背景](#背景) + - [思路](#思路) + - [架构](#架构) + - [安装和使用](#安装和使用) + - [特性介绍](#特性介绍) + - [完整使用样例](#完整使用样例) + - [E2E测试介绍](#e2e测试介绍) + - [ServiceFence说明](#servicefence说明) + - [常见问题](#常见问题) + + +# 懒加载概述 + +[English](./README.md) + +## 特点 + +1. 支持1.8+的Istio版本,无侵入性,[版本适配详情](https://github.com/slime-io/slime/issues/145) +2. 可自动对接整个服务网格 +3. 兜底转发过程支持Istio所有流量治理能力 +4. 兜底逻辑简单,与服务数量无关,无性能问题 +5. 支持为服务手动或自动启用懒加载 +6. 支持Accesslog和Prometheus等多种动态服务依赖获取方式 +7. 支持添加静态服务依赖关系,动静依赖关系结合,功能全面 + + + + + +## 背景 + +懒加载即按需加载。 + +没有懒加载时,服务数量过多时,Envoy配置量太大,新上的应用长时间处于Not Ready状态。为应用配置Custom Resource `Sidecar`,并自动的获取服务依赖关系,更新Sidecar可以解决此问题。 + + + +## 思路 + +引入一个服务`global-sidecar`,它是一个兜底应用。它会被集群的Istiod注入sidecar容器。该sidecar容器拥有全量的配置和服务发现信息。兜底路由替换为指向global-sidecar。 + +引入新的Custom Resource Definition `ServiceFence`。详见[ServiceFence说明](#ServiceFence说明) + +最后,将控制逻辑包含到lazyload controller组件中。它会为启用懒加载的服务创建ServiceFence和Sidecar,根据配置获取到服务调用关系,更新ServiceFence和Sidecar。 + + + +## 架构 + +整个模块由Lazyload controller和global-sidecar两部分组成。Lazyload controller无需注入sidecar,global-sidecar则需要注入。 + + + + + +具体的细节说明可以参见[架构](./lazyload_tutorials_zh.md#%E6%9E%B6%E6%9E%84) + + + +## 安装和使用 + +1. 根据global-sidecar的部署模式不同,该模块目前分为两种模式: + + - Cluster模式:使用cluster级别的global-sidecar:集群唯一global-sidecar应用 + + - Namespace模式:使用namespace级别的global-sidecar:每个使用懒加载的namespace下一个global-sidecar应用 + +2. 根据服务依赖关系指标来源不同,该模块分为两种模式: + + - Accesslog模式:global-sidecar通过入流量拦截,生成包含服务依赖关系的accesslog + - Prometheus模式:业务应用在完成访问后,生成metric,上报prometheus。此种模式需要集群对接Prometheus + +总的来说,Lazyload模块有4种使用模式,较为推荐Cluster+Accesslog模式。 + +详见 [安装和使用](./lazyload_tutorials_zh.md#%E5%AE%89%E8%A3%85%E5%92%8C%E4%BD%BF%E7%94%A8) + + + +## 特性介绍 + +- 可基于Accesslog开启懒加载 +- 支持为服务手动或自动启用懒加载 +- 支持自定义兜底流量分派 +- 支持添加静态服务依赖关系 +- 支持自定义服务依赖别名 +- 日志输出到本地并轮转 + +详见 [特性介绍](./lazyload_tutorials_zh.md#%E7%89%B9%E6%80%A7%E4%BB%8B%E7%BB%8D) + + + +## 完整使用样例 + +详见 [示例: 为bookinfo的productpage服务开启懒加载](./lazyload_tutorials_zh.md#%E7%A4%BA%E4%BE%8B) + + + +## E2E测试介绍 + +在进行功能开发时,可以通过E2E测试验证模块功能正确性。 + +详见 [E2E测试教程](https://github.com/slime-io/slime/blob/master/doc/zh/slime_e2e_test_zh.md) + + + +## ServiceFence说明 + +ServiceFence可以看作是针对某一服务的Sidecar资源,区别是ServiceFence不仅会根据依赖关系生成Sidecar资源,同时会根据VirtualService规则判断服务的真实后端,并自动扩大Fence的范围。 + +例如,c.default.svc.cluster.local在fence中。此时有一条路由规则的host为c.default.svc.cluster.local,其destinatoin为d.default.svc.cluster.local,那么d服务也会被自动扩充到Fence中。 + +服务围栏 + + + +## 常见问题 + +详见 [FAQ](./lazyload_tutorials_zh.md#FAQ) + diff --git a/staging/src/slime.io/slime/modules/lazyload/api/v1alpha1/fence_module.pb.go b/staging/src/slime.io/slime/modules/lazyload/api/v1alpha1/fence_module.pb.go new file mode 100644 index 00000000..601480fa --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/api/v1alpha1/fence_module.pb.go @@ -0,0 +1,251 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: fence_module.proto + +package v1alpha1 + +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type Fence struct { + // service ports enable lazyload + WormholePort []string `protobuf:"bytes,1,rep,name=wormholePort,proto3" json:"wormholePort,omitempty"` + // whether enable ServiceFence auto generating + // default value is false + AutoFence bool `protobuf:"varint,2,opt,name=autoFence,proto3" json:"autoFence,omitempty"` + // the namespace list which enable lazyload + Namespace []string `protobuf:"bytes,3,rep,name=namespace,proto3" json:"namespace,omitempty"` + // custom outside dispatch traffic rules + Dispatches []*Dispatch `protobuf:"bytes,4,rep,name=dispatches,proto3" json:"dispatches,omitempty"` + // can convert to one or many domain alias rules + DomainAliases []*DomainAlias `protobuf:"bytes,5,rep,name=domainAliases,proto3" json:"domainAliases,omitempty"` + // default behavior of create fence or not when autoFence is true + // default value is false + DefaultFence bool `protobuf:"varint,6,opt,name=defaultFence,proto3" json:"defaultFence,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Fence) Reset() { *m = Fence{} } +func (m *Fence) String() string { return proto.CompactTextString(m) } +func (*Fence) ProtoMessage() {} +func (*Fence) Descriptor() ([]byte, []int) { + return fileDescriptor_8eebc4b237a55c9b, []int{0} +} +func (m *Fence) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Fence.Unmarshal(m, b) +} +func (m *Fence) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Fence.Marshal(b, m, deterministic) +} +func (m *Fence) XXX_Merge(src proto.Message) { + xxx_messageInfo_Fence.Merge(m, src) +} +func (m *Fence) XXX_Size() int { + return xxx_messageInfo_Fence.Size(m) +} +func (m *Fence) XXX_DiscardUnknown() { + xxx_messageInfo_Fence.DiscardUnknown(m) +} + +var xxx_messageInfo_Fence proto.InternalMessageInfo + +func (m *Fence) GetWormholePort() []string { + if m != nil { + return m.WormholePort + } + return nil +} + +func (m *Fence) GetAutoFence() bool { + if m != nil { + return m.AutoFence + } + return false +} + +func (m *Fence) GetNamespace() []string { + if m != nil { + return m.Namespace + } + return nil +} + +func (m *Fence) GetDispatches() []*Dispatch { + if m != nil { + return m.Dispatches + } + return nil +} + +func (m *Fence) GetDomainAliases() []*DomainAlias { + if m != nil { + return m.DomainAliases + } + return nil +} + +func (m *Fence) GetDefaultFence() bool { + if m != nil { + return m.DefaultFence + } + return false +} + +// The general idea is to assign different default traffic to different targets +// for correct processing by means of domain matching. +type Dispatch struct { + // dispatch rule name + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // domain matching contents + Domains []string `protobuf:"bytes,2,rep,name=domains,proto3" json:"domains,omitempty"` + // target cluster + Cluster string `protobuf:"bytes,3,opt,name=cluster,proto3" json:"cluster,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Dispatch) Reset() { *m = Dispatch{} } +func (m *Dispatch) String() string { return proto.CompactTextString(m) } +func (*Dispatch) ProtoMessage() {} +func (*Dispatch) Descriptor() ([]byte, []int) { + return fileDescriptor_8eebc4b237a55c9b, []int{1} +} +func (m *Dispatch) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Dispatch.Unmarshal(m, b) +} +func (m *Dispatch) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Dispatch.Marshal(b, m, deterministic) +} +func (m *Dispatch) XXX_Merge(src proto.Message) { + xxx_messageInfo_Dispatch.Merge(m, src) +} +func (m *Dispatch) XXX_Size() int { + return xxx_messageInfo_Dispatch.Size(m) +} +func (m *Dispatch) XXX_DiscardUnknown() { + xxx_messageInfo_Dispatch.DiscardUnknown(m) +} + +var xxx_messageInfo_Dispatch proto.InternalMessageInfo + +func (m *Dispatch) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Dispatch) GetDomains() []string { + if m != nil { + return m.Domains + } + return nil +} + +func (m *Dispatch) GetCluster() string { + if m != nil { + return m.Cluster + } + return "" +} + +// DomainAlias regexp expression, which is alias for target domain +// default value is empty +// example: +// domainAliases: +// - pattern: (?P[^\.]+)\.(?P[^\.]+)\.svc\.cluster\.local$ +// template: +// - $namespace.$service.service.mailsaas +type DomainAlias struct { + Pattern string `protobuf:"bytes,1,opt,name=pattern,proto3" json:"pattern,omitempty"` + Templates []string `protobuf:"bytes,2,rep,name=templates,proto3" json:"templates,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DomainAlias) Reset() { *m = DomainAlias{} } +func (m *DomainAlias) String() string { return proto.CompactTextString(m) } +func (*DomainAlias) ProtoMessage() {} +func (*DomainAlias) Descriptor() ([]byte, []int) { + return fileDescriptor_8eebc4b237a55c9b, []int{2} +} +func (m *DomainAlias) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DomainAlias.Unmarshal(m, b) +} +func (m *DomainAlias) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DomainAlias.Marshal(b, m, deterministic) +} +func (m *DomainAlias) XXX_Merge(src proto.Message) { + xxx_messageInfo_DomainAlias.Merge(m, src) +} +func (m *DomainAlias) XXX_Size() int { + return xxx_messageInfo_DomainAlias.Size(m) +} +func (m *DomainAlias) XXX_DiscardUnknown() { + xxx_messageInfo_DomainAlias.DiscardUnknown(m) +} + +var xxx_messageInfo_DomainAlias proto.InternalMessageInfo + +func (m *DomainAlias) GetPattern() string { + if m != nil { + return m.Pattern + } + return "" +} + +func (m *DomainAlias) GetTemplates() []string { + if m != nil { + return m.Templates + } + return nil +} + +func init() { + proto.RegisterType((*Fence)(nil), "slime.microservice.lazyload.v1alpha1.Fence") + proto.RegisterType((*Dispatch)(nil), "slime.microservice.lazyload.v1alpha1.Dispatch") + proto.RegisterType((*DomainAlias)(nil), "slime.microservice.lazyload.v1alpha1.DomainAlias") +} + +func init() { proto.RegisterFile("fence_module.proto", fileDescriptor_8eebc4b237a55c9b) } + +var fileDescriptor_8eebc4b237a55c9b = []byte{ + // 319 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0x31, 0x4f, 0xeb, 0x30, + 0x10, 0xc7, 0x95, 0xa6, 0xed, 0x6b, 0xdd, 0xf7, 0x16, 0x4f, 0x1e, 0xde, 0x10, 0x45, 0x0c, 0x19, + 0x90, 0xa3, 0xc2, 0x27, 0x00, 0x01, 0x23, 0x42, 0x59, 0x90, 0x58, 0xd0, 0x91, 0x5c, 0x55, 0x4b, + 0x76, 0x6c, 0xd9, 0x4e, 0x11, 0x7c, 0x2e, 0x3e, 0x20, 0xb2, 0x53, 0x2b, 0xed, 0xd6, 0xcd, 0xf7, + 0x3f, 0xff, 0x7f, 0xba, 0xfb, 0xeb, 0x08, 0xdd, 0x61, 0xdf, 0xe2, 0xbb, 0xd2, 0xdd, 0x20, 0x91, + 0x1b, 0xab, 0xbd, 0xa6, 0x57, 0x4e, 0x0a, 0x85, 0x5c, 0x89, 0xd6, 0x6a, 0x87, 0xf6, 0x20, 0x5a, + 0xe4, 0x12, 0xbe, 0xbf, 0xa4, 0x86, 0x8e, 0x1f, 0xb6, 0x20, 0xcd, 0x1e, 0xb6, 0xe5, 0xcf, 0x8c, + 0x2c, 0x9e, 0x82, 0x99, 0x96, 0xe4, 0xef, 0xa7, 0xb6, 0x6a, 0xaf, 0x25, 0xbe, 0x68, 0xeb, 0x59, + 0x56, 0xe4, 0xd5, 0xba, 0x39, 0xd3, 0xe8, 0x7f, 0xb2, 0x86, 0xc1, 0xeb, 0x68, 0x60, 0xb3, 0x22, + 0xab, 0x56, 0xcd, 0x24, 0x84, 0x6e, 0x0f, 0x0a, 0x9d, 0x81, 0x16, 0x59, 0x1e, 0xed, 0x93, 0x40, + 0x9f, 0x09, 0xe9, 0x84, 0x33, 0xe0, 0xdb, 0x3d, 0x3a, 0x36, 0x2f, 0xf2, 0x6a, 0x73, 0xc3, 0xf9, + 0x25, 0x43, 0xf2, 0x87, 0xa3, 0xaf, 0x39, 0x21, 0xd0, 0x57, 0xf2, 0xaf, 0xd3, 0x0a, 0x44, 0x7f, + 0x27, 0x05, 0x38, 0x74, 0x6c, 0x11, 0x91, 0xdb, 0x0b, 0x91, 0x93, 0xb5, 0x39, 0xe7, 0x84, 0x20, + 0x3a, 0xdc, 0xc1, 0x20, 0xfd, 0xb8, 0xe7, 0x32, 0xee, 0x79, 0xa6, 0x95, 0x0d, 0x59, 0xa5, 0xa1, + 0x28, 0x25, 0xf3, 0xb0, 0x25, 0xcb, 0x8a, 0xac, 0x5a, 0x37, 0xf1, 0x4d, 0x19, 0xf9, 0x33, 0x42, + 0x1d, 0x9b, 0xc5, 0x20, 0x52, 0x19, 0x3a, 0xad, 0x1c, 0x9c, 0x47, 0xcb, 0xf2, 0x68, 0x48, 0x65, + 0xf9, 0x48, 0x36, 0x27, 0x53, 0x85, 0x8f, 0x06, 0xbc, 0x47, 0xdb, 0x1f, 0xc9, 0xa9, 0x0c, 0x39, + 0x7b, 0x54, 0x46, 0x82, 0xc7, 0x84, 0x9f, 0x84, 0x7b, 0xfe, 0x76, 0x3d, 0x26, 0x20, 0x74, 0x1d, + 0x1f, 0xf5, 0x78, 0x16, 0xae, 0x4e, 0x29, 0xd4, 0x60, 0x44, 0x9d, 0x92, 0xf8, 0x58, 0xc6, 0x73, + 0xb9, 0xfd, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x73, 0xd6, 0x40, 0xbd, 0x44, 0x02, 0x00, 0x00, +} diff --git a/staging/src/slime.io/slime/modules/lazyload/api/v1alpha1/fence_module.proto b/staging/src/slime.io/slime/modules/lazyload/api/v1alpha1/fence_module.proto new file mode 100644 index 00000000..989e6fe0 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/api/v1alpha1/fence_module.proto @@ -0,0 +1,49 @@ +/* +* @Author: wangchenyu +* @Date: 2022/2/23 +*/ + +syntax = "proto3"; +package slime.microservice.lazyload.v1alpha1; + +option go_package = "slime.io/slime/modules/lazyload/api/v1alpha1"; + +message Fence { + // service ports enable lazyload + repeated string wormholePort = 1; + // whether enable ServiceFence auto generating + // default value is false + bool autoFence = 2; + // the namespace list which enable lazyload + repeated string namespace = 3; + // custom outside dispatch traffic rules + repeated Dispatch dispatches = 4; + // can convert to one or many domain alias rules + repeated DomainAlias domainAliases = 5; + // default behavior of create fence or not when autoFence is true + // default value is false + bool defaultFence = 6; +} + +// The general idea is to assign different default traffic to different targets +// for correct processing by means of domain matching. +message Dispatch { + // dispatch rule name + string name = 1; + // domain matching contents + repeated string domains = 2; + // target cluster + string cluster = 3; +} + +// DomainAlias regexp expression, which is alias for target domain +// default value is empty +// example: +// domainAliases: +// - pattern: (?P[^\.]+)\.(?P[^\.]+)\.svc\.cluster\.local$ +// template: +// - $namespace.$service.service.mailsaas +message DomainAlias { + string pattern = 1; + repeated string templates = 2; +} diff --git a/staging/src/slime.io/slime/modules/lazyload/api/v1alpha1/groupversion_info.go b/staging/src/slime.io/slime/modules/lazyload/api/v1alpha1/groupversion_info.go new file mode 100644 index 00000000..5bd5f692 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/api/v1alpha1/groupversion_info.go @@ -0,0 +1,36 @@ +/* + + +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 v1alpha1 contains API Schema definitions for the microservice.slime.io v1alpha1 API group +// +kubebuilder:object:generate=true +// +groupName=microservice.slime.io +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "microservice.slime.io", Version: "v1alpha1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/staging/src/slime.io/slime/modules/lazyload/api/v1alpha1/service_fence.pb.go b/staging/src/slime.io/slime/modules/lazyload/api/v1alpha1/service_fence.pb.go new file mode 100644 index 00000000..1bcf4d70 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/api/v1alpha1/service_fence.pb.go @@ -0,0 +1,649 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: service_fence.proto + +package v1alpha1 + +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type Destinations_Status int32 + +const ( + Destinations_ACTIVE Destinations_Status = 0 + Destinations_EXPIRE Destinations_Status = 1 + // In order to avoid deleting frequently, add status EXPIREWAIT between ACTIVE and EXPIRE. + // When new metric does not contain host of ACTIVE status, its status will change to EXPIREWAIT. If new metric does not contain + // host of EXPIREWAIT status, which means this host is not contained in the last two metrics, the status will change to EXPIRE. + // Otherwise, EXPIREWAIT status will change back to ACTIVE. + // Hosts of ACTIVE or EXPIREWAIT status are all valid for sidecar. + // For prometheus metric source, as metric can continuously be watched, we can set status update interval in the future version, + // refer to RecentlyCalled of RecyclingStrategy. But for accesslog metric source, metric only stores in lazyload controller memory. + // Metric can not continuously produce after host added to sidecar. So after lazyload controller rebooting, we can not tell whether + // old host is valid or not until it is removed from sidecar and goes to global-sidecar again. + // We do not have a proper solution to do same thing for accesslog metric source so far. Need further thinking. + Destinations_EXPIREWAIT Destinations_Status = 2 +) + +var Destinations_Status_name = map[int32]string{ + 0: "ACTIVE", + 1: "EXPIRE", + 2: "EXPIREWAIT", +} + +var Destinations_Status_value = map[string]int32{ + "ACTIVE": 0, + "EXPIRE": 1, + "EXPIREWAIT": 2, +} + +func (x Destinations_Status) String() string { + return proto.EnumName(Destinations_Status_name, int32(x)) +} + +func (Destinations_Status) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_b4b8d9f0db3c7310, []int{5, 0} +} + +type Timestamp struct { + // Represents seconds of UTC time since Unix epoch + // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to + // 9999-12-31T23:59:59Z inclusive. + Seconds int64 `protobuf:"varint,1,opt,name=seconds,proto3" json:"seconds,omitempty"` + // Non-negative fractions of a second at nanosecond resolution. Negative + // second values with fractions must still have non-negative nanos values + // that count forward in time. Must be from 0 to 999,999,999 + // inclusive. + Nanos int32 `protobuf:"varint,2,opt,name=nanos,proto3" json:"nanos,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Timestamp) Reset() { *m = Timestamp{} } +func (m *Timestamp) String() string { return proto.CompactTextString(m) } +func (*Timestamp) ProtoMessage() {} +func (*Timestamp) Descriptor() ([]byte, []int) { + return fileDescriptor_b4b8d9f0db3c7310, []int{0} +} +func (m *Timestamp) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Timestamp.Unmarshal(m, b) +} +func (m *Timestamp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Timestamp.Marshal(b, m, deterministic) +} +func (m *Timestamp) XXX_Merge(src proto.Message) { + xxx_messageInfo_Timestamp.Merge(m, src) +} +func (m *Timestamp) XXX_Size() int { + return xxx_messageInfo_Timestamp.Size(m) +} +func (m *Timestamp) XXX_DiscardUnknown() { + xxx_messageInfo_Timestamp.DiscardUnknown(m) +} + +var xxx_messageInfo_Timestamp proto.InternalMessageInfo + +func (m *Timestamp) GetSeconds() int64 { + if m != nil { + return m.Seconds + } + return 0 +} + +func (m *Timestamp) GetNanos() int32 { + if m != nil { + return m.Nanos + } + return 0 +} + +// Spec Example +// spec: +// enable: true +// host: +// reviews.default.svc.cluster.local: # static dependency of reviews.default service +// stable: +// test/*: {} # static dependency of all services in namespace 'test' +// namespaceSelector: # Match namespace names, multiple namespaces are 'or' relations, static dependency +// - foo +// - bar +// labelSelector: # Match service label, multiple selectors are 'or' relationship, static dependency +// - selector: +// project: back +// - selector: # labels in same selector are 'and' relationship +// project: front +// group: web +// workloadSelector: +// labels: +// group: foo +// zone: hz +// fromService: false +type ServiceFenceSpec struct { + Host map[string]*RecyclingStrategy `protobuf:"bytes,1,rep,name=host,proto3" json:"host,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // Switch to render servicefence as sidecar + Enable bool `protobuf:"varint,2,opt,name=enable,proto3" json:"enable,omitempty"` + // services in these namespaces are all static dependency, will not expire + NamespaceSelector []string `protobuf:"bytes,3,rep,name=namespaceSelector,proto3" json:"namespaceSelector,omitempty"` + // services match one selector of the label selector are all static dependency, will not expire + LabelSelector []*Selector `protobuf:"bytes,4,rep,name=labelSelector,proto3" json:"labelSelector,omitempty"` + WorkloadSelector *WorkloadSelector `protobuf:"bytes,5,opt,name=workloadSelector,proto3" json:"workloadSelector,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ServiceFenceSpec) Reset() { *m = ServiceFenceSpec{} } +func (m *ServiceFenceSpec) String() string { return proto.CompactTextString(m) } +func (*ServiceFenceSpec) ProtoMessage() {} +func (*ServiceFenceSpec) Descriptor() ([]byte, []int) { + return fileDescriptor_b4b8d9f0db3c7310, []int{1} +} +func (m *ServiceFenceSpec) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ServiceFenceSpec.Unmarshal(m, b) +} +func (m *ServiceFenceSpec) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ServiceFenceSpec.Marshal(b, m, deterministic) +} +func (m *ServiceFenceSpec) XXX_Merge(src proto.Message) { + xxx_messageInfo_ServiceFenceSpec.Merge(m, src) +} +func (m *ServiceFenceSpec) XXX_Size() int { + return xxx_messageInfo_ServiceFenceSpec.Size(m) +} +func (m *ServiceFenceSpec) XXX_DiscardUnknown() { + xxx_messageInfo_ServiceFenceSpec.DiscardUnknown(m) +} + +var xxx_messageInfo_ServiceFenceSpec proto.InternalMessageInfo + +func (m *ServiceFenceSpec) GetHost() map[string]*RecyclingStrategy { + if m != nil { + return m.Host + } + return nil +} + +func (m *ServiceFenceSpec) GetEnable() bool { + if m != nil { + return m.Enable + } + return false +} + +func (m *ServiceFenceSpec) GetNamespaceSelector() []string { + if m != nil { + return m.NamespaceSelector + } + return nil +} + +func (m *ServiceFenceSpec) GetLabelSelector() []*Selector { + if m != nil { + return m.LabelSelector + } + return nil +} + +func (m *ServiceFenceSpec) GetWorkloadSelector() *WorkloadSelector { + if m != nil { + return m.WorkloadSelector + } + return nil +} + +type Selector struct { + Selector map[string]string `protobuf:"bytes,1,rep,name=selector,proto3" json:"selector,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Selector) Reset() { *m = Selector{} } +func (m *Selector) String() string { return proto.CompactTextString(m) } +func (*Selector) ProtoMessage() {} +func (*Selector) Descriptor() ([]byte, []int) { + return fileDescriptor_b4b8d9f0db3c7310, []int{2} +} +func (m *Selector) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Selector.Unmarshal(m, b) +} +func (m *Selector) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Selector.Marshal(b, m, deterministic) +} +func (m *Selector) XXX_Merge(src proto.Message) { + xxx_messageInfo_Selector.Merge(m, src) +} +func (m *Selector) XXX_Size() int { + return xxx_messageInfo_Selector.Size(m) +} +func (m *Selector) XXX_DiscardUnknown() { + xxx_messageInfo_Selector.DiscardUnknown(m) +} + +var xxx_messageInfo_Selector proto.InternalMessageInfo + +func (m *Selector) GetSelector() map[string]string { + if m != nil { + return m.Selector + } + return nil +} + +type WorkloadSelector struct { + // take effect when labels is empty + // true: sidecar.workloadSelector.labels = svc.spec.selector + // false: sidecar.workloadSelector.labels = map[string]string{env.config.global.service: svc.name} + FromService bool `protobuf:"varint,1,opt,name=fromService,proto3" json:"fromService,omitempty"` + // top priority, if labels is not empty, sidecar.workloadSelector.labels = sf.spec.workloadSelector.labels + Labels map[string]string `protobuf:"bytes,2,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *WorkloadSelector) Reset() { *m = WorkloadSelector{} } +func (m *WorkloadSelector) String() string { return proto.CompactTextString(m) } +func (*WorkloadSelector) ProtoMessage() {} +func (*WorkloadSelector) Descriptor() ([]byte, []int) { + return fileDescriptor_b4b8d9f0db3c7310, []int{3} +} +func (m *WorkloadSelector) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_WorkloadSelector.Unmarshal(m, b) +} +func (m *WorkloadSelector) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_WorkloadSelector.Marshal(b, m, deterministic) +} +func (m *WorkloadSelector) XXX_Merge(src proto.Message) { + xxx_messageInfo_WorkloadSelector.Merge(m, src) +} +func (m *WorkloadSelector) XXX_Size() int { + return xxx_messageInfo_WorkloadSelector.Size(m) +} +func (m *WorkloadSelector) XXX_DiscardUnknown() { + xxx_messageInfo_WorkloadSelector.DiscardUnknown(m) +} + +var xxx_messageInfo_WorkloadSelector proto.InternalMessageInfo + +func (m *WorkloadSelector) GetFromService() bool { + if m != nil { + return m.FromService + } + return false +} + +func (m *WorkloadSelector) GetLabels() map[string]string { + if m != nil { + return m.Labels + } + return nil +} + +type RecyclingStrategy struct { + // Configuration that will not be cleaned up + Stable *RecyclingStrategy_Stable `protobuf:"bytes,1,opt,name=stable,proto3" json:"stable,omitempty"` + // Configurations that expire after expiration + Deadline *RecyclingStrategy_Deadline `protobuf:"bytes,2,opt,name=deadline,proto3" json:"deadline,omitempty"` + // Deprecated + Auto *RecyclingStrategy_Auto `protobuf:"bytes,3,opt,name=auto,proto3" json:"auto,omitempty"` + RecentlyCalled *Timestamp `protobuf:"bytes,4,opt,name=RecentlyCalled,proto3" json:"RecentlyCalled,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RecyclingStrategy) Reset() { *m = RecyclingStrategy{} } +func (m *RecyclingStrategy) String() string { return proto.CompactTextString(m) } +func (*RecyclingStrategy) ProtoMessage() {} +func (*RecyclingStrategy) Descriptor() ([]byte, []int) { + return fileDescriptor_b4b8d9f0db3c7310, []int{4} +} +func (m *RecyclingStrategy) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_RecyclingStrategy.Unmarshal(m, b) +} +func (m *RecyclingStrategy) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_RecyclingStrategy.Marshal(b, m, deterministic) +} +func (m *RecyclingStrategy) XXX_Merge(src proto.Message) { + xxx_messageInfo_RecyclingStrategy.Merge(m, src) +} +func (m *RecyclingStrategy) XXX_Size() int { + return xxx_messageInfo_RecyclingStrategy.Size(m) +} +func (m *RecyclingStrategy) XXX_DiscardUnknown() { + xxx_messageInfo_RecyclingStrategy.DiscardUnknown(m) +} + +var xxx_messageInfo_RecyclingStrategy proto.InternalMessageInfo + +func (m *RecyclingStrategy) GetStable() *RecyclingStrategy_Stable { + if m != nil { + return m.Stable + } + return nil +} + +func (m *RecyclingStrategy) GetDeadline() *RecyclingStrategy_Deadline { + if m != nil { + return m.Deadline + } + return nil +} + +func (m *RecyclingStrategy) GetAuto() *RecyclingStrategy_Auto { + if m != nil { + return m.Auto + } + return nil +} + +func (m *RecyclingStrategy) GetRecentlyCalled() *Timestamp { + if m != nil { + return m.RecentlyCalled + } + return nil +} + +type RecyclingStrategy_Stable struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RecyclingStrategy_Stable) Reset() { *m = RecyclingStrategy_Stable{} } +func (m *RecyclingStrategy_Stable) String() string { return proto.CompactTextString(m) } +func (*RecyclingStrategy_Stable) ProtoMessage() {} +func (*RecyclingStrategy_Stable) Descriptor() ([]byte, []int) { + return fileDescriptor_b4b8d9f0db3c7310, []int{4, 0} +} +func (m *RecyclingStrategy_Stable) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_RecyclingStrategy_Stable.Unmarshal(m, b) +} +func (m *RecyclingStrategy_Stable) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_RecyclingStrategy_Stable.Marshal(b, m, deterministic) +} +func (m *RecyclingStrategy_Stable) XXX_Merge(src proto.Message) { + xxx_messageInfo_RecyclingStrategy_Stable.Merge(m, src) +} +func (m *RecyclingStrategy_Stable) XXX_Size() int { + return xxx_messageInfo_RecyclingStrategy_Stable.Size(m) +} +func (m *RecyclingStrategy_Stable) XXX_DiscardUnknown() { + xxx_messageInfo_RecyclingStrategy_Stable.DiscardUnknown(m) +} + +var xxx_messageInfo_RecyclingStrategy_Stable proto.InternalMessageInfo + +type RecyclingStrategy_Deadline struct { + Expire *Timestamp `protobuf:"bytes,1,opt,name=expire,proto3" json:"expire,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RecyclingStrategy_Deadline) Reset() { *m = RecyclingStrategy_Deadline{} } +func (m *RecyclingStrategy_Deadline) String() string { return proto.CompactTextString(m) } +func (*RecyclingStrategy_Deadline) ProtoMessage() {} +func (*RecyclingStrategy_Deadline) Descriptor() ([]byte, []int) { + return fileDescriptor_b4b8d9f0db3c7310, []int{4, 1} +} +func (m *RecyclingStrategy_Deadline) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_RecyclingStrategy_Deadline.Unmarshal(m, b) +} +func (m *RecyclingStrategy_Deadline) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_RecyclingStrategy_Deadline.Marshal(b, m, deterministic) +} +func (m *RecyclingStrategy_Deadline) XXX_Merge(src proto.Message) { + xxx_messageInfo_RecyclingStrategy_Deadline.Merge(m, src) +} +func (m *RecyclingStrategy_Deadline) XXX_Size() int { + return xxx_messageInfo_RecyclingStrategy_Deadline.Size(m) +} +func (m *RecyclingStrategy_Deadline) XXX_DiscardUnknown() { + xxx_messageInfo_RecyclingStrategy_Deadline.DiscardUnknown(m) +} + +var xxx_messageInfo_RecyclingStrategy_Deadline proto.InternalMessageInfo + +func (m *RecyclingStrategy_Deadline) GetExpire() *Timestamp { + if m != nil { + return m.Expire + } + return nil +} + +type RecyclingStrategy_Auto struct { + Duration *Timestamp `protobuf:"bytes,1,opt,name=duration,proto3" json:"duration,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RecyclingStrategy_Auto) Reset() { *m = RecyclingStrategy_Auto{} } +func (m *RecyclingStrategy_Auto) String() string { return proto.CompactTextString(m) } +func (*RecyclingStrategy_Auto) ProtoMessage() {} +func (*RecyclingStrategy_Auto) Descriptor() ([]byte, []int) { + return fileDescriptor_b4b8d9f0db3c7310, []int{4, 2} +} +func (m *RecyclingStrategy_Auto) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_RecyclingStrategy_Auto.Unmarshal(m, b) +} +func (m *RecyclingStrategy_Auto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_RecyclingStrategy_Auto.Marshal(b, m, deterministic) +} +func (m *RecyclingStrategy_Auto) XXX_Merge(src proto.Message) { + xxx_messageInfo_RecyclingStrategy_Auto.Merge(m, src) +} +func (m *RecyclingStrategy_Auto) XXX_Size() int { + return xxx_messageInfo_RecyclingStrategy_Auto.Size(m) +} +func (m *RecyclingStrategy_Auto) XXX_DiscardUnknown() { + xxx_messageInfo_RecyclingStrategy_Auto.DiscardUnknown(m) +} + +var xxx_messageInfo_RecyclingStrategy_Auto proto.InternalMessageInfo + +func (m *RecyclingStrategy_Auto) GetDuration() *Timestamp { + if m != nil { + return m.Duration + } + return nil +} + +type Destinations struct { + // Deprecated + RecentlyCalled *Timestamp `protobuf:"bytes,1,opt,name=RecentlyCalled,proto3" json:"RecentlyCalled,omitempty"` + Hosts []string `protobuf:"bytes,2,rep,name=hosts,proto3" json:"hosts,omitempty"` + Status Destinations_Status `protobuf:"varint,3,opt,name=status,proto3,enum=slime.microservice.lazyload.v1alpha1.Destinations_Status" json:"status,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Destinations) Reset() { *m = Destinations{} } +func (m *Destinations) String() string { return proto.CompactTextString(m) } +func (*Destinations) ProtoMessage() {} +func (*Destinations) Descriptor() ([]byte, []int) { + return fileDescriptor_b4b8d9f0db3c7310, []int{5} +} +func (m *Destinations) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Destinations.Unmarshal(m, b) +} +func (m *Destinations) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Destinations.Marshal(b, m, deterministic) +} +func (m *Destinations) XXX_Merge(src proto.Message) { + xxx_messageInfo_Destinations.Merge(m, src) +} +func (m *Destinations) XXX_Size() int { + return xxx_messageInfo_Destinations.Size(m) +} +func (m *Destinations) XXX_DiscardUnknown() { + xxx_messageInfo_Destinations.DiscardUnknown(m) +} + +var xxx_messageInfo_Destinations proto.InternalMessageInfo + +func (m *Destinations) GetRecentlyCalled() *Timestamp { + if m != nil { + return m.RecentlyCalled + } + return nil +} + +func (m *Destinations) GetHosts() []string { + if m != nil { + return m.Hosts + } + return nil +} + +func (m *Destinations) GetStatus() Destinations_Status { + if m != nil { + return m.Status + } + return Destinations_ACTIVE +} + +type ServiceFenceStatus struct { + Domains map[string]*Destinations `protobuf:"bytes,1,rep,name=domains,proto3" json:"domains,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + MetricStatus map[string]string `protobuf:"bytes,3,rep,name=metricStatus,proto3" json:"metricStatus,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Visitor map[string]bool `protobuf:"bytes,2,rep,name=visitor,proto3" json:"visitor,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ServiceFenceStatus) Reset() { *m = ServiceFenceStatus{} } +func (m *ServiceFenceStatus) String() string { return proto.CompactTextString(m) } +func (*ServiceFenceStatus) ProtoMessage() {} +func (*ServiceFenceStatus) Descriptor() ([]byte, []int) { + return fileDescriptor_b4b8d9f0db3c7310, []int{6} +} +func (m *ServiceFenceStatus) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ServiceFenceStatus.Unmarshal(m, b) +} +func (m *ServiceFenceStatus) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ServiceFenceStatus.Marshal(b, m, deterministic) +} +func (m *ServiceFenceStatus) XXX_Merge(src proto.Message) { + xxx_messageInfo_ServiceFenceStatus.Merge(m, src) +} +func (m *ServiceFenceStatus) XXX_Size() int { + return xxx_messageInfo_ServiceFenceStatus.Size(m) +} +func (m *ServiceFenceStatus) XXX_DiscardUnknown() { + xxx_messageInfo_ServiceFenceStatus.DiscardUnknown(m) +} + +var xxx_messageInfo_ServiceFenceStatus proto.InternalMessageInfo + +func (m *ServiceFenceStatus) GetDomains() map[string]*Destinations { + if m != nil { + return m.Domains + } + return nil +} + +func (m *ServiceFenceStatus) GetMetricStatus() map[string]string { + if m != nil { + return m.MetricStatus + } + return nil +} + +func (m *ServiceFenceStatus) GetVisitor() map[string]bool { + if m != nil { + return m.Visitor + } + return nil +} + +func init() { + proto.RegisterEnum("slime.microservice.lazyload.v1alpha1.Destinations_Status", Destinations_Status_name, Destinations_Status_value) + proto.RegisterType((*Timestamp)(nil), "slime.microservice.lazyload.v1alpha1.Timestamp") + proto.RegisterType((*ServiceFenceSpec)(nil), "slime.microservice.lazyload.v1alpha1.ServiceFenceSpec") + proto.RegisterMapType((map[string]*RecyclingStrategy)(nil), "slime.microservice.lazyload.v1alpha1.ServiceFenceSpec.HostEntry") + proto.RegisterType((*Selector)(nil), "slime.microservice.lazyload.v1alpha1.Selector") + proto.RegisterMapType((map[string]string)(nil), "slime.microservice.lazyload.v1alpha1.Selector.SelectorEntry") + proto.RegisterType((*WorkloadSelector)(nil), "slime.microservice.lazyload.v1alpha1.WorkloadSelector") + proto.RegisterMapType((map[string]string)(nil), "slime.microservice.lazyload.v1alpha1.WorkloadSelector.LabelsEntry") + proto.RegisterType((*RecyclingStrategy)(nil), "slime.microservice.lazyload.v1alpha1.RecyclingStrategy") + proto.RegisterType((*RecyclingStrategy_Stable)(nil), "slime.microservice.lazyload.v1alpha1.RecyclingStrategy.Stable") + proto.RegisterType((*RecyclingStrategy_Deadline)(nil), "slime.microservice.lazyload.v1alpha1.RecyclingStrategy.Deadline") + proto.RegisterType((*RecyclingStrategy_Auto)(nil), "slime.microservice.lazyload.v1alpha1.RecyclingStrategy.Auto") + proto.RegisterType((*Destinations)(nil), "slime.microservice.lazyload.v1alpha1.Destinations") + proto.RegisterType((*ServiceFenceStatus)(nil), "slime.microservice.lazyload.v1alpha1.ServiceFenceStatus") + proto.RegisterMapType((map[string]*Destinations)(nil), "slime.microservice.lazyload.v1alpha1.ServiceFenceStatus.DomainsEntry") + proto.RegisterMapType((map[string]string)(nil), "slime.microservice.lazyload.v1alpha1.ServiceFenceStatus.MetricStatusEntry") + proto.RegisterMapType((map[string]bool)(nil), "slime.microservice.lazyload.v1alpha1.ServiceFenceStatus.VisitorEntry") +} + +func init() { proto.RegisterFile("service_fence.proto", fileDescriptor_b4b8d9f0db3c7310) } + +var fileDescriptor_b4b8d9f0db3c7310 = []byte{ + // 775 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x96, 0xcd, 0x6e, 0xe3, 0x36, + 0x10, 0xc7, 0x2b, 0x7f, 0x45, 0x1e, 0x3b, 0x81, 0xc3, 0x06, 0x85, 0xa1, 0x93, 0x61, 0xf4, 0xe0, + 0x43, 0x20, 0x37, 0x2e, 0xd0, 0x36, 0x1f, 0x68, 0xf3, 0xe5, 0x36, 0x69, 0x1b, 0x20, 0xa5, 0x8c, + 0x24, 0x08, 0x0a, 0x04, 0xb4, 0xc4, 0x24, 0x42, 0x24, 0x51, 0x10, 0x29, 0xb7, 0xee, 0xb1, 0x6f, + 0xb2, 0xb7, 0xbd, 0xef, 0x63, 0xec, 0x1b, 0xec, 0xd3, 0x2c, 0x44, 0x4a, 0x82, 0x6c, 0x07, 0x58, + 0xdb, 0xbb, 0x37, 0x52, 0xd4, 0xfc, 0x66, 0xfe, 0x33, 0xa3, 0xa1, 0xe0, 0x6b, 0x4e, 0xa3, 0x89, + 0x6b, 0xd3, 0x87, 0x47, 0x1a, 0xd8, 0xd4, 0x0c, 0x23, 0x26, 0x18, 0xfa, 0x96, 0x7b, 0xae, 0x4f, + 0x4d, 0xdf, 0xb5, 0x23, 0x96, 0x9e, 0x9b, 0x1e, 0xf9, 0x6f, 0xea, 0x31, 0xe2, 0x98, 0x93, 0x3d, + 0xe2, 0x85, 0xcf, 0x64, 0xaf, 0x7b, 0x08, 0xf5, 0x91, 0xeb, 0x53, 0x2e, 0x88, 0x1f, 0xa2, 0x36, + 0x6c, 0x70, 0x6a, 0xb3, 0xc0, 0xe1, 0x6d, 0xad, 0xa3, 0xf5, 0xca, 0x38, 0xdb, 0xa2, 0x1d, 0xa8, + 0x06, 0x24, 0x60, 0xbc, 0x5d, 0xea, 0x68, 0xbd, 0x2a, 0x56, 0x9b, 0xee, 0x87, 0x32, 0xb4, 0x2c, + 0x85, 0xfe, 0x35, 0xf1, 0x6c, 0x85, 0xd4, 0x46, 0x23, 0xa8, 0x3c, 0x33, 0x2e, 0xda, 0x5a, 0xa7, + 0xdc, 0x6b, 0x0c, 0x8e, 0xcd, 0x65, 0xc2, 0x30, 0xe7, 0x29, 0xe6, 0x05, 0xe3, 0x62, 0x18, 0x88, + 0x68, 0x8a, 0x25, 0x0d, 0x7d, 0x03, 0x35, 0x1a, 0x90, 0xb1, 0x47, 0x65, 0x04, 0x3a, 0x4e, 0x77, + 0x68, 0x17, 0xb6, 0x03, 0xe2, 0x53, 0x1e, 0x12, 0x9b, 0x5a, 0xd4, 0xa3, 0xb6, 0x60, 0x51, 0xbb, + 0xdc, 0x29, 0xf7, 0xea, 0x78, 0xf1, 0x00, 0x8d, 0x60, 0xd3, 0x23, 0x63, 0xea, 0xe5, 0x6f, 0x56, + 0x64, 0x90, 0xe6, 0xb2, 0x41, 0x2a, 0x2b, 0x3c, 0x0b, 0x41, 0x63, 0x68, 0xfd, 0xc3, 0xa2, 0x97, + 0xe4, 0xe5, 0x1c, 0x5c, 0xed, 0x68, 0xbd, 0xc6, 0xe0, 0x87, 0xe5, 0xc0, 0xb7, 0x73, 0xd6, 0x78, + 0x81, 0x67, 0x84, 0x50, 0xcf, 0x53, 0x82, 0x5a, 0x50, 0x7e, 0xa1, 0x53, 0x59, 0xa3, 0x3a, 0x4e, + 0x96, 0xe8, 0x0a, 0xaa, 0x13, 0xe2, 0xc5, 0x2a, 0x3b, 0x8d, 0xc1, 0x8f, 0xcb, 0xf9, 0xc5, 0xd4, + 0x9e, 0xda, 0x9e, 0x1b, 0x3c, 0x59, 0x22, 0x22, 0x82, 0x3e, 0x4d, 0xb1, 0xa2, 0x1c, 0x94, 0x7e, + 0xd2, 0xba, 0x6f, 0x34, 0xd0, 0x73, 0x89, 0x77, 0xa0, 0xf3, 0x4c, 0x9a, 0x2a, 0xec, 0xd1, 0x6a, + 0x39, 0xcb, 0x17, 0xaa, 0xa8, 0x39, 0xcd, 0x38, 0x84, 0xcd, 0x99, 0xa3, 0x57, 0xc4, 0xed, 0x14, + 0xc5, 0xd5, 0x8b, 0x31, 0xbe, 0xd7, 0xa0, 0x35, 0x9f, 0x3c, 0xd4, 0x81, 0xc6, 0x63, 0xc4, 0xfc, + 0xb4, 0xa5, 0x24, 0x48, 0xc7, 0xc5, 0x47, 0xe8, 0x1e, 0x6a, 0xb2, 0x82, 0x49, 0x3b, 0x27, 0x5a, + 0x4e, 0xd7, 0x2b, 0x93, 0xf9, 0xa7, 0x84, 0x28, 0x45, 0x29, 0xd1, 0xd8, 0x87, 0x46, 0xe1, 0xf1, + 0x4a, 0x6a, 0xde, 0x56, 0x60, 0x7b, 0xa1, 0x24, 0xe8, 0x06, 0x6a, 0x5c, 0xc8, 0xce, 0xd7, 0x64, + 0x6d, 0x7f, 0x5e, 0xb3, 0xb6, 0xa6, 0x25, 0x29, 0x38, 0xa5, 0xa1, 0xbf, 0x41, 0x77, 0x28, 0x71, + 0x3c, 0x37, 0xc8, 0xba, 0xe6, 0x78, 0x5d, 0xf2, 0x79, 0xca, 0xc1, 0x39, 0x11, 0x5d, 0x43, 0x85, + 0xc4, 0x82, 0xb5, 0xcb, 0x92, 0x7c, 0xb4, 0x2e, 0xf9, 0x24, 0x16, 0x0c, 0x4b, 0x12, 0xba, 0x85, + 0x2d, 0x4c, 0x6d, 0x1a, 0x08, 0x6f, 0x7a, 0x46, 0x3c, 0x8f, 0x3a, 0xed, 0x8a, 0x64, 0xf7, 0x97, + 0x63, 0xe7, 0x53, 0x0e, 0xcf, 0x61, 0x0c, 0x1d, 0x6a, 0x2a, 0x35, 0x86, 0x05, 0x7a, 0x26, 0x05, + 0xfd, 0x06, 0x35, 0xfa, 0x6f, 0xe8, 0x46, 0x59, 0xda, 0x57, 0x76, 0x93, 0x9a, 0x1b, 0x16, 0x54, + 0x12, 0x15, 0xe8, 0x0f, 0xd0, 0x9d, 0x38, 0x22, 0xc2, 0x65, 0xc1, 0xba, 0xc8, 0x1c, 0xd0, 0xfd, + 0xbf, 0x04, 0xcd, 0x73, 0xca, 0x85, 0x1b, 0xc8, 0x3d, 0x7f, 0x25, 0x3b, 0xda, 0x17, 0xc9, 0x4e, + 0xd2, 0xae, 0xc9, 0x00, 0x56, 0x9f, 0x4a, 0x1d, 0xab, 0x0d, 0xfa, 0x4b, 0x36, 0xa5, 0x88, 0xb9, + 0x2c, 0xf0, 0xd6, 0x60, 0x7f, 0x39, 0x37, 0xc5, 0x90, 0x93, 0x7e, 0x14, 0x31, 0xc7, 0x29, 0xa8, + 0xfb, 0x9d, 0x2c, 0x83, 0x88, 0x39, 0x02, 0xa8, 0x9d, 0x9c, 0x8d, 0x2e, 0x6f, 0x86, 0xad, 0xaf, + 0x92, 0xf5, 0xf0, 0xee, 0xfa, 0x12, 0x0f, 0x5b, 0x1a, 0xda, 0x02, 0x50, 0xeb, 0xdb, 0x93, 0xcb, + 0x51, 0xab, 0xd4, 0x7d, 0x57, 0x01, 0x34, 0x73, 0x71, 0x28, 0xf3, 0x07, 0xd8, 0x70, 0x98, 0x4f, + 0xdc, 0x80, 0xa7, 0xa3, 0x6a, 0xb8, 0xc6, 0x1d, 0x24, 0x51, 0xe6, 0xb9, 0xe2, 0xa8, 0x2f, 0x3c, + 0xa3, 0xa2, 0x00, 0x9a, 0x3e, 0x15, 0x91, 0x6b, 0x5b, 0x59, 0x0a, 0x12, 0x2f, 0xbf, 0xaf, 0xed, + 0xe5, 0xaa, 0x00, 0x53, 0xae, 0x66, 0xf8, 0x89, 0xa0, 0x89, 0xcb, 0xdd, 0x64, 0xf6, 0x96, 0x3e, + 0x53, 0xd0, 0x8d, 0xe2, 0xa4, 0x82, 0x52, 0xaa, 0x11, 0x40, 0xb3, 0xa8, 0xf4, 0x95, 0xa1, 0x75, + 0x31, 0x7b, 0xbf, 0x0c, 0x56, 0x2f, 0x77, 0x61, 0xd0, 0x19, 0xbf, 0xc0, 0xf6, 0x82, 0xe6, 0x55, + 0x26, 0xa5, 0x71, 0x00, 0xcd, 0xa2, 0x92, 0x4f, 0xd9, 0xea, 0x05, 0xdb, 0x53, 0xf3, 0x7e, 0x57, + 0x05, 0xef, 0xb2, 0xbe, 0x5c, 0xf4, 0x7d, 0xe6, 0xc4, 0x1e, 0xe5, 0xfd, 0x4c, 0x40, 0x9f, 0x84, + 0x6e, 0x3f, 0x13, 0x31, 0xae, 0xc9, 0xdf, 0xa9, 0xef, 0x3f, 0x06, 0x00, 0x00, 0xff, 0xff, 0x6f, + 0xa9, 0x94, 0x30, 0x65, 0x09, 0x00, 0x00, +} diff --git a/staging/src/slime.io/slime/modules/lazyload/api/v1alpha1/service_fence.proto b/staging/src/slime.io/slime/modules/lazyload/api/v1alpha1/service_fence.proto new file mode 100644 index 00000000..959852f3 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/api/v1alpha1/service_fence.proto @@ -0,0 +1,133 @@ +/* +* @Author: yangdihang +* @Date: 2020/5/21 +*/ + +syntax = "proto3"; +package slime.microservice.lazyload.v1alpha1; + +option go_package = "slime.io/slime/modules/lazyload/api/v1alpha1"; + +// ServiceFence is a layer of encapsulation on top of the community Sidecar CR +// Its main role is the same as that of the Sidecar resource, which isolates configurations that the service does not care about, +// thus improving the performance of the service grid in large-scale scenarios +// ServiceFence can be used to manage the lifecycle of isolated configurations +// ServiceFence has three recording strategies: +// 1. stable, stable configuration, manual user recall configuration +// 2. deadline, due for recycling +// 3. auto, automatic recovery based on service deactivation + +message Timestamp { + + // Represents seconds of UTC time since Unix epoch + // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to + // 9999-12-31T23:59:59Z inclusive. + int64 seconds = 1; + + // Non-negative fractions of a second at nanosecond resolution. Negative + // second values with fractions must still have non-negative nanos values + // that count forward in time. Must be from 0 to 999,999,999 + // inclusive. + int32 nanos = 2; +} + +// Spec Example +// spec: +// enable: true +// host: +// reviews.default.svc.cluster.local: # static dependency of reviews.default service +// stable: +// test/*: {} # static dependency of all services in namespace 'test' +// namespaceSelector: # Match namespace names, multiple namespaces are 'or' relations, static dependency +// - foo +// - bar +// labelSelector: # Match service label, multiple selectors are 'or' relationship, static dependency +// - selector: +// project: back +// - selector: # labels in same selector are 'and' relationship +// project: front +// group: web +// workloadSelector: +// labels: +// group: foo +// zone: hz +// fromService: false +message ServiceFenceSpec { + map host = 1; + // Switch to render servicefence as sidecar + bool enable = 2; + // services in these namespaces are all static dependency, will not expire + repeated string namespaceSelector = 3; + // services match one selector of the label selector are all static dependency, will not expire + repeated Selector labelSelector = 4; + WorkloadSelector workloadSelector = 5; +} + +message Selector { + map selector = 1; +} + +message WorkloadSelector { + // take effect when labels is empty + // true: sidecar.workloadSelector.labels = svc.spec.selector + // false: sidecar.workloadSelector.labels = map[string]string{env.config.global.service: svc.name} + bool fromService = 1; + // top priority, if labels is not empty, sidecar.workloadSelector.labels = sf.spec.workloadSelector.labels + map labels = 2; +} + +message RecyclingStrategy { + + message Stable { + } + + message Deadline { + Timestamp expire = 1; + } + + message Auto { + Timestamp duration = 1; + } + // Configuration that will not be cleaned up + Stable stable = 1; + + // Configurations that expire after expiration + Deadline deadline = 2; + + // Deprecated + Auto auto = 3; + + Timestamp RecentlyCalled = 4; +} + + +message Destinations { + + // Deprecated + Timestamp RecentlyCalled = 1; + + repeated string hosts = 2; + + enum Status { + ACTIVE = 0; + EXPIRE = 1; + // In order to avoid deleting frequently, add status EXPIREWAIT between ACTIVE and EXPIRE. + // When new metric does not contain host of ACTIVE status, its status will change to EXPIREWAIT. If new metric does not contain + // host of EXPIREWAIT status, which means this host is not contained in the last two metrics, the status will change to EXPIRE. + // Otherwise, EXPIREWAIT status will change back to ACTIVE. + // Hosts of ACTIVE or EXPIREWAIT status are all valid for sidecar. + // For prometheus metric source, as metric can continuously be watched, we can set status update interval in the future version, + // refer to RecentlyCalled of RecyclingStrategy. But for accesslog metric source, metric only stores in lazyload controller memory. + // Metric can not continuously produce after host added to sidecar. So after lazyload controller rebooting, we can not tell whether + // old host is valid or not until it is removed from sidecar and goes to global-sidecar again. + // We do not have a proper solution to do same thing for accesslog metric source so far. Need further thinking. + EXPIREWAIT = 2; + } + Status status = 3; +} + +message ServiceFenceStatus { + map domains = 1; + map metricStatus = 3; + map visitor = 2; +} diff --git a/staging/src/slime.io/slime/modules/lazyload/api/v1alpha1/servicefence_types.go b/staging/src/slime.io/slime/modules/lazyload/api/v1alpha1/servicefence_types.go new file mode 100644 index 00000000..ebfaeac4 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/api/v1alpha1/servicefence_types.go @@ -0,0 +1,41 @@ +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ServiceFence is the Schema for the servicefences API +// +kubebuilder:subresource:status +// +kubebuilder:resource:path=servicefences,scope=Namespaced +type ServiceFence struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ServiceFenceSpec `json:"spec,omitempty"` + Status ServiceFenceStatus `json:"status,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ServiceFenceList contains a list of ServiceFence +type ServiceFenceList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ServiceFence `json:"items"` +} + +// GetSpec from a wrapper +func (in *ServiceFence) GetSpec() map[string]interface{} { + return nil +} + +// GetObjectMeta from a wrapper +func (in *ServiceFence) GetObjectMeta() metav1.ObjectMeta { + return in.ObjectMeta +} + +func init() { + SchemeBuilder.Register(&ServiceFence{}, &ServiceFenceList{}) +} diff --git a/staging/src/slime.io/slime/modules/lazyload/api/v1alpha1/zz_generated.deepcopy.go b/staging/src/slime.io/slime/modules/lazyload/api/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 00000000..8eac947c --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/api/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,466 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Destinations) DeepCopyInto(out *Destinations) { + *out = *in + if in.RecentlyCalled != nil { + in, out := &in.RecentlyCalled, &out.RecentlyCalled + *out = new(Timestamp) + (*in).DeepCopyInto(*out) + } + if in.Hosts != nil { + in, out := &in.Hosts, &out.Hosts + *out = make([]string, len(*in)) + copy(*out, *in) + } + out.XXX_NoUnkeyedLiteral = in.XXX_NoUnkeyedLiteral + if in.XXX_unrecognized != nil { + in, out := &in.XXX_unrecognized, &out.XXX_unrecognized + *out = make([]byte, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Destinations. +func (in *Destinations) DeepCopy() *Destinations { + if in == nil { + return nil + } + out := new(Destinations) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Dispatch) DeepCopyInto(out *Dispatch) { + *out = *in + if in.Domains != nil { + in, out := &in.Domains, &out.Domains + *out = make([]string, len(*in)) + copy(*out, *in) + } + out.XXX_NoUnkeyedLiteral = in.XXX_NoUnkeyedLiteral + if in.XXX_unrecognized != nil { + in, out := &in.XXX_unrecognized, &out.XXX_unrecognized + *out = make([]byte, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Dispatch. +func (in *Dispatch) DeepCopy() *Dispatch { + if in == nil { + return nil + } + out := new(Dispatch) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Fence) DeepCopyInto(out *Fence) { + *out = *in + if in.WormholePort != nil { + in, out := &in.WormholePort, &out.WormholePort + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Namespace != nil { + in, out := &in.Namespace, &out.Namespace + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Dispatches != nil { + in, out := &in.Dispatches, &out.Dispatches + *out = make([]*Dispatch, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(Dispatch) + (*in).DeepCopyInto(*out) + } + } + } + out.XXX_NoUnkeyedLiteral = in.XXX_NoUnkeyedLiteral + if in.XXX_unrecognized != nil { + in, out := &in.XXX_unrecognized, &out.XXX_unrecognized + *out = make([]byte, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Fence. +func (in *Fence) DeepCopy() *Fence { + if in == nil { + return nil + } + out := new(Fence) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RecyclingStrategy) DeepCopyInto(out *RecyclingStrategy) { + *out = *in + if in.Stable != nil { + in, out := &in.Stable, &out.Stable + *out = new(RecyclingStrategy_Stable) + (*in).DeepCopyInto(*out) + } + if in.Deadline != nil { + in, out := &in.Deadline, &out.Deadline + *out = new(RecyclingStrategy_Deadline) + (*in).DeepCopyInto(*out) + } + if in.Auto != nil { + in, out := &in.Auto, &out.Auto + *out = new(RecyclingStrategy_Auto) + (*in).DeepCopyInto(*out) + } + if in.RecentlyCalled != nil { + in, out := &in.RecentlyCalled, &out.RecentlyCalled + *out = new(Timestamp) + (*in).DeepCopyInto(*out) + } + out.XXX_NoUnkeyedLiteral = in.XXX_NoUnkeyedLiteral + if in.XXX_unrecognized != nil { + in, out := &in.XXX_unrecognized, &out.XXX_unrecognized + *out = make([]byte, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RecyclingStrategy. +func (in *RecyclingStrategy) DeepCopy() *RecyclingStrategy { + if in == nil { + return nil + } + out := new(RecyclingStrategy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RecyclingStrategy_Auto) DeepCopyInto(out *RecyclingStrategy_Auto) { + *out = *in + if in.Duration != nil { + in, out := &in.Duration, &out.Duration + *out = new(Timestamp) + (*in).DeepCopyInto(*out) + } + out.XXX_NoUnkeyedLiteral = in.XXX_NoUnkeyedLiteral + if in.XXX_unrecognized != nil { + in, out := &in.XXX_unrecognized, &out.XXX_unrecognized + *out = make([]byte, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RecyclingStrategy_Auto. +func (in *RecyclingStrategy_Auto) DeepCopy() *RecyclingStrategy_Auto { + if in == nil { + return nil + } + out := new(RecyclingStrategy_Auto) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RecyclingStrategy_Deadline) DeepCopyInto(out *RecyclingStrategy_Deadline) { + *out = *in + if in.Expire != nil { + in, out := &in.Expire, &out.Expire + *out = new(Timestamp) + (*in).DeepCopyInto(*out) + } + out.XXX_NoUnkeyedLiteral = in.XXX_NoUnkeyedLiteral + if in.XXX_unrecognized != nil { + in, out := &in.XXX_unrecognized, &out.XXX_unrecognized + *out = make([]byte, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RecyclingStrategy_Deadline. +func (in *RecyclingStrategy_Deadline) DeepCopy() *RecyclingStrategy_Deadline { + if in == nil { + return nil + } + out := new(RecyclingStrategy_Deadline) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RecyclingStrategy_Stable) DeepCopyInto(out *RecyclingStrategy_Stable) { + *out = *in + out.XXX_NoUnkeyedLiteral = in.XXX_NoUnkeyedLiteral + if in.XXX_unrecognized != nil { + in, out := &in.XXX_unrecognized, &out.XXX_unrecognized + *out = make([]byte, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RecyclingStrategy_Stable. +func (in *RecyclingStrategy_Stable) DeepCopy() *RecyclingStrategy_Stable { + if in == nil { + return nil + } + out := new(RecyclingStrategy_Stable) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Selector) DeepCopyInto(out *Selector) { + *out = *in + if in.Selector != nil { + in, out := &in.Selector, &out.Selector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + out.XXX_NoUnkeyedLiteral = in.XXX_NoUnkeyedLiteral + if in.XXX_unrecognized != nil { + in, out := &in.XXX_unrecognized, &out.XXX_unrecognized + *out = make([]byte, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Selector. +func (in *Selector) DeepCopy() *Selector { + if in == nil { + return nil + } + out := new(Selector) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceFence) DeepCopyInto(out *ServiceFence) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceFence. +func (in *ServiceFence) DeepCopy() *ServiceFence { + if in == nil { + return nil + } + out := new(ServiceFence) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ServiceFence) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceFenceList) DeepCopyInto(out *ServiceFenceList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ServiceFence, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceFenceList. +func (in *ServiceFenceList) DeepCopy() *ServiceFenceList { + if in == nil { + return nil + } + out := new(ServiceFenceList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ServiceFenceList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceFenceSpec) DeepCopyInto(out *ServiceFenceSpec) { + *out = *in + if in.Host != nil { + in, out := &in.Host, &out.Host + *out = make(map[string]*RecyclingStrategy, len(*in)) + for key, val := range *in { + var outVal *RecyclingStrategy + if val == nil { + (*out)[key] = nil + } else { + in, out := &val, &outVal + *out = new(RecyclingStrategy) + (*in).DeepCopyInto(*out) + } + (*out)[key] = outVal + } + } + if in.NamespaceSelector != nil { + in, out := &in.NamespaceSelector, &out.NamespaceSelector + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.LabelSelector != nil { + in, out := &in.LabelSelector, &out.LabelSelector + *out = make([]*Selector, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(Selector) + (*in).DeepCopyInto(*out) + } + } + } + if in.WorkloadSelector != nil { + in, out := &in.WorkloadSelector, &out.WorkloadSelector + *out = new(WorkloadSelector) + (*in).DeepCopyInto(*out) + } + out.XXX_NoUnkeyedLiteral = in.XXX_NoUnkeyedLiteral + if in.XXX_unrecognized != nil { + in, out := &in.XXX_unrecognized, &out.XXX_unrecognized + *out = make([]byte, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceFenceSpec. +func (in *ServiceFenceSpec) DeepCopy() *ServiceFenceSpec { + if in == nil { + return nil + } + out := new(ServiceFenceSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceFenceStatus) DeepCopyInto(out *ServiceFenceStatus) { + *out = *in + if in.Domains != nil { + in, out := &in.Domains, &out.Domains + *out = make(map[string]*Destinations, len(*in)) + for key, val := range *in { + var outVal *Destinations + if val == nil { + (*out)[key] = nil + } else { + in, out := &val, &outVal + *out = new(Destinations) + (*in).DeepCopyInto(*out) + } + (*out)[key] = outVal + } + } + if in.MetricStatus != nil { + in, out := &in.MetricStatus, &out.MetricStatus + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Visitor != nil { + in, out := &in.Visitor, &out.Visitor + *out = make(map[string]bool, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + out.XXX_NoUnkeyedLiteral = in.XXX_NoUnkeyedLiteral + if in.XXX_unrecognized != nil { + in, out := &in.XXX_unrecognized, &out.XXX_unrecognized + *out = make([]byte, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceFenceStatus. +func (in *ServiceFenceStatus) DeepCopy() *ServiceFenceStatus { + if in == nil { + return nil + } + out := new(ServiceFenceStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Timestamp) DeepCopyInto(out *Timestamp) { + *out = *in + out.XXX_NoUnkeyedLiteral = in.XXX_NoUnkeyedLiteral + if in.XXX_unrecognized != nil { + in, out := &in.XXX_unrecognized, &out.XXX_unrecognized + *out = make([]byte, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Timestamp. +func (in *Timestamp) DeepCopy() *Timestamp { + if in == nil { + return nil + } + out := new(Timestamp) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkloadSelector) DeepCopyInto(out *WorkloadSelector) { + *out = *in + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + out.XXX_NoUnkeyedLiteral = in.XXX_NoUnkeyedLiteral + if in.XXX_unrecognized != nil { + in, out := &in.XXX_unrecognized, &out.XXX_unrecognized + *out = make([]byte, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkloadSelector. +func (in *WorkloadSelector) DeepCopy() *WorkloadSelector { + if in == nil { + return nil + } + out := new(WorkloadSelector) + in.DeepCopyInto(out) + return out +} diff --git a/staging/src/slime.io/slime/modules/lazyload/charts/templates/cluster-global-sidecar.yaml b/staging/src/slime.io/slime/modules/lazyload/charts/templates/cluster-global-sidecar.yaml new file mode 100644 index 00000000..e1a33056 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/charts/templates/cluster-global-sidecar.yaml @@ -0,0 +1,412 @@ +{{ if .Values.component }} +{{ if .Values.component.globalSidecar }} +{{ if .Values.component.globalSidecar.enable }} +{{ $gs := .Values.component.globalSidecar }} + {{ range $.Values.module }} + {{ if or (eq (default "" .name) "lazyload") (eq (default "" .kind) "lazyload") }} + {{ if .global }} + {{ if .global.misc }} + {{ if .global.misc.globalSidecarMode }} + {{ if eq .global.misc.globalSidecarMode "cluster" }} + {{ $f := .general }} + {{ if .fence }} + {{ $f = .fence }} + {{- end -}} + {{ $g := .global }} + {{ $name := .name }} +--- +apiVersion: v1 +kind: Service +metadata: + name: global-sidecar + namespace: {{ $.Values.namespace }} + labels: + app: global-sidecar + service: global-sidecar + slime.io/serviceFenced: "false" +spec: + ports: + {{- range $f.wormholePort }} + - name: http-{{ . }} + port: {{ int . }} + protocol: TCP + targetPort: {{ int . }} + {{- end }} + selector: + app: global-sidecar + sessionAffinity: None + type: ClusterIP +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: global-sidecar + namespace: {{ $.Values.namespace }} + labels: + account: global-sidecar +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: global-sidecar + namespace: {{ $.Values.namespace }} + labels: + app: global-sidecar +spec: + replicas: {{ $gs.replicas | default 1 }} + selector: + matchLabels: + app: global-sidecar + template: + metadata: + annotations: + proxy.istio.io/config: | + proxyMetadata: + ISTIO_META_SLIME_APP: + LAZYLOAD_GLOBAL_SIDECAR + ISTIO_META_ISTIO_VERSION: + "999.0.0" + {{- if $g }} + {{- if $g.misc }} + {{- if $g.misc.metricSourceType }} + {{- if (eq $g.misc.metricSourceType "accesslog") }} + sidecar.istio.io/bootstrapOverride: "lazyload-accesslog-source" + {{- end }} + {{- end }} + {{- end }} + {{- end }} +# {{- if $f.globalSidecar }} +# {{- if $f.globalSidecar.excludeInbounds }} +# traffic.sidecar.istio.io/excludeInboundPorts: {{ $f.globalSidecar.excludeInboundPorts }} +# {{- end }} +# {{- end }} + labels: + app: global-sidecar + {{- if $gs.sidecarInject }} + {{- if $gs.sidecarInject.enable }} + {{- if eq $gs.sidecarInject.mode "pod" }} + {{- with $gs.sidecarInject.labels }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + spec: + serviceAccountName: global-sidecar + containers: + - name: global-sidecar + env: + - name: PROBE_PORT + value: {{ default 18181 $gs.probePort | quote }} + - name: LOG_LEVEL + value: {{ default "info" $g.log.logLevel }} + - name: WORMHOLE_PORTS + value: {{ join "," $f.wormholePort | quote }} + {{- if $gs.image.tag }} + image: "{{ $gs.image.repository }}:{{ $gs.image.tag}}" + {{- else }} + image: "{{ $gs.image.repository }}" + {{- end }} + imagePullPolicy: Always + ports: + {{- range $f.wormholePort }} + - containerPort: {{ int . }} + protocol: TCP + {{- end }} + livenessProbe: + failureThreshold: 3 + httpGet: + path: /healthz/live + port: {{ default 18181 $gs.probePort }} + scheme: HTTP + initialDelaySeconds: 600 + periodSeconds: 30 + successThreshold: 1 + timeoutSeconds: 15 + readinessProbe: + failureThreshold: 30 + httpGet: + path: /healthz/ready + port: {{ default 18181 $gs.probePort }} + scheme: HTTP + initialDelaySeconds: 1 + periodSeconds: 2 + successThreshold: 1 + timeoutSeconds: 1 + resources: + {{- toYaml $gs.resources | nindent 12 }} + securityContext: + runAsUser: 1000 +--- + {{- if or (not $g) (not $g.misc) (eq (default "prometheus" $g.misc.metricSourceType) "prometheus") }} +apiVersion: networking.istio.io/v1alpha3 +kind: EnvoyFilter +metadata: + name: global-sidecar-metadata-exchange-remove + namespace: {{ $.Values.namespace }} +spec: + configPatches: + - applyTo: HTTP_FILTER + match: + proxy: + metadata: + SLIME_APP: LAZYLOAD_GLOBAL_SIDECAR + context: SIDECAR_INBOUND + listener: + filterChain: + filter: + name: envoy.filters.network.http_connection_manager + subFilter: + name: istio.metadata_exchange + patch: + operation: REMOVE + - applyTo: HTTP_FILTER + match: + proxy: + metadata: + SLIME_APP: LAZYLOAD_GLOBAL_SIDECAR + context: SIDECAR_OUTBOUND + listener: + filterChain: + filter: + name: envoy.filters.network.http_connection_manager + subFilter: + name: istio.metadata_exchange + patch: + operation: REMOVE + - applyTo: HTTP_FILTER + match: + proxy: + metadata: + SLIME_APP: LAZYLOAD_GLOBAL_SIDECAR + context: GATEWAY + listener: + filterChain: + filter: + name: envoy.filters.network.http_connection_manager + subFilter: + name: istio.metadata_exchange + patch: + operation: REMOVE + {{- end }} +--- +apiVersion: networking.istio.io/v1alpha3 +kind: EnvoyFilter +metadata: + name: to-global-sidecar + namespace: {{ $.Values.istioNamespace }} +spec: + configPatches: + {{- range $f.wormholePort}} + - applyTo: VIRTUAL_HOST + match: + context: SIDECAR_OUTBOUND + routeConfiguration: + name: "{{ . }}" + vhost: + name: allow_any + patch: + operation: REMOVE + - applyTo: ROUTE_CONFIGURATION + match: + context: SIDECAR_OUTBOUND + routeConfiguration: + name: "{{ . }}" + patch: + operation: MERGE + value: + virtual_hosts: + {{- if $f.dispatches }} + {{- $svcPort := . }} + {{- range $f.dispatches }} + - domains: {{ toYaml .domains | nindent 12 }} + name: {{ .name }} + routes: + - match: + prefix: / + route: + {{- if eq .cluster "_GLOBAL_SIDECAR" }} + cluster: outbound|{{$svcPort}}||global-sidecar.{{ $.Values.namespace }}.svc.cluster.local + {{- else }} + cluster: {{ tpl .cluster $ }} + # (dict "fence" $f "dispatch" . "root" $ "Template" (dict "BasePath" "xx")) + {{- end }} + {{- end }} + {{ else }} + - domains: + - '*' + name: to_global_sidecar + routes: + - match: + prefix: / + route: + cluster: outbound|{{.}}||global-sidecar.{{ $.Values.namespace }}.svc.cluster.local + {{- end }} + request_headers_to_add: + - header: + key: "Slime-Orig-Dest" + value: "%DOWNSTREAM_LOCAL_ADDRESS%" + append: true + - applyTo: HTTP_FILTER + match: + context: SIDECAR_OUTBOUND + listener: + name: 0.0.0.0_{{ . }} + filterChain: + filter: + name: "envoy.filters.network.http_connection_manager" + subFilter: + name: "envoy.filters.http.router" + patch: + operation: INSERT_BEFORE + value: + name: envoy.filters.http.lua + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua + inline_code: | + -- place holder + - applyTo: HTTP_ROUTE + match : + context: SIDECAR_OUTBOUND + routeConfiguration: + name: "{{ . }}" + vhost: + name: to_global_sidecar + patch: + operation: MERGE + value: + typed_per_filter_config: + envoy.filters.http.lua: + "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.LuaPerRoute + source_code: + inline_string: | + function envoy_on_request(request_handle) + request_handle:headers():replace("Slime-Source-Ns", os.getenv("POD_NAMESPACE")) + end + - applyTo: VIRTUAL_HOST + match: + proxy: + metadata: + SLIME_APP: LAZYLOAD_GLOBAL_SIDECAR + context: SIDECAR_OUTBOUND + routeConfiguration: + name: "{{ . }}" + vhost: + name: to_global_sidecar + patch: + operation: REMOVE + - applyTo: ROUTE_CONFIGURATION + match: + proxy: + metadata: + SLIME_APP: LAZYLOAD_GLOBAL_SIDECAR + context: SIDECAR_OUTBOUND + routeConfiguration: + name: "{{ . }}" + patch: + operation: MERGE + value: + virtual_hosts: + - domains: + - '*' + name: allow_any_new + routes: + - match: + prefix: / + route: + cluster: PassthroughCluster + {{- end }} +--- +{{- if $g }} +{{- if $g.misc }} +{{- if $g.misc.metricSourceType }} +{{- if (eq $g.misc.metricSourceType "accesslog") }} +apiVersion: networking.istio.io/v1alpha3 +kind: EnvoyFilter +metadata: + name: global-sidecar-accesslog + namespace: {{ $.Values.namespace }} +spec: + workloadSelector: + labels: + app: global-sidecar + configPatches: + - applyTo: NETWORK_FILTER + match: + #context: GATEWAY + listener: + filterChain: + filter: + name: "envoy.filters.network.http_connection_manager" + patch: + operation: MERGE + value: + typed_config: + "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager" + access_log: + - name: envoy.access_loggers.http_grpc + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.grpc.v3.HttpGrpcAccessLogConfig + common_config: + log_name: http_envoy_accesslog + transport_api_version: "V3" + grpc_service: + envoy_grpc: + #cluster_name: outbound|{{$.Values.service.logSourcePort}}||{{$.Values.name}}.{{$.Values.namespace}}.svc.cluster.local + cluster_name: lazyload-accesslog-source + {{- end }} + {{- end }} + {{- end }} + {{- end }} +--- +{{- if $g }} +{{- if $g.misc }} +{{- if $g.misc.metricSourceType }} +{{- if (eq $g.misc.metricSourceType "accesslog") }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: lazyload-accesslog-source + namespace: {{ $.Values.namespace }} +data: + custom_bootstrap.json: | + { + "static_resources": { + "clusters": [{ + "name": "lazyload-accesslog-source", + "type": "STRICT_DNS", + "connect_timeout": "5s", + "http2_protocol_options": {}, + "dns_lookup_family": "V4_ONLY", + "load_assignment": { + "cluster_name": "lazyload-accesslog-source", + "endpoints": [{ + "lb_endpoints": [{ + "endpoint": { + "address": { + "socket_address": { + "address": "{{ .name }}.{{ $.Values.namespace }}", + "port_value": {{ $.Values.service.logSourcePort }} + } + } + } + }] + }] + }, + "respect_dns_ttl": true + }] + } + } +{{- end }} +{{- end }} +{{- end }} +{{- end }} +{{ end }} +{{ end }} +{{ end }} +{{ end }} +{{ end }} +{{ end }} +{{ end }} +{{ end }} +{{ end }} diff --git a/staging/src/slime.io/slime/modules/lazyload/charts/templates/namespace-global-sidecar.yaml b/staging/src/slime.io/slime/modules/lazyload/charts/templates/namespace-global-sidecar.yaml new file mode 100644 index 00000000..fa07c0d4 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/charts/templates/namespace-global-sidecar.yaml @@ -0,0 +1,382 @@ +{{ if .Values.component }} + {{ if .Values.component.globalSidecar }} + {{ if .Values.component.globalSidecar.enable }} + {{ $gs := .Values.component.globalSidecar }} + {{ range $.Values.module }} + {{ if or (eq (default "" .name) "lazyload") (eq (default "" .kind) "lazyload") }} + {{ if .global }} + {{ if .global.misc }} + {{ if .global.misc.globalSidecarMode }} + {{ if eq .global.misc.globalSidecarMode "namespace" }} + {{ $f := .general }} + {{ if .fence }} + {{ $f = .fence }} + {{- end -}} + {{ $g := .global }} + {{ $name := .name }} + {{ range $_, $ns := $f.namespace }} +--- +apiVersion: v1 +kind: Service +metadata: + name: global-sidecar + namespace: {{ $ns }} + labels: + app: global-sidecar + service: global-sidecar + slime.io/serviceFenced: "false" +spec: + ports: + {{- range $f.wormholePort }} + - name: http-{{ . }} + port: {{ int . }} + protocol: TCP + targetPort: {{ int . }} + {{- end }} + selector: + app: global-sidecar + sessionAffinity: None + type: ClusterIP +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: global-sidecar + namespace: {{ $ns }} + labels: + account: global-sidecar +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: global-sidecar + namespace: {{ $ns }} + labels: + app: global-sidecar +spec: + replicas: {{ $gs.replicas | default 1 }} + selector: + matchLabels: + app: global-sidecar + template: + metadata: + annotations: + proxy.istio.io/config: | + proxyMetadata: + ISTIO_META_SLIME_APP: + LAZYLOAD_GLOBAL_SIDECAR + ISTIO_META_ISTIO_VERSION: + "999.0.0" + {{- if $g }} + {{- if $g.misc }} + {{- if $g.misc.metricSourceType }} + {{- if (eq $g.misc.metricSourceType "accesslog") }} + sidecar.istio.io/bootstrapOverride: "lazyload-accesslog-source" + {{- end }} + {{- end }} + {{- end }} + {{- end }} +# {{- if $f.globalSidecar }} +# {{- if $f.globalSidecar.excludeInbounds }} +# traffic.sidecar.istio.io/excludeInboundPorts: {{ $f.globalSidecar.excludeInboundPorts }} +# {{- end }} +# {{- end }} + labels: + app: global-sidecar + {{- if $gs.sidecarInject }} + {{- if $gs.sidecarInject.enable }} + {{- if eq $gs.sidecarInject.mode "pod" }} + {{- with $gs.sidecarInject.labels }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + spec: + serviceAccountName: global-sidecar + containers: + - name: global-sidecar + env: + - name: PROBE_PORT + value: {{ default 18181 $gs.probePort | quote }} + - name: LOG_LEVEL + value: {{ default "info" $g.log.logLevel }} + - name: WORMHOLE_PORTS + value: {{ join "," $f.wormholePort | quote }} + {{- if $gs.image.tag }} + image: "{{ $gs.image.repository }}:{{ $gs.image.tag }}" + {{- else }} + image: "{{ $gs.image.repository }}" + {{- end }} + imagePullPolicy: Always + ports: + {{- range $f.wormholePort }} + - containerPort: {{ int . }} + protocol: TCP + {{- end }} + livenessProbe: + failureThreshold: 3 + httpGet: + path: /healthz/live + port: {{ default 18181 $gs.probePort }} + scheme: HTTP + initialDelaySeconds: 600 + periodSeconds: 30 + successThreshold: 1 + timeoutSeconds: 15 + readinessProbe: + failureThreshold: 30 + httpGet: + path: /healthz/ready + port: {{ default 18181 $gs.probePort }} + scheme: HTTP + initialDelaySeconds: 1 + periodSeconds: 2 + successThreshold: 1 + timeoutSeconds: 1 + resources: + {{- toYaml $gs.resources | nindent 12 }} + securityContext: + runAsUser: 1000 +--- + {{- if or (not $g) (not $g.misc) (eq (default "prometheus" $g.misc.metricSourceType) "prometheus") }} +apiVersion: networking.istio.io/v1alpha3 +kind: EnvoyFilter +metadata: + name: global-sidecar-metadata-exchange-remove + namespace: {{ $ns }} +spec: + configPatches: + - applyTo: HTTP_FILTER + match: + proxy: + metadata: + SLIME_APP: LAZYLOAD_GLOBAL_SIDECAR + context: SIDECAR_INBOUND + listener: + filterChain: + filter: + name: envoy.filters.network.http_connection_manager + subFilter: + name: istio.metadata_exchange + patch: + operation: REMOVE + - applyTo: HTTP_FILTER + match: + proxy: + metadata: + SLIME_APP: LAZYLOAD_GLOBAL_SIDECAR + context: SIDECAR_OUTBOUND + listener: + filterChain: + filter: + name: envoy.filters.network.http_connection_manager + subFilter: + name: istio.metadata_exchange + patch: + operation: REMOVE + - applyTo: HTTP_FILTER + match: + proxy: + metadata: + SLIME_APP: LAZYLOAD_GLOBAL_SIDECAR + context: GATEWAY + listener: + filterChain: + filter: + name: envoy.filters.network.http_connection_manager + subFilter: + name: istio.metadata_exchange + patch: + operation: REMOVE + {{- end }} +--- +apiVersion: networking.istio.io/v1alpha3 +kind: EnvoyFilter +metadata: + name: to-global-sidecar + namespace: {{ $ns }} +spec: + configPatches: + {{- range $f.wormholePort}} + - applyTo: VIRTUAL_HOST + match: + context: SIDECAR_OUTBOUND + routeConfiguration: + name: "{{ . }}" + vhost: + name: allow_any + patch: + operation: REMOVE + - applyTo: ROUTE_CONFIGURATION + match: + context: SIDECAR_OUTBOUND + routeConfiguration: + name: "{{ . }}" + patch: + operation: MERGE + value: + virtual_hosts: + {{- if $f.dispatches }} + {{- $svcPort := . }} + {{- range $f.dispatches }} + - domains: {{ toYaml .domains | nindent 12 }} + name: {{ .name }} + routes: + - match: + prefix: / + route: + {{- if eq .cluster "_GLOBAL_SIDECAR" }} + cluster: outbound|{{$svcPort}}||global-sidecar.{{ $ns }}.svc.cluster.local + {{- else }} + cluster: {{ tpl .cluster $ }} + # (dict "fence" $f "dispatch" . "root" $ "Template" (dict "BasePath" "xx")) + {{- end }} + {{- end }} + {{ else }} + - domains: + - '*' + name: to_global_sidecar + routes: + - match: + prefix: / + route: + cluster: outbound|{{.}}||global-sidecar.{{ $ns }}.svc.cluster.local + {{- end }} + request_headers_to_add: + - header: + key: "Slime-Orig-Dest" + value: "%DOWNSTREAM_LOCAL_ADDRESS%" + append: true + - header: + key: "Slime-Source-Ns" + value: {{ $ns }} + append: true + - applyTo: VIRTUAL_HOST + match: + proxy: + metadata: + SLIME_APP: LAZYLOAD_GLOBAL_SIDECAR + context: SIDECAR_OUTBOUND + routeConfiguration: + name: "{{ . }}" + vhost: + name: to_global_sidecar + patch: + operation: REMOVE + - applyTo: ROUTE_CONFIGURATION + match: + proxy: + metadata: + SLIME_APP: LAZYLOAD_GLOBAL_SIDECAR + context: SIDECAR_OUTBOUND + routeConfiguration: + name: "{{ . }}" + patch: + operation: MERGE + value: + virtual_hosts: + - domains: + - '*' + name: allow_any_new + routes: + - match: + prefix: / + route: + cluster: PassthroughCluster + {{- end }} +--- + {{- if $g }} + {{- if $g.misc }} + {{- if $g.misc.metricSourceType }} + {{- if (eq $g.misc.metricSourceType "accesslog") }} +apiVersion: networking.istio.io/v1alpha3 +kind: EnvoyFilter +metadata: + name: global-sidecar-accesslog + namespace: {{ $ns }} +spec: + workloadSelector: + labels: + app: global-sidecar + configPatches: + - applyTo: NETWORK_FILTER + match: + #context: GATEWAY + listener: + filterChain: + filter: + name: "envoy.filters.network.http_connection_manager" + patch: + operation: MERGE + value: + typed_config: + "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager" + access_log: + - name: envoy.access_loggers.http_grpc + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.grpc.v3.HttpGrpcAccessLogConfig + common_config: + log_name: http_envoy_accesslog + transport_api_version: "V3" + grpc_service: + envoy_grpc: + #cluster_name: outbound|{{$.Values.service.logSourcePort}}||{{$.Values.name}}.{{$.Values.namespace}}.svc.cluster.local + cluster_name: lazyload-accesslog-source + {{- end }} + {{- end }} + {{- end }} + {{- end }} +--- + {{- if $g }} + {{- if $g.misc }} + {{- if $g.misc.metricSourceType }} + {{- if (eq $g.misc.metricSourceType "accesslog") }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: lazyload-accesslog-source + namespace: {{ $ns }} +data: + custom_bootstrap.json: | + { + "static_resources": { + "clusters": [{ + "name": "lazyload-accesslog-source", + "type": "STRICT_DNS", + "connect_timeout": "5s", + "http2_protocol_options": {}, + "dns_lookup_family": "V4_ONLY", + "load_assignment": { + "cluster_name": "lazyload-accesslog-source", + "endpoints": [{ + "lb_endpoints": [{ + "endpoint": { + "address": { + "socket_address": { + "address": "{{ $name }}.{{ $.Values.namespace }}", + "port_value": {{ $.Values.service.logSourcePort }} + } + } + } + }] + }] + }, + "respect_dns_ttl": true + }] + } + } + {{- end }} + {{- end }} + {{- end }} + {{- end }} +{{ end }} +{{ end }} +{{ end }} +{{ end }} +{{ end }} +{{ end }} +{{ end }} +{{ end }} +{{ end }} +{{ end }} diff --git a/staging/src/slime.io/slime/modules/lazyload/charts/values.yaml b/staging/src/slime.io/slime/modules/lazyload/charts/values.yaml new file mode 100644 index 00000000..e69de29b diff --git a/staging/src/slime.io/slime/modules/lazyload/cmd/proxy/Dockerfile b/staging/src/slime.io/slime/modules/lazyload/cmd/proxy/Dockerfile new file mode 100644 index 00000000..083fca9b --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/cmd/proxy/Dockerfile @@ -0,0 +1,8 @@ +# Use distroless as minimal base image to package the manager binary +# Refer to https://github.com/GoogleContainerTools/distroless for more details +FROM ubuntu:bionic +WORKDIR / + +COPY manager.exe ./proxy-app + +ENTRYPOINT ["/proxy-app"] diff --git a/staging/src/slime.io/slime/modules/lazyload/cmd/proxy/main.go b/staging/src/slime.io/slime/modules/lazyload/cmd/proxy/main.go new file mode 100644 index 00000000..9688eb93 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/cmd/proxy/main.go @@ -0,0 +1,80 @@ +package main + +import ( + "net/http" + "os" + "strconv" + "strings" + "sync" + "time" + + log "github.com/sirupsen/logrus" + + "slime.io/slime/modules/lazyload/pkg/proxy" +) + +const ( + EnvWormholePorts = "WORMHOLE_PORTS" + EnvProbePort = "PROBE_PORT" + EnvLogLevel = "LOG_LEVEL" +) + +func main() { + // set log config + logLevel := os.Getenv(EnvLogLevel) + if logLevel == "" { + logLevel = "info" + } + level, err := log.ParseLevel(logLevel) + if err != nil { + os.Exit(1) + } + log.SetLevel(level) + log.SetFormatter(&log.TextFormatter{ + TimestampFormat: time.RFC3339, + }) + + // start health check server + probePort := ":" + os.Getenv(EnvProbePort) + go func() { + handler := &proxy.HealthzProxy{} + log.Println("Starting health check on", probePort) + if err := http.ListenAndServe(probePort, handler); err != nil { + log.Fatal("ListenAndServe:", err) + } + }() + + // start multi ports defined in WormholePorts + wormholePorts := os.Getenv(EnvWormholePorts) + wormholePortsArr := strings.Split(wormholePorts, ",") + var whPorts []int + for _, whp := range wormholePortsArr { + if whp == probePort { + log.Errorf("wormholePort can not be %s, which is reserved for health check", probePort) + os.Exit(1) + } + + p, err := strconv.Atoi(whp) + if err != nil { + log.Errorf("wrong wormholePort value %s", whp) + os.Exit(1) + } + whPorts = append(whPorts, p) + } + + var wg sync.WaitGroup + for _, whPort := range whPorts { + wg.Add(1) + handler := &proxy.Proxy{WormholePort: whPort} + go func(whPort int) { + log.Println("Starting proxy on", "0.0.0.0"+":"+strconv.Itoa(whPort)) + if err := http.ListenAndServe("0.0.0.0"+":"+strconv.Itoa(whPort), handler); err != nil { + log.Fatal("Proxy ListenAndServe error:", err) + } + wg.Done() + }(whPort) + } + + wg.Wait() + log.Infof("All servers exited.") +} diff --git a/staging/src/slime.io/slime/modules/lazyload/cmd/proxy/publish.sh b/staging/src/slime.io/slime/modules/lazyload/cmd/proxy/publish.sh new file mode 100644 index 00000000..ddd3da9e --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/cmd/proxy/publish.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +export MOD=global-sidecar +../../../slime/bin/publish.sh "$@" diff --git a/staging/src/slime.io/slime/modules/lazyload/config/certmanager/certificate.yaml b/staging/src/slime.io/slime/modules/lazyload/config/certmanager/certificate.yaml new file mode 100644 index 00000000..58db114f --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/config/certmanager/certificate.yaml @@ -0,0 +1,26 @@ +# The following manifests contain a self-signed issuer CR and a certificate CR. +# More document can be found at https://docs.cert-manager.io +# WARNING: Targets CertManager 0.11 check https://docs.cert-manager.io/en/latest/tasks/upgrading/index.html for +# breaking changes +apiVersion: cert-manager.io/v1alpha2 +kind: Issuer +metadata: + name: selfsigned-issuer + namespace: system +spec: + selfSigned: {} +--- +apiVersion: cert-manager.io/v1alpha2 +kind: Certificate +metadata: + name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml + namespace: system +spec: + # $(SERVICE_NAME) and $(SERVICE_NAMESPACE) will be substituted by kustomize + dnsNames: + - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc + - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc.cluster.local + issuerRef: + kind: Issuer + name: selfsigned-issuer + secretName: webhook-server-cert # this secret will not be prefixed, since it's not managed by kustomize diff --git a/staging/src/slime.io/slime/modules/lazyload/config/certmanager/kustomization.yaml b/staging/src/slime.io/slime/modules/lazyload/config/certmanager/kustomization.yaml new file mode 100644 index 00000000..bebea5a5 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/config/certmanager/kustomization.yaml @@ -0,0 +1,5 @@ +resources: +- certificate.yaml + +configurations: +- kustomizeconfig.yaml diff --git a/staging/src/slime.io/slime/modules/lazyload/config/certmanager/kustomizeconfig.yaml b/staging/src/slime.io/slime/modules/lazyload/config/certmanager/kustomizeconfig.yaml new file mode 100644 index 00000000..90d7c313 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/config/certmanager/kustomizeconfig.yaml @@ -0,0 +1,16 @@ +# This configuration is for teaching kustomize how to update name ref and var substitution +nameReference: +- kind: Issuer + group: cert-manager.io + fieldSpecs: + - kind: Certificate + group: cert-manager.io + path: spec/issuerRef/name + +varReference: +- kind: Certificate + group: cert-manager.io + path: spec/commonName +- kind: Certificate + group: cert-manager.io + path: spec/dnsNames diff --git a/staging/src/slime.io/slime/modules/lazyload/config/crd/kustomization.yaml b/staging/src/slime.io/slime/modules/lazyload/config/crd/kustomization.yaml new file mode 100644 index 00000000..5e1a4660 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/config/crd/kustomization.yaml @@ -0,0 +1,21 @@ +# This kustomization.yaml is not intended to be run by itself, +# since it depends on service name and namespace that are out of this kustomize package. +# It should be run by config/default +resources: +- bases/microservice.slime.io_servicefences.yaml +# +kubebuilder:scaffold:crdkustomizeresource + +patchesStrategicMerge: +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. +# patches here are for enabling the conversion webhook for each CRD +#- patches/webhook_in_servicefences.yaml +# +kubebuilder:scaffold:crdkustomizewebhookpatch + +# [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix. +# patches here are for enabling the CA injection for each CRD +#- patches/cainjection_in_servicefences.yaml +# +kubebuilder:scaffold:crdkustomizecainjectionpatch + +# the following config is for teaching kustomize how to do kustomization for CRDs. +configurations: +- kustomizeconfig.yaml diff --git a/staging/src/slime.io/slime/modules/lazyload/config/crd/kustomizeconfig.yaml b/staging/src/slime.io/slime/modules/lazyload/config/crd/kustomizeconfig.yaml new file mode 100644 index 00000000..6f83d9a9 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/config/crd/kustomizeconfig.yaml @@ -0,0 +1,17 @@ +# This file is for teaching kustomize how to substitute name and namespace reference in CRD +nameReference: +- kind: Service + version: v1 + fieldSpecs: + - kind: CustomResourceDefinition + group: apiextensions.k8s.io + path: spec/conversion/webhookClientConfig/service/name + +namespace: +- kind: CustomResourceDefinition + group: apiextensions.k8s.io + path: spec/conversion/webhookClientConfig/service/namespace + create: false + +varReference: +- path: metadata/annotations diff --git a/staging/src/slime.io/slime/modules/lazyload/config/crd/patches/cainjection_in_servicefences.yaml b/staging/src/slime.io/slime/modules/lazyload/config/crd/patches/cainjection_in_servicefences.yaml new file mode 100644 index 00000000..ea087df5 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/config/crd/patches/cainjection_in_servicefences.yaml @@ -0,0 +1,8 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: servicefences.microservice.slime.io diff --git a/staging/src/slime.io/slime/modules/lazyload/config/crd/patches/webhook_in_servicefences.yaml b/staging/src/slime.io/slime/modules/lazyload/config/crd/patches/webhook_in_servicefences.yaml new file mode 100644 index 00000000..c33ec7f5 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/config/crd/patches/webhook_in_servicefences.yaml @@ -0,0 +1,17 @@ +# The following patch enables conversion webhook for CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: servicefences.microservice.slime.io +spec: + conversion: + strategy: Webhook + webhookClientConfig: + # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, + # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) + caBundle: Cg== + service: + namespace: system + name: webhook-service + path: /convert diff --git a/staging/src/slime.io/slime/modules/lazyload/config/default/kustomization.yaml b/staging/src/slime.io/slime/modules/lazyload/config/default/kustomization.yaml new file mode 100644 index 00000000..d97246b6 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/config/default/kustomization.yaml @@ -0,0 +1,70 @@ +# Adds namespace to all resources. +namespace: lazyload-system + +# Value of this field is prepended to the +# names of all resources, e.g. a deployment named +# "wordpress" becomes "alices-wordpress". +# Note that it should also match with the prefix (text before '-') of the namespace +# field above. +namePrefix: lazyload- + +# Labels to add to all resources and selectors. +#commonLabels: +# someName: someValue + +bases: +- ../crd +- ../rbac +- ../manager +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in +# crd/kustomization.yaml +#- ../webhook +# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. +#- ../certmanager +# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. +#- ../prometheus + +patchesStrategicMerge: + # Protect the /metrics endpoint by putting it behind auth. + # If you want your controller-manager to expose the /metrics + # endpoint w/o any authn/z, please comment the following line. +- manager_auth_proxy_patch.yaml + +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in +# crd/kustomization.yaml +#- manager_webhook_patch.yaml + +# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. +# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks. +# 'CERTMANAGER' needs to be enabled to use ca injection +#- webhookcainjection_patch.yaml + +# the following config is for teaching kustomize how to do var substitution +vars: +# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. +#- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR +# objref: +# kind: Certificate +# group: cert-manager.io +# version: v1alpha2 +# name: serving-cert # this name should match the one in certificate.yaml +# fieldref: +# fieldpath: metadata.namespace +#- name: CERTIFICATE_NAME +# objref: +# kind: Certificate +# group: cert-manager.io +# version: v1alpha2 +# name: serving-cert # this name should match the one in certificate.yaml +#- name: SERVICE_NAMESPACE # namespace of the service +# objref: +# kind: Service +# version: v1 +# name: webhook-service +# fieldref: +# fieldpath: metadata.namespace +#- name: SERVICE_NAME +# objref: +# kind: Service +# version: v1 +# name: webhook-service diff --git a/staging/src/slime.io/slime/modules/lazyload/config/default/manager_auth_proxy_patch.yaml b/staging/src/slime.io/slime/modules/lazyload/config/default/manager_auth_proxy_patch.yaml new file mode 100644 index 00000000..77e743d1 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/config/default/manager_auth_proxy_patch.yaml @@ -0,0 +1,25 @@ +# This patch inject a sidecar container which is a HTTP proxy for the +# controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system +spec: + template: + spec: + containers: + - name: kube-rbac-proxy + image: gcr.io/kubebuilder/kube-rbac-proxy:v0.5.0 + args: + - "--secure-listen-address=0.0.0.0:8443" + - "--upstream=http://127.0.0.1:8080/" + - "--logtostderr=true" + - "--v=10" + ports: + - containerPort: 8443 + name: https + - name: manager + args: + - "--metrics-addr=127.0.0.1:8080" + - "--enable-leader-election" diff --git a/staging/src/slime.io/slime/modules/lazyload/config/default/manager_webhook_patch.yaml b/staging/src/slime.io/slime/modules/lazyload/config/default/manager_webhook_patch.yaml new file mode 100644 index 00000000..738de350 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/config/default/manager_webhook_patch.yaml @@ -0,0 +1,23 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system +spec: + template: + spec: + containers: + - name: manager + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert + readOnly: true + volumes: + - name: cert + secret: + defaultMode: 420 + secretName: webhook-server-cert diff --git a/staging/src/slime.io/slime/modules/lazyload/config/default/webhookcainjection_patch.yaml b/staging/src/slime.io/slime/modules/lazyload/config/default/webhookcainjection_patch.yaml new file mode 100644 index 00000000..7e79bf99 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/config/default/webhookcainjection_patch.yaml @@ -0,0 +1,15 @@ +# This patch add annotation to admission webhook config and +# the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize. +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: MutatingWebhookConfiguration +metadata: + name: mutating-webhook-configuration + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) +--- +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: ValidatingWebhookConfiguration +metadata: + name: validating-webhook-configuration + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) diff --git a/staging/src/slime.io/slime/modules/lazyload/config/manager/kustomization.yaml b/staging/src/slime.io/slime/modules/lazyload/config/manager/kustomization.yaml new file mode 100644 index 00000000..5c5f0b84 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/config/manager/kustomization.yaml @@ -0,0 +1,2 @@ +resources: +- manager.yaml diff --git a/staging/src/slime.io/slime/modules/lazyload/config/manager/manager.yaml b/staging/src/slime.io/slime/modules/lazyload/config/manager/manager.yaml new file mode 100644 index 00000000..b6c85a52 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/config/manager/manager.yaml @@ -0,0 +1,39 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + control-plane: controller-manager + name: system +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system + labels: + control-plane: controller-manager +spec: + selector: + matchLabels: + control-plane: controller-manager + replicas: 1 + template: + metadata: + labels: + control-plane: controller-manager + spec: + containers: + - command: + - /manager + args: + - --enable-leader-election + image: controller:latest + name: manager + resources: + limits: + cpu: 100m + memory: 30Mi + requests: + cpu: 100m + memory: 20Mi + terminationGracePeriodSeconds: 10 diff --git a/staging/src/slime.io/slime/modules/lazyload/config/prometheus/kustomization.yaml b/staging/src/slime.io/slime/modules/lazyload/config/prometheus/kustomization.yaml new file mode 100644 index 00000000..ed137168 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/config/prometheus/kustomization.yaml @@ -0,0 +1,2 @@ +resources: +- monitor.yaml diff --git a/staging/src/slime.io/slime/modules/lazyload/config/prometheus/monitor.yaml b/staging/src/slime.io/slime/modules/lazyload/config/prometheus/monitor.yaml new file mode 100644 index 00000000..9b8047b7 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/config/prometheus/monitor.yaml @@ -0,0 +1,16 @@ + +# Prometheus Monitor Service (Metrics) +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + labels: + control-plane: controller-manager + name: controller-manager-metrics-monitor + namespace: system +spec: + endpoints: + - path: /metrics + port: https + selector: + matchLabels: + control-plane: controller-manager diff --git a/staging/src/slime.io/slime/modules/lazyload/config/rbac/auth_proxy_client_clusterrole.yaml b/staging/src/slime.io/slime/modules/lazyload/config/rbac/auth_proxy_client_clusterrole.yaml new file mode 100644 index 00000000..7d62534c --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/config/rbac/auth_proxy_client_clusterrole.yaml @@ -0,0 +1,7 @@ +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + name: metrics-reader +rules: +- nonResourceURLs: ["/metrics"] + verbs: ["get"] diff --git a/staging/src/slime.io/slime/modules/lazyload/config/rbac/auth_proxy_role.yaml b/staging/src/slime.io/slime/modules/lazyload/config/rbac/auth_proxy_role.yaml new file mode 100644 index 00000000..618f5e41 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/config/rbac/auth_proxy_role.yaml @@ -0,0 +1,13 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: proxy-role +rules: +- apiGroups: ["authentication.k8s.io"] + resources: + - tokenreviews + verbs: ["create"] +- apiGroups: ["authorization.k8s.io"] + resources: + - subjectaccessreviews + verbs: ["create"] diff --git a/staging/src/slime.io/slime/modules/lazyload/config/rbac/auth_proxy_role_binding.yaml b/staging/src/slime.io/slime/modules/lazyload/config/rbac/auth_proxy_role_binding.yaml new file mode 100644 index 00000000..48ed1e4b --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/config/rbac/auth_proxy_role_binding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: proxy-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: proxy-role +subjects: +- kind: ServiceAccount + name: default + namespace: system diff --git a/staging/src/slime.io/slime/modules/lazyload/config/rbac/auth_proxy_service.yaml b/staging/src/slime.io/slime/modules/lazyload/config/rbac/auth_proxy_service.yaml new file mode 100644 index 00000000..6cf656be --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/config/rbac/auth_proxy_service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + control-plane: controller-manager + name: controller-manager-metrics-service + namespace: system +spec: + ports: + - name: https + port: 8443 + targetPort: https + selector: + control-plane: controller-manager diff --git a/staging/src/slime.io/slime/modules/lazyload/config/rbac/kustomization.yaml b/staging/src/slime.io/slime/modules/lazyload/config/rbac/kustomization.yaml new file mode 100644 index 00000000..66c28338 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/config/rbac/kustomization.yaml @@ -0,0 +1,12 @@ +resources: +- role.yaml +- role_binding.yaml +- leader_election_role.yaml +- leader_election_role_binding.yaml +# Comment the following 4 lines if you want to disable +# the auth proxy (https://github.com/brancz/kube-rbac-proxy) +# which protects your /metrics endpoint. +- auth_proxy_service.yaml +- auth_proxy_role.yaml +- auth_proxy_role_binding.yaml +- auth_proxy_client_clusterrole.yaml diff --git a/staging/src/slime.io/slime/modules/lazyload/config/rbac/leader_election_role.yaml b/staging/src/slime.io/slime/modules/lazyload/config/rbac/leader_election_role.yaml new file mode 100644 index 00000000..eaa79158 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/config/rbac/leader_election_role.yaml @@ -0,0 +1,32 @@ +# permissions to do leader election. +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: leader-election-role +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - configmaps/status + verbs: + - get + - update + - patch +- apiGroups: + - "" + resources: + - events + verbs: + - create diff --git a/staging/src/slime.io/slime/modules/lazyload/config/rbac/leader_election_role_binding.yaml b/staging/src/slime.io/slime/modules/lazyload/config/rbac/leader_election_role_binding.yaml new file mode 100644 index 00000000..eed16906 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/config/rbac/leader_election_role_binding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: leader-election-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: leader-election-role +subjects: +- kind: ServiceAccount + name: default + namespace: system diff --git a/staging/src/slime.io/slime/modules/lazyload/config/rbac/role_binding.yaml b/staging/src/slime.io/slime/modules/lazyload/config/rbac/role_binding.yaml new file mode 100644 index 00000000..8f265870 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/config/rbac/role_binding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: manager-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: system diff --git a/staging/src/slime.io/slime/modules/lazyload/config/rbac/servicefence_editor_role.yaml b/staging/src/slime.io/slime/modules/lazyload/config/rbac/servicefence_editor_role.yaml new file mode 100644 index 00000000..4af5f11a --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/config/rbac/servicefence_editor_role.yaml @@ -0,0 +1,24 @@ +# permissions for end users to edit servicefences. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: servicefence-editor-role +rules: +- apiGroups: + - microservice.slime.io + resources: + - servicefences + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - microservice.slime.io + resources: + - servicefences/status + verbs: + - get diff --git a/staging/src/slime.io/slime/modules/lazyload/config/rbac/servicefence_viewer_role.yaml b/staging/src/slime.io/slime/modules/lazyload/config/rbac/servicefence_viewer_role.yaml new file mode 100644 index 00000000..61924173 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/config/rbac/servicefence_viewer_role.yaml @@ -0,0 +1,20 @@ +# permissions for end users to view servicefences. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: servicefence-viewer-role +rules: +- apiGroups: + - microservice.slime.io + resources: + - servicefences + verbs: + - get + - list + - watch +- apiGroups: + - microservice.slime.io + resources: + - servicefences/status + verbs: + - get diff --git a/staging/src/slime.io/slime/modules/lazyload/config/samples/microservice.slime.io_v1alpha1_servicefence.yaml b/staging/src/slime.io/slime/modules/lazyload/config/samples/microservice.slime.io_v1alpha1_servicefence.yaml new file mode 100644 index 00000000..125dc7b0 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/config/samples/microservice.slime.io_v1alpha1_servicefence.yaml @@ -0,0 +1,7 @@ +apiVersion: microservice.slime.io/v1alpha1 +kind: Servicefence +metadata: + name: servicefence-sample +spec: + # Add fields here + foo: bar diff --git a/staging/src/slime.io/slime/modules/lazyload/config/webhook/kustomization.yaml b/staging/src/slime.io/slime/modules/lazyload/config/webhook/kustomization.yaml new file mode 100644 index 00000000..9cf26134 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/config/webhook/kustomization.yaml @@ -0,0 +1,6 @@ +resources: +- manifests.yaml +- service.yaml + +configurations: +- kustomizeconfig.yaml diff --git a/staging/src/slime.io/slime/modules/lazyload/config/webhook/kustomizeconfig.yaml b/staging/src/slime.io/slime/modules/lazyload/config/webhook/kustomizeconfig.yaml new file mode 100644 index 00000000..25e21e3c --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/config/webhook/kustomizeconfig.yaml @@ -0,0 +1,25 @@ +# the following config is for teaching kustomize where to look at when substituting vars. +# It requires kustomize v2.1.0 or newer to work properly. +nameReference: +- kind: Service + version: v1 + fieldSpecs: + - kind: MutatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/name + - kind: ValidatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/name + +namespace: +- kind: MutatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/namespace + create: true +- kind: ValidatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/namespace + create: true + +varReference: +- path: metadata/annotations diff --git a/staging/src/slime.io/slime/modules/lazyload/config/webhook/service.yaml b/staging/src/slime.io/slime/modules/lazyload/config/webhook/service.yaml new file mode 100644 index 00000000..31e0f829 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/config/webhook/service.yaml @@ -0,0 +1,12 @@ + +apiVersion: v1 +kind: Service +metadata: + name: webhook-service + namespace: system +spec: + ports: + - port: 443 + targetPort: 9443 + selector: + control-plane: controller-manager diff --git a/staging/src/slime.io/slime/modules/lazyload/controllers/domain.go b/staging/src/slime.io/slime/modules/lazyload/controllers/domain.go new file mode 100644 index 00000000..55663af5 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/controllers/domain.go @@ -0,0 +1,82 @@ +package controllers + +import ( + "regexp" + + lazyloadv1alpha1 "slime.io/slime/modules/lazyload/api/v1alpha1" +) + +func newDomainAliasRules(domainAlias []*lazyloadv1alpha1.DomainAlias) []*domainAliasRule { + var rules []*domainAliasRule + if domainAlias == nil { + return nil + } + + for _, da := range domainAlias { + log.Infof("lazyload domainAlias: get pattern %s, get templates %v", da.Pattern, da.Templates) + + pattern := da.Pattern + re, err := regexp.Compile(pattern) + if err != nil { + log.Errorf("compile domainAlias pattern %s err: %+v", pattern, err) + return nil + } + templates := da.Templates + if len(templates) == 0 { + log.Errorf("domainAlias template is empty") + return nil + } + rule := &domainAliasRule{ + pattern: pattern, + templates: templates, + re: re, + } + rules = append(rules, rule) + } + return rules +} + +func domainAddAlias(src string, rules []*domainAliasRule) []string { + dest := []string{src} + if rules == nil { + return dest + } + + for _, rule := range rules { + allIndexes := rule.re.FindAllSubmatchIndex([]byte(src), -1) + if idxNum := len(allIndexes); idxNum != 1 { + if idxNum > 1 { + log.Warnf("domain %s matches more than once on pattern %s", src, rule.pattern) + } + continue + } + for _, template := range rule.templates { + var domain []byte + // expand the template according allIndexes + domain = rule.re.ExpandString(domain, template, src, allIndexes[0]) + if len(domain) == 0 { + continue + } + dest = append(dest, string(domain)) + } + } + + log.Debugf("domainAddAlias src: %s, dest: %v", src, dest) + return dest +} + +func addToDomains(domains map[string]*lazyloadv1alpha1.Destinations, fh string) { + if domains[fh] != nil { + return + } + + allHost := []string{fh} + if hs := getDestination(fh); len(hs) > 0 { + allHost = append(allHost, hs...) + } + + domains[fh] = &lazyloadv1alpha1.Destinations{ + Hosts: allHost, + Status: lazyloadv1alpha1.Destinations_ACTIVE, + } +} diff --git a/staging/src/slime.io/slime/modules/lazyload/controllers/model.go b/staging/src/slime.io/slime/modules/lazyload/controllers/model.go new file mode 100644 index 00000000..8c6eb66d --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/controllers/model.go @@ -0,0 +1,42 @@ +/* +* @Author: yangdihang +* @Date: 2020/6/2 + */ + +package controllers + +import ( + "regexp" + "sync" + + "slime.io/slime/framework/model" + modmodel "slime.io/slime/modules/lazyload/model" +) + +var log = modmodel.ModuleLog.WithField(model.LogFieldKeyPkg, "controllers") + +type Diff struct { + Deleted []string + Added []string +} + +type LabelItem struct { + Name string + Value string +} + +type NsSvcCache struct { + Data map[string]map[string]struct{} + sync.RWMutex +} + +type LabelSvcCache struct { + Data map[LabelItem]map[string]struct{} + sync.RWMutex +} + +type domainAliasRule struct { + pattern string + templates []string + re *regexp.Regexp +} diff --git a/staging/src/slime.io/slime/modules/lazyload/controllers/process.go b/staging/src/slime.io/slime/modules/lazyload/controllers/process.go new file mode 100644 index 00000000..41be0ddd --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/controllers/process.go @@ -0,0 +1,308 @@ +package controllers + +import ( + "context" + "strings" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "slime.io/slime/framework/model" + "slime.io/slime/framework/model/metric" + lazyloadv1alpha1 "slime.io/slime/modules/lazyload/api/v1alpha1" +) + +const ( + LabelServiceFenced = "slime.io/serviceFenced" + ServiceFencedTrue = "true" + ServiceFencedFalse = "false" + + LabelCreatedBy = "app.kubernetes.io/created-by" + CreatedByFenceController = "fence-controller" +) + +func (r *ServicefenceReconciler) WatchMetric() { + log := log.WithField("reporter", "ServicefenceReconciler").WithField("function", "WatchMetric") + log.Infof("start watching metric") + + for { + select { + case metric, ok := <-r.watcherMetricChan: + if !ok { + log.Warningf("watcher mertic channel closed, break process loop") + return + } + r.ConsumeMetric(metric) + case metric, ok := <-r.tickerMetricChan: + if !ok { + log.Warningf("ticker metric channel closed, break process loop") + return + } + r.ConsumeMetric(metric) + } + } +} + +func (r *ServicefenceReconciler) ConsumeMetric(metric metric.Metric) { + for meta, results := range metric { + log.Debugf("got metric for %s", meta) + namespace, name := strings.Split(meta, "/")[0], strings.Split(meta, "/")[1] + nn := types.NamespacedName{Namespace: namespace, Name: name} + if len(results) != 1 { + log.Errorf("wrong metric results length for %s", meta) + continue + } + value := results[0].Value + if _, err := r.Refresh(reconcile.Request{NamespacedName: nn}, value); err != nil { + log.Errorf("refresh error:%v", err) + } + } +} + +func (r *ServicefenceReconciler) Refresh(req reconcile.Request, value map[string]string) (reconcile.Result, error) { + log := log.WithField("reporter", "ServicefenceReconciler").WithField("function", "Refresh") + + r.reconcileLock.Lock() + defer r.reconcileLock.Unlock() + + sf := &lazyloadv1alpha1.ServiceFence{} + err := r.Client.Get(context.TODO(), req.NamespacedName, sf) + if err != nil { + if errors.IsNotFound(err) { + sf = nil + err = nil + } else { + log.Errorf("can not get ServiceFence %s, %+v", req.NamespacedName.Name, err) + return reconcile.Result{}, err + } + } + + if sf == nil { + log.Info("ServiceFence Not Found, skip") + return reconcile.Result{}, nil + } else if rev := model.IstioRevFromLabel(sf.Labels); !r.env.RevInScope(rev) { + log.Infof("existing sf %v istioRev %s but our %s, skip ...", + req.NamespacedName, rev, r.env.IstioRev()) + return reconcile.Result{}, nil + } + + // use updateVisitedHostStatus to update svf.spec and svf.status + sf.Status.MetricStatus = value + diff := r.updateVisitedHostStatus(sf) + r.recordVisitor(sf, diff) + + if sf.Spec.Enable { + if err := r.refreshSidecar(sf); err != nil { + // XXX return err? + log.Errorf("refresh sidecar %v met err: %v", req.NamespacedName, err) + } + } + + return reconcile.Result{}, nil +} + +func (r *ServicefenceReconciler) isNsFenced(ns *corev1.Namespace) bool { + if ns != nil && ns.Labels != nil { + switch ns.Labels[LabelServiceFenced] { + case ServiceFencedTrue: + return true + case ServiceFencedFalse: + return false + } + } + return r.cfg.DefaultFence +} + +func (r *ServicefenceReconciler) isServiceFenced(ctx context.Context, svc *corev1.Service) bool { + var svcLabel string + if svc.Labels != nil { + svcLabel = svc.Labels[LabelServiceFenced] + } + + switch svcLabel { + case ServiceFencedFalse: + return false + case ServiceFencedTrue: + return true + default: + if r.staleNamespaces[svc.Namespace] { + ns := &corev1.Namespace{} + if err := r.Client.Get(ctx, types.NamespacedName{ + Namespace: "", + Name: svc.Namespace, + }, ns); err != nil { + if errors.IsNotFound(err) { + ns = nil + } else { + ns = nil + log.Errorf("fail to get ns: %s", svc.Namespace) + } + } + + if ns != nil { + return r.isNsFenced(ns) + } + } + return r.enabledNamespaces[svc.Namespace] + } +} + +func (r *ServicefenceReconciler) ReconcileService(req ctrl.Request) (ctrl.Result, error) { + ctx := context.TODO() + + r.reconcileLock.Lock() + defer r.reconcileLock.Unlock() + + return r.refreshFenceStatusOfService(ctx, nil, req.NamespacedName) +} + +func (r *ServicefenceReconciler) ReconcileNamespace(req ctrl.Request) (ret ctrl.Result, err error) { + ctx := context.TODO() + + // Fetch the namespace instance + ns := &corev1.Namespace{} + err = r.Client.Get(ctx, req.NamespacedName, ns) + + if ns.Name == r.env.Config.Global.IstioNamespace || ns.Name == r.env.Config.Global.SlimeNamespace { + log.Debugf("auto fence does not apply to istio and slime namespace, skip") + return reconcile.Result{}, nil + } + + r.reconcileLock.Lock() + defer r.reconcileLock.Unlock() + + defer func() { + if err == nil { + delete(r.staleNamespaces, req.Name) + } + }() + + if err != nil { + if errors.IsNotFound(err) { + ns = nil + delete(r.enabledNamespaces, req.Name) + return reconcile.Result{}, nil // do not process deletion ... + } else { + log.Errorf("get namespace %s error, %+v", req.NamespacedName, err) + return reconcile.Result{}, err + } + } + + nsFenced := r.isNsFenced(ns) + + if nsFenced == r.enabledNamespaces[req.Name] { + return reconcile.Result{}, nil + } else { + prev := r.enabledNamespaces[req.Name] + r.enabledNamespaces[req.Name] = nsFenced + defer func() { + if err != nil { + r.enabledNamespaces[req.Name] = prev // restore, leave to re-process next time + r.staleNamespaces[req.Name] = true + } + }() + } + + // refresh service fenced status + services := &corev1.ServiceList{} + if err = r.Client.List(ctx, services, client.InNamespace(req.Name)); err != nil { + log.Errorf("list services %s failed, %+v", req.Name, err) + return reconcile.Result{}, err + } + + for _, svc := range services.Items { + if ret, err = r.refreshFenceStatusOfService(ctx, &svc, types.NamespacedName{}); err != nil { + log.Errorf("refreshFenceStatusOfService services %s failed, %+v", svc.Name, err) + return ret, err + } + } + + return ctrl.Result{}, nil +} + +// refreshFenceStatusOfService caller should hold the reconcile lock. +func (r *ServicefenceReconciler) refreshFenceStatusOfService(ctx context.Context, svc *corev1.Service, nsName types.NamespacedName) (reconcile.Result, error) { + if svc == nil { + // Fetch the Service instance + svc = &corev1.Service{} + err := r.Client.Get(ctx, nsName, svc) + if err != nil { + if errors.IsNotFound(err) { + svc = nil + } else { + log.Errorf("get service %s error, %+v", nsName, err) + return reconcile.Result{}, err + } + } + } else { + nsName = types.NamespacedName{ + Namespace: svc.Namespace, + Name: svc.Name, + } + } + + // Fetch the ServiceFence instance + sf := &lazyloadv1alpha1.ServiceFence{} + err := r.Client.Get(ctx, nsName, sf) + if err != nil { + if errors.IsNotFound(err) { + sf = nil + } else { + log.Errorf("get serviceFence %s error, %+v", nsName, err) + return reconcile.Result{}, err + } + } + + if sf == nil { + if svc != nil && r.isServiceFenced(ctx, svc) { + // add svc -> add sf + sf = &lazyloadv1alpha1.ServiceFence{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: svc.Name, + Namespace: svc.Namespace, + }, + Spec: lazyloadv1alpha1.ServiceFenceSpec{ + Enable: true, + WorkloadSelector: &lazyloadv1alpha1.WorkloadSelector{ + FromService: true, + }, + }, + } + markFenceCreatedByController(sf) + model.PatchIstioRevLabel(&sf.Labels, r.env.IstioRev()) + if err = r.Client.Create(ctx, sf); err != nil { + log.Errorf("create fence %s failed, %+v", nsName, err) + return reconcile.Result{}, err + } + } + } else if rev := model.IstioRevFromLabel(sf.Labels); !r.env.RevInScope(rev) { + // check if svc needs auto fence created + log.Errorf("existed fence %v istioRev %s but our rev %s, skip ...", + nsName, rev, r.env.IstioRev()) + } else if isFenceCreatedByController(sf) && (svc == nil || !r.isServiceFenced(ctx, svc)) { + if err := r.Client.Delete(ctx, sf); err != nil { + log.Errorf("delete fence %s failed, %+v", nsName, err) + } + } + + return ctrl.Result{}, nil +} + +func isFenceCreatedByController(sf *lazyloadv1alpha1.ServiceFence) bool { + if sf.Labels == nil { + return false + } + return sf.Labels[LabelCreatedBy] == CreatedByFenceController +} + +func markFenceCreatedByController(sf *lazyloadv1alpha1.ServiceFence) { + if sf.Labels == nil { + sf.Labels = map[string]string{} + } + sf.Labels = map[string]string{LabelCreatedBy: CreatedByFenceController} +} diff --git a/staging/src/slime.io/slime/modules/lazyload/controllers/producer.go b/staging/src/slime.io/slime/modules/lazyload/controllers/producer.go new file mode 100644 index 00000000..2b8a1dd9 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/controllers/producer.go @@ -0,0 +1,485 @@ +package controllers + +import ( + "context" + stderrors "errors" + "fmt" + "strconv" + "strings" + "sync" + "time" + + envoy_config_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + data_accesslog "github.com/envoyproxy/go-control-plane/envoy/data/accesslog/v3" + prometheusApi "github.com/prometheus/client_golang/api" + prometheusV1 "github.com/prometheus/client_golang/api/prometheus/v1" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/cache" + "slime.io/slime/framework/apis/config/v1alpha1" + "slime.io/slime/framework/bootstrap" + "slime.io/slime/framework/model/metric" + "slime.io/slime/framework/model/trigger" + "slime.io/slime/framework/util" + lazyloadapiv1alpha1 "slime.io/slime/modules/lazyload/api/v1alpha1" +) + +const ( + AccessLogConvertorName = "lazyload-accesslog-convertor" + MetricSourceTypePrometheus = "prometheus" + MetricSourceTypeAccesslog = "accesslog" + SvfResource = "servicefences" +) + +// call back function for watcher producer +func (r *ServicefenceReconciler) handleWatcherEvent(event trigger.WatcherEvent) metric.QueryMap { + // check event + gvks := []schema.GroupVersionKind{ + {Group: "networking.istio.io", Version: "v1beta1", Kind: "Sidecar"}, + } + invalidEvent := false + for _, gvk := range gvks { + if event.GVK == gvk && r.getInterestMeta()[event.NN.String()] { + invalidEvent = true + } + } + if !invalidEvent { + return nil + } + + // generate query map for producer + qm := make(map[string][]metric.Handler) + var hs []metric.Handler + + // check metric source type + switch r.env.Config.Global.Misc["metricSourceType"] { + case MetricSourceTypePrometheus: + for pName, pHandler := range r.env.Config.Metric.Prometheus.Handlers { + hs = append(hs, generateHandler(event.NN.Name, event.NN.Namespace, pName, pHandler)) + } + case MetricSourceTypeAccesslog: + hs = []metric.Handler{ + { + Name: AccessLogConvertorName, + Query: "", + }, + } + } + + qm[event.NN.String()] = hs + return qm +} + +// call back function for ticker producer +func (r *ServicefenceReconciler) handleTickerEvent(event trigger.TickerEvent) metric.QueryMap { + // no need to check time duration + + // generate query map for producer + // check metric source type + qm := make(map[string][]metric.Handler) + + switch r.env.Config.Global.Misc["metricSourceType"] { + case MetricSourceTypePrometheus: + for meta := range r.getInterestMeta() { + namespace, name := strings.Split(meta, "/")[0], strings.Split(meta, "/")[1] + var hs []metric.Handler + for pName, pHandler := range r.env.Config.Metric.Prometheus.Handlers { + hs = append(hs, generateHandler(name, namespace, pName, pHandler)) + } + qm[meta] = hs + } + case MetricSourceTypeAccesslog: + for meta := range r.getInterestMeta() { + qm[meta] = []metric.Handler{ + { + Name: AccessLogConvertorName, + Query: "", + }, + } + } + } + + return qm +} + +func generateHandler(name, namespace, pName string, pHandler *v1alpha1.Prometheus_Source_Handler) metric.Handler { + query := strings.ReplaceAll(pHandler.Query, "$namespace", namespace) + query = strings.ReplaceAll(query, "$source_app", name) + return metric.Handler{Name: pName, Query: query} +} + +func newProducerConfig(env bootstrap.Environment) (*metric.ProducerConfig, error) { + // init metric source + var enablePrometheusSource bool + var prometheusSourceConfig metric.PrometheusSourceConfig + var accessLogSourceConfig metric.AccessLogSourceConfig + var err error + + switch env.Config.Global.Misc["metricSourceType"] { + case MetricSourceTypePrometheus: + enablePrometheusSource = true + prometheusSourceConfig, err = newPrometheusSourceConfig(env) + if err != nil { + return nil, err + } + case MetricSourceTypeAccesslog: + enablePrometheusSource = false + // init log source port + port := env.Config.Global.Misc["logSourcePort"] + + // init initCache + initCache, err := newInitCache(env) + if err != nil { + return nil, err + } + log.Debugf("initCache is %+v", initCache) + + // make preparation for handler + ipToSvcCache, svcToIpsCache, cacheLock, err := newIpToSvcCache(env.K8SClient) + if err != nil { + return nil, err + } + + // init accessLog source config + accessLogSourceConfig = metric.AccessLogSourceConfig{ + ServePort: port, + AccessLogConvertorConfigs: []metric.AccessLogConvertorConfig{ + { + Name: AccessLogConvertorName, + Handler: func(logEntry []*data_accesslog.HTTPAccessLogEntry) (map[string]map[string]string, error) { + return accessLogHandler(logEntry, ipToSvcCache, svcToIpsCache, cacheLock) + }, + InitCache: initCache, + }, + }, + } + default: + return nil, stderrors.New("wrong metricSourceType") + } + + // init whole producer config + pc := &metric.ProducerConfig{ + EnablePrometheusSource: enablePrometheusSource, + PrometheusSourceConfig: prometheusSourceConfig, + AccessLogSourceConfig: accessLogSourceConfig, + EnableWatcherProducer: true, + WatcherProducerConfig: metric.WatcherProducerConfig{ + Name: "lazyload-watcher", + MetricChan: make(chan metric.Metric), + WatcherTriggerConfig: trigger.WatcherTriggerConfig{ + Kinds: []schema.GroupVersionKind{ + { + Group: "networking.istio.io", + Version: "v1beta1", + Kind: "Sidecar", + }, + }, + EventChan: make(chan trigger.WatcherEvent), + DynamicClient: env.DynamicClient, + }, + }, + EnableTickerProducer: true, + TickerProducerConfig: metric.TickerProducerConfig{ + Name: "lazyload-ticker", + MetricChan: make(chan metric.Metric), + TickerTriggerConfig: trigger.TickerTriggerConfig{ + Durations: []time.Duration{ + 30 * time.Second, + }, + EventChan: make(chan trigger.TickerEvent), + }, + }, + StopChan: env.Stop, + } + + return pc, nil +} + +func newPrometheusSourceConfig(env bootstrap.Environment) (metric.PrometheusSourceConfig, error) { + ps := env.Config.Metric.Prometheus + if ps == nil { + return metric.PrometheusSourceConfig{}, stderrors.New("failure create prometheus client, empty prometheus config") + } + promClient, err := prometheusApi.NewClient(prometheusApi.Config{ + Address: ps.Address, + RoundTripper: nil, + }) + if err != nil { + return metric.PrometheusSourceConfig{}, err + } + + return metric.PrometheusSourceConfig{ + Api: prometheusV1.NewAPI(promClient), + }, nil +} + +func newInitCache(env bootstrap.Environment) (map[string]map[string]string, error) { + result := make(map[string]map[string]string) + + svfGvr := schema.GroupVersionResource{ + Group: lazyloadapiv1alpha1.GroupVersion.Group, + Version: lazyloadapiv1alpha1.GroupVersion.Version, + Resource: SvfResource, + } + + dc := env.DynamicClient + svfList, err := dc.Resource(svfGvr).List(metav1.ListOptions{}) + if err != nil { + return nil, fmt.Errorf("list servicefence error: %v", err) + } + for _, svf := range svfList.Items { + meta := svf.GetNamespace() + "/" + svf.GetName() + value := make(map[string]string) + ms, existed, err := unstructured.NestedMap(svf.Object, "status", "metricStatus") + if err != nil { + log.Errorf("got servicefence %s status.metricStatus error: %v", meta, err) + continue + } + if existed { + for k, v := range ms { + ok := false + if value[k], ok = v.(string); !ok { + log.Errorf("servicefence %s metricStatus value is not string, value: %+v", meta, v) + continue + } + } + } + result[meta] = value + } + + return result, nil +} + +func accessLogHandler(logEntry []*data_accesslog.HTTPAccessLogEntry, ipToSvcCache map[string]string, + svcToIpsCache map[string][]string, cacheLock *sync.RWMutex, +) (map[string]map[string]string, error) { + log := log.WithField("reporter", "accesslog convertor").WithField("function", "accessLogHandler") + result := make(map[string]map[string]string) + + tmpResult := make(map[string]map[string]int) + for _, entry := range logEntry { + // tmpValue := make(map[string]int) + + // fetch sourceEp + sourceIp, err := fetchSourceIp(entry) + if err != nil { + return nil, err + } + if sourceIp == "" { + continue + } + + // fetch sourceSvcMeta + sourceSvc, err := spliceSourceSvc(sourceIp, ipToSvcCache, cacheLock) + if err != nil { + return nil, err + } + if sourceSvc == "" { + continue + } + + // fetch destinationSvcMeta + destinationSvc := spliceDestinationSvc(entry, sourceSvc, svcToIpsCache, cacheLock) + if destinationSvc == "" { + continue + } + + // push result + if dstSvcMappings, ok := tmpResult[sourceSvc]; !ok { + tmpValue := make(map[string]int) + tmpValue[destinationSvc] = 1 + tmpResult[sourceSvc] = tmpValue + } else { + dstSvcMappings[destinationSvc] += 1 + } + + log.Debugf("tmpResult[%s][%s]: %d", sourceSvc, destinationSvc, tmpResult[sourceSvc][destinationSvc]) + } + + for sourceSvc, dstSvcMappings := range tmpResult { + result[sourceSvc] = make(map[string]string) + for dstSvc, count := range dstSvcMappings { + result[sourceSvc][dstSvc] = strconv.Itoa(count) + } + } + + return result, nil +} + +func fetchSourceIp(entry *data_accesslog.HTTPAccessLogEntry) (string, error) { + log := log.WithField("reporter", "accesslog convertor").WithField("function", "fetchSourceIp") + if entry.CommonProperties.DownstreamRemoteAddress == nil { + log.Debugf("DownstreamRemoteAddress is nil, skip") + return "", nil + } + downstreamSock, ok := entry.CommonProperties.DownstreamRemoteAddress.Address.(*envoy_config_core.Address_SocketAddress) + if !ok { + return "", stderrors.New("wrong type of DownstreamRemoteAddress") + } + if downstreamSock == nil || downstreamSock.SocketAddress == nil { + return "", stderrors.New("downstream socket address is nil") + } + log.Debugf("SourceEp is: %s", downstreamSock.SocketAddress.Address) + return downstreamSock.SocketAddress.Address, nil +} + +func spliceSourceSvc(sourceIp string, ipToSvcCache map[string]string, cacheLock *sync.RWMutex) (string, error) { + cacheLock.RLock() + defer cacheLock.RUnlock() + + for ip, svc := range ipToSvcCache { + if sourceIp == ip { + return svc, nil + } + } + + return "", nil +} + +func spliceDestinationSvc(entry *data_accesslog.HTTPAccessLogEntry, sourceSvc string, svcToIpsCache map[string][]string, cacheLock *sync.RWMutex) string { + log := log.WithField("reporter", "accesslog convertor").WithField("function", "spliceDestinationSvc") + var destSvc string + upstreamCluster := entry.CommonProperties.UpstreamCluster + parts := strings.Split(upstreamCluster, "|") + if len(parts) != 4 { + log.Errorf("UpstreamCluster is wrong: parts number is not 4, skip") + return "" + } + // only handle inbound access log + if parts[0] != "inbound" { + log.Debugf("This log is not inbound, skip") + return "" + } + // get destination service info from request.authority + auth := entry.Request.Authority + dest := strings.Split(auth, ":")[0] + destParts := strings.Split(dest, ".") + switch len(destParts) { + case 1: + srcParts := strings.Split(sourceSvc, "/") + destSvc = dest + "." + srcParts[0] + ".svc.cluster.local" + case 2: + destSvc = completeDestSvcName(destParts, dest, "svc.cluster.local", svcToIpsCache, cacheLock) + case 3: + if destParts[2] == "svc" { + destSvc = completeDestSvcName(destParts, dest, "cluster.local", svcToIpsCache, cacheLock) + } else { + destSvc = dest + } + default: + destSvc = dest + } + + log.Debugf("DestinationSvc is: %s", "{destination_service=\""+destSvc+"\"}") + return "{destination_service=\"" + destSvc + "\"}" +} + +func completeDestSvcName(destParts []string, dest, suffix string, svcToIpsCache map[string][]string, cacheLock *sync.RWMutex) (destSvc string) { + cacheLock.RLock() + defer cacheLock.RUnlock() + + svc := destParts[1] + "/" + destParts[0] + if _, ok := svcToIpsCache[svc]; ok { + // dest is abbreviation of service, add suffix + destSvc = dest + suffix + } else { + // not abbreviation of service, no suffix + destSvc = dest + } + return +} + +func newIpToSvcCache(clientSet *kubernetes.Clientset) (map[string]string, map[string][]string, *sync.RWMutex, error) { + log := log.WithField("reporter", "AccessLogConvertor").WithField("function", "generateSvcToIpsCache") + ipToSvcCache := make(map[string]string) + svcToIpsCache := make(map[string][]string) + var cacheLock sync.RWMutex + + // init svcToIps + eps, err := clientSet.CoreV1().Endpoints("").List(metav1.ListOptions{}) + if err != nil { + return nil, nil, nil, stderrors.New("failed to get endpoints list") + } + + for _, ep := range eps.Items { + svc := ep.GetNamespace() + "/" + ep.GetName() + var addresses []string + for _, subset := range ep.Subsets { + for _, address := range subset.Addresses { + addresses = append(addresses, address.IP) + ipToSvcCache[address.IP] = svc + } + } + svcToIpsCache[svc] = addresses + } + + // init endpoint watcher + epsClient := clientSet.CoreV1().Endpoints("") + lw := &cache.ListWatch{ + ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { + return epsClient.List(options) + }, + WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { + return epsClient.Watch(options) + }, + } + watcher := util.ListWatcher(context.Background(), lw) + + go func() { + log.Infof("Endpoint cacher is running") + for { + e, ok := <-watcher.ResultChan() + if !ok { + log.Warningf("a result chan of endpoint watcher is closed, break process loop") + return + } + + ep, ok := e.Object.(*v1.Endpoints) + if !ok { + log.Errorf("invalid type of object in endpoint watcher event") + continue + } + + svc := ep.GetNamespace() + "/" + ep.GetName() + // delete event + if e.Type == watch.Deleted { + cacheLock.Lock() + for _, ip := range svcToIpsCache[svc] { + delete(ipToSvcCache, ip) + } + delete(svcToIpsCache, svc) + cacheLock.Unlock() + continue + } + + // add, update event + ep, err := clientSet.CoreV1().Endpoints(ep.GetNamespace()).Get(ep.GetName(), metav1.GetOptions{}) + if err != nil { + continue + } + // delete previous key, value + cacheLock.Lock() + for _, ip := range svcToIpsCache[svc] { + delete(ipToSvcCache, ip) + } + // add new key, value + var addresses []string + for _, subset := range ep.Subsets { + for _, address := range subset.Addresses { + addresses = append(addresses, address.IP) + ipToSvcCache[address.IP] = svc + } + } + svcToIpsCache[svc] = addresses + cacheLock.Unlock() + + } + }() + + return ipToSvcCache, svcToIpsCache, &cacheLock, nil +} diff --git a/staging/src/slime.io/slime/modules/lazyload/controllers/service_cache.go b/staging/src/slime.io/slime/modules/lazyload/controllers/service_cache.go new file mode 100644 index 00000000..79d2fd50 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/controllers/service_cache.go @@ -0,0 +1,122 @@ +package controllers + +import ( + "context" + stderrors "errors" + + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/cache" + "slime.io/slime/framework/util" +) + +func newSvcCache(clientSet *kubernetes.Clientset) (*NsSvcCache, *LabelSvcCache, error) { + log := log.WithField("function", "newLabelSvcCache") + nsSvcCache := &NsSvcCache{Data: map[string]map[string]struct{}{}} + labelSvcCache := &LabelSvcCache{Data: map[LabelItem]map[string]struct{}{}} + + // init labelSvcCache + services, err := clientSet.CoreV1().Services("").List(metav1.ListOptions{}) + if err != nil { + return nil, nil, stderrors.New("failed to get service list") + } + + for _, service := range services.Items { + ns := service.GetNamespace() + name := service.GetName() + svc := ns + "/" + name + if nsSvcCache.Data[ns] == nil { + nsSvcCache.Data[ns] = make(map[string]struct{}) + } + nsSvcCache.Data[ns][svc] = struct{}{} + for k, v := range service.GetLabels() { + label := LabelItem{ + Name: k, + Value: v, + } + if labelSvcCache.Data[label] == nil { + labelSvcCache.Data[label] = make(map[string]struct{}) + } + labelSvcCache.Data[label][svc] = struct{}{} + } + } + + // init service watcher + servicesClient := clientSet.CoreV1().Services("") + lw := &cache.ListWatch{ + ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { + return servicesClient.List(options) + }, + WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { + return servicesClient.Watch(options) + }, + } + watcher := util.ListWatcher(context.Background(), lw) + + go func() { + log.Infof("Service cacher is running") + for { + e, ok := <-watcher.ResultChan() + if !ok { + log.Warningf("a result chan of service watcher is closed, break process loop") + return + } + + service, ok := e.Object.(*v1.Service) + if !ok { + log.Errorf("invalid type of object in service watcher event") + continue + } + ns := service.GetNamespace() + name := service.GetName() + eventSvc := ns + "/" + name + // delete eventSvc from labelSvcCache to ensure final consistency + labelSvcCache.Lock() + for label, m := range labelSvcCache.Data { + delete(m, eventSvc) + if len(m) == 0 { + delete(labelSvcCache.Data, label) + } + } + labelSvcCache.Unlock() + + // delete event + // delete eventSvc from ns->svc map + if e.Type == watch.Deleted { + nsSvcCache.Lock() + delete(nsSvcCache.Data[ns], eventSvc) + nsSvcCache.Unlock() + // labelSvcCache already deleted, skip + continue + } + + // add, update event + // add eventSvc to nsSvcCache + nsSvcCache.Lock() + if nsSvcCache.Data[ns] == nil { + nsSvcCache.Data[ns] = make(map[string]struct{}) + } + nsSvcCache.Data[ns][eventSvc] = struct{}{} + nsSvcCache.Unlock() + // add eventSvc to labelSvcCache again + labelSvcCache.Lock() + for k, v := range service.GetLabels() { + label := LabelItem{ + Name: k, + Value: v, + } + if labelSvcCache.Data[label] == nil { + labelSvcCache.Data[label] = make(map[string]struct{}) + } + labelSvcCache.Data[label][eventSvc] = struct{}{} + } + labelSvcCache.Unlock() + + } + }() + + return nsSvcCache, labelSvcCache, nil +} diff --git a/staging/src/slime.io/slime/modules/lazyload/controllers/servicefence_controller.go b/staging/src/slime.io/slime/modules/lazyload/controllers/servicefence_controller.go new file mode 100644 index 00000000..e5206543 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/controllers/servicefence_controller.go @@ -0,0 +1,771 @@ +/* + + +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 controllers + +import ( + "context" + "fmt" + "reflect" + "sort" + "strings" + "sync" + "time" + + "slime.io/slime/framework/model" + "slime.io/slime/framework/model/metric" + + istio "istio.io/api/networking/v1alpha3" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "slime.io/slime/framework/apis/networking/v1alpha3" + "slime.io/slime/framework/bootstrap" + "slime.io/slime/framework/controllers" + "slime.io/slime/framework/util" + + lazyloadv1alpha1 "slime.io/slime/modules/lazyload/api/v1alpha1" + modmodel "slime.io/slime/modules/lazyload/model" +) + +// ServicefenceReconciler reconciles a Servicefence object +type ServicefenceReconciler struct { + client.Client + Scheme *runtime.Scheme + cfg *lazyloadv1alpha1.Fence + env bootstrap.Environment + interestMeta map[string]bool + interestMetaCopy map[string]bool // for outside read + watcherMetricChan <-chan metric.Metric + tickerMetricChan <-chan metric.Metric + reconcileLock sync.RWMutex + staleNamespaces map[string]bool + enabledNamespaces map[string]bool + nsSvcCache *NsSvcCache + labelSvcCache *LabelSvcCache + defaultAddNamespaces []string + doAliasRules []*domainAliasRule +} + +// NewReconciler returns a new reconcile.Reconciler +func NewReconciler(cfg *lazyloadv1alpha1.Fence, mgr manager.Manager, env bootstrap.Environment) *ServicefenceReconciler { + log := modmodel.ModuleLog.WithField(model.LogFieldKeyFunction, "NewReconciler") + + // generate producer config + pc, err := newProducerConfig(env) + if err != nil { + log.Errorf("%v", err) + return nil + } + + r := &ServicefenceReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + env: env, + interestMeta: map[string]bool{}, + interestMetaCopy: map[string]bool{}, + watcherMetricChan: pc.WatcherProducerConfig.MetricChan, + tickerMetricChan: pc.TickerProducerConfig.MetricChan, + staleNamespaces: map[string]bool{}, + enabledNamespaces: map[string]bool{}, + defaultAddNamespaces: []string{env.Config.Global.IstioNamespace, env.Config.Global.SlimeNamespace}, + doAliasRules: newDomainAliasRules(cfg.DomainAliases), + cfg: cfg, + } + + // start service related cache + r.nsSvcCache, r.labelSvcCache, err = newSvcCache(env.K8SClient) + if err != nil { + log.Errorf("init LabelSvcCache err: %v", err) + return nil + } + + // reconciler defines producer metric handler + pc.WatcherProducerConfig.NeedUpdateMetricHandler = r.handleWatcherEvent + pc.TickerProducerConfig.NeedUpdateMetricHandler = r.handleTickerEvent + + // start producer + metric.NewProducer(pc) + log.Infof("producers starts") + + if env.Config.Metric != nil || env.Config.Global.Misc["metricSourceType"] == MetricSourceTypeAccesslog { + go r.WatchMetric() + } else { + log.Warningf("watching metric is not running") + } + + return r +} + +// +kubebuilder:rbac:groups=microservice.slime.io,resources=servicefences,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=microservice.slime.io,resources=servicefences/status,verbs=get;update;patch + +func (r *ServicefenceReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { + _ = context.Background() + log := modmodel.ModuleLog.WithField(model.LogFieldKeyResource, req.NamespacedName) + + // Fetch the ServiceFence instance + instance := &lazyloadv1alpha1.ServiceFence{} + err := r.Client.Get(context.TODO(), req.NamespacedName, instance) + + r.reconcileLock.Lock() + defer r.reconcileLock.Unlock() + + // TODO watch sidecar + if err != nil { + if errors.IsNotFound(err) { + // TODO should be recovered? maybe we should call refreshFenceStatusOfService here + log.Info("serviceFence is deleted") + // r.interestMeta.Pop(req.NamespacedName.String()) + delete(r.interestMeta, req.NamespacedName.String()) + r.updateInterestMetaCopy() + return r.refreshFenceStatusOfService(context.TODO(), nil, req.NamespacedName) + } else { + log.Errorf("get serviceFence error,%+v", err) + return reconcile.Result{}, err + } + } + + if rev := model.IstioRevFromLabel(instance.Labels); !r.env.RevInScope(rev) { // remove watch ? + log.Infof("exsiting sf %v istioRev %s but our %s, skip...", + req.NamespacedName, rev, r.env.IstioRev()) + return reconcile.Result{}, nil + } + log.Infof("ServicefenceReconciler got serviceFence request, %+v", req.NamespacedName) + + // 资源更新 + diff := r.updateVisitedHostStatus(instance) + r.recordVisitor(instance, diff) + if instance.Spec.Enable { + err = r.refreshSidecar(instance) + r.interestMeta[req.NamespacedName.String()] = true + r.updateInterestMetaCopy() + } + + return ctrl.Result{}, err +} + +func (r *ServicefenceReconciler) updateInterestMetaCopy() { + newInterestMeta := make(map[string]bool) + for k, v := range r.interestMeta { + newInterestMeta[k] = v + } + r.interestMetaCopy = newInterestMeta +} + +func (r *ServicefenceReconciler) getInterestMeta() map[string]bool { + r.reconcileLock.RLock() + defer r.reconcileLock.RUnlock() + return r.interestMetaCopy +} + +func (r *ServicefenceReconciler) refreshSidecar(instance *lazyloadv1alpha1.ServiceFence) error { + log := log.WithField("reporter", "ServicefenceReconciler").WithField("function", "refreshSidecar") + sidecar, err := r.newSidecar(instance, r.env) + if err != nil { + log.Errorf("servicefence generate sidecar failed, %+v", err) + return err + } + if sidecar == nil { + return nil + } + // Set VisitedHost instance as the owner and controller + if err := controllerutil.SetControllerReference(instance, sidecar, r.Scheme); err != nil { + log.Errorf("attach ownerReference to sidecar failed, %+v", err) + return err + } + sfRev := model.IstioRevFromLabel(instance.Labels) + model.PatchIstioRevLabel(&sidecar.Labels, sfRev) + + // Check if this Pod already exists + found := &v1alpha3.Sidecar{} + nsName := types.NamespacedName{Name: sidecar.Name, Namespace: sidecar.Namespace} + err = r.Client.Get(context.TODO(), nsName, found) + + if err != nil { + if errors.IsNotFound(err) { + found = nil + err = nil + } else { + return err + } + } + + if found == nil { + log.Infof("Creating a new Sidecar in %s:%s", sidecar.Namespace, sidecar.Name) + err = r.Client.Create(context.TODO(), sidecar) + if err != nil { + return err + } + } else if rev := model.IstioRevFromLabel(found.Labels); rev != sfRev { + log.Infof("existed sidecar %v istioRev %s but our rev %s, skip update ...", + nsName, rev, sfRev) + } else { + if !reflect.DeepEqual(found.Spec, sidecar.Spec) { + log.Infof("Update a Sidecar in %s:%s", sidecar.Namespace, sidecar.Name) + sidecar.ResourceVersion = found.ResourceVersion + err = r.Client.Update(context.TODO(), sidecar) + if err != nil { + return err + } + } + } + + return nil +} + +// recordVisitor update the dest servicefences' visitor according to src sf's visit diff +func (r *ServicefenceReconciler) recordVisitor(sf *lazyloadv1alpha1.ServiceFence, diff Diff) { + for _, addHost := range diff.Added { + destSf := r.prepareDestFence(sf, addHost) + if destSf == nil { + continue + } + destSf.Status.Visitor[sf.Namespace+"/"+sf.Name] = true + _ = r.Client.Status().Update(context.TODO(), destSf) + } + + for _, delHost := range diff.Deleted { + destSf := r.prepareDestFence(sf, delHost) + if destSf == nil { + continue + } + delete(destSf.Status.Visitor, sf.Namespace+"/"+sf.Name) + _ = r.Client.Status().Update(context.TODO(), destSf) + } +} + +// prepareDestFence prepares servicefence of specified host +func (r *ServicefenceReconciler) prepareDestFence(srcSf *lazyloadv1alpha1.ServiceFence, h string) *lazyloadv1alpha1.ServiceFence { + nsName := parseHost(srcSf.Namespace, h) + if nsName == nil { + return nil + } + + svc := &corev1.Service{} + if err := r.Client.Get(context.TODO(), *nsName, svc); err != nil { + // XXX err handle + return nil + } + + destSf := &lazyloadv1alpha1.ServiceFence{} +retry: // FIXME fix infinite loop + err := r.Client.Get(context.TODO(), *nsName, destSf) + if err != nil { + if errors.IsNotFound(err) { + // XXX maybe should not auto create + destSf.Name = nsName.Name + destSf.Namespace = nsName.Namespace + // XXX set controlled by + // XXX patch rev + if err = r.Client.Create(context.TODO(), destSf); err != nil { + goto retry + } + } else { + return nil + } + } + + if destSf.Status.Visitor == nil { + destSf.Status.Visitor = make(map[string]bool) + } + return destSf +} + +func parseHost(sourceNs, h string) *types.NamespacedName { + s := strings.Split(h, ".") + if len(s) == 5 || len(s) == 2 { // shortname.ns or full(shortname.ns.svc.cluster.local) + return &types.NamespacedName{ + Namespace: s[1], + Name: s[0], + } + } + if len(s) == 1 { // shortname + return &types.NamespacedName{ + Namespace: sourceNs, + Name: s[0], + } + } + return nil // unknown host format, maybe external host +} + +func (r *ServicefenceReconciler) updateVisitedHostStatus(sf *lazyloadv1alpha1.ServiceFence) Diff { + domains := r.genDomains(sf, r.doAliasRules) + + delta := Diff{ + Deleted: make([]string, 0), + Added: make([]string, 0), + } + for k, dest := range sf.Status.Domains { + if _, ok := domains[k]; !ok { + if dest.Status == lazyloadv1alpha1.Destinations_ACTIVE { + // active -> pending + domains[k] = &lazyloadv1alpha1.Destinations{ + Hosts: dest.Hosts, + Status: lazyloadv1alpha1.Destinations_EXPIREWAIT, + } + } else { + // pending -> delete + delta.Deleted = append(delta.Deleted, k) + } + } + } + for k := range domains { + if _, ok := sf.Status.Domains[k]; !ok { + delta.Added = append(delta.Added, k) + } + } + sf.Status.Domains = domains + + _ = r.Client.Status().Update(context.TODO(), sf) + + return delta +} + +func (r *ServicefenceReconciler) genDomains(sf *lazyloadv1alpha1.ServiceFence, rules []*domainAliasRule) map[string]*lazyloadv1alpha1.Destinations { + domains := make(map[string]*lazyloadv1alpha1.Destinations) + + addDomainsWithHost(domains, sf, r.nsSvcCache, rules) + addDomainsWithLabelSelector(domains, sf, r.labelSvcCache, rules) + addDomainsWithMetricStatus(domains, sf, rules) + + return domains +} + +// update domains with spec.host +func addDomainsWithHost(domains map[string]*lazyloadv1alpha1.Destinations, sf *lazyloadv1alpha1.ServiceFence, nsSvcCache *NsSvcCache, + rules []*domainAliasRule, +) { + checkStatus := func(now int64, strategy *lazyloadv1alpha1.RecyclingStrategy) lazyloadv1alpha1.Destinations_Status { + switch { + case strategy.Stable != nil: + // ... + case strategy.Deadline != nil: + if now > strategy.Deadline.Expire.Seconds { + return lazyloadv1alpha1.Destinations_EXPIRE + } + case strategy.Auto != nil: + if strategy.RecentlyCalled != nil { + if now-strategy.RecentlyCalled.Seconds > strategy.Auto.Duration.Seconds { + return lazyloadv1alpha1.Destinations_EXPIRE + } + } + } + return lazyloadv1alpha1.Destinations_ACTIVE + } + + for h, strategy := range sf.Spec.Host { + if strings.HasSuffix(h, "/*") { + // handle namespace level host, like 'default/*' + handleNsHost(h, domains, nsSvcCache, rules) + } else { + // handle service level host, like 'a.default.svc.cluster.local' or 'www.netease.com' + handleSvcHost(h, strategy, checkStatus, domains, sf, rules) + } + } +} + +func handleNsHost(h string, domains map[string]*lazyloadv1alpha1.Destinations, nsSvcCache *NsSvcCache, rules []*domainAliasRule) { + hostParts := strings.Split(h, "/") + if len(hostParts) != 2 { + log.Errorf("%s is invalid host, skip", h) + return + } + + nsSvcCache.RLock() + defer nsSvcCache.RUnlock() + + svcs := nsSvcCache.Data[hostParts[0]] + var allHost []string + for svc := range svcs { + svcParts := strings.Split(svc, "/") + fullHost := fmt.Sprintf("%s.%s.svc.cluster.local", svcParts[1], svcParts[0]) + if !isValidHost(fullHost) { + continue + } + + fullHosts := domainAddAlias(fullHost, rules) + for _, fh := range fullHosts { + if domains[fh] != nil { + continue + } + // service relates to other services + if hs := getDestination(fh); len(hs) > 0 { + for i := 0; i < len(hs); { + hParts := strings.Split(hs[i], ".") + // ignore destSvc that in the same namespace + if hParts[1] == hostParts[0] { + hs[i], hs[len(hs)-1] = hs[len(hs)-1], hs[i] + hs = hs[:len(hs)-1] + } else { + i++ + } + } + + allHost = append(allHost, hs...) + } + } + } + domains[h] = &lazyloadv1alpha1.Destinations{ + Hosts: allHost, + Status: lazyloadv1alpha1.Destinations_ACTIVE, + } +} + +func handleSvcHost(fullHost string, strategy *lazyloadv1alpha1.RecyclingStrategy, + checkStatus func(now int64, strategy *lazyloadv1alpha1.RecyclingStrategy) lazyloadv1alpha1.Destinations_Status, + domains map[string]*lazyloadv1alpha1.Destinations, sf *lazyloadv1alpha1.ServiceFence, rules []*domainAliasRule, +) { + now := time.Now().Unix() + + if !isValidHost(fullHost) { + return + } + + fullHosts := domainAddAlias(fullHost, rules) + for _, fh := range fullHosts { + if domains[fh] != nil { + return + } + + allHost := []string{fh} + if hs := getDestination(fh); len(hs) > 0 { + allHost = append(allHost, hs...) + } + + domains[fh] = &lazyloadv1alpha1.Destinations{ + Hosts: allHost, + Status: checkStatus(now, strategy), + } + } +} + +// update domains with spec.labelSelector +func addDomainsWithLabelSelector(domains map[string]*lazyloadv1alpha1.Destinations, sf *lazyloadv1alpha1.ServiceFence, + labelSvcCache *LabelSvcCache, rules []*domainAliasRule, +) { + labelSvcCache.RLock() + defer labelSvcCache.RUnlock() + + for _, selector := range sf.Spec.LabelSelector { + + var result map[string]struct{} + // generate result for this selector + for k, v := range selector.Selector { + label := LabelItem{ + Name: k, + Value: v, + } + svcs := labelSvcCache.Data[label] + if svcs == nil { + result = nil + break + } + // init result + if result == nil { + result = make(map[string]struct{}, len(svcs)) + for svc := range svcs { + result[svc] = struct{}{} + } + } else { + // check result for other labels + for re := range result { + if _, ok := svcs[re]; !ok { + // not exist svc in this label cache + delete(result, re) + } + } + } + } + + // get hosts of each service + for re := range result { + subdomains := strings.Split(re, "/") + fullHost := fmt.Sprintf("%s.%s.svc.cluster.local", subdomains[1], subdomains[0]) + if !isValidHost(fullHost) { + continue + } + + fullHosts := domainAddAlias(fullHost, rules) + for _, fh := range fullHosts { + addToDomains(domains, fh) + } + } + + } +} + +// update domains with Status.MetricStatus +func addDomainsWithMetricStatus(domains map[string]*lazyloadv1alpha1.Destinations, sf *lazyloadv1alpha1.ServiceFence, rules []*domainAliasRule) { + for metricName := range sf.Status.MetricStatus { + metricName = strings.Trim(metricName, "{}") + if !strings.HasPrefix(metricName, "destination_service") && !strings.HasPrefix(metricName, "request_host") { + continue + } + // destination_service format like: "grafana.istio-system.svc.cluster.local" + + var fullHost string + // trim "" + if ss := strings.Split(metricName, "\""); len(ss) != 3 { + continue + } else { + // remove port + fullHost = strings.SplitN(ss[1], ":", 2)[0] + } + + if !isValidHost(fullHost) { + continue + } + + fullHosts := domainAddAlias(fullHost, rules) + for _, fh := range fullHosts { + addToDomains(domains, fh) + } + } +} + +func (r *ServicefenceReconciler) newSidecar(sf *lazyloadv1alpha1.ServiceFence, env bootstrap.Environment) (*v1alpha3.Sidecar, error) { + hosts := make([]string, 0) + + if !sf.Spec.Enable { + return nil, nil + } + + for _, ns := range r.defaultAddNamespaces { + hosts = append(hosts, ns+"/*") + } + + for k, v := range sf.Status.Domains { + if v.Status == lazyloadv1alpha1.Destinations_ACTIVE || v.Status == lazyloadv1alpha1.Destinations_EXPIREWAIT { + if strings.HasSuffix(k, "/*") { + if !r.isDefaultAddNs(k) { + hosts = append(hosts, k) + } + } + + for _, h := range v.Hosts { + hosts = append(hosts, "*/"+h) + } + } + } + + // check whether using namespace global-sidecar + // if so, init config of sidecar will adds */global-sidecar.${svf.ns}.svc.cluster.local + if env.Config.Global.Misc["globalSidecarMode"] == "namespace" { + hosts = append(hosts, fmt.Sprintf("*/global-sidecar.%s.svc.cluster.local", sf.Namespace)) + } + + // remove duplicated hosts + noDupHosts := make([]string, 0, len(hosts)) + temp := map[string]struct{}{} + for _, item := range hosts { + if _, ok := temp[item]; !ok { + temp[item] = struct{}{} + noDupHosts = append(noDupHosts, item) + } + } + hosts = noDupHosts + + // sort hosts so that it follows the Equals semantics + sort.Strings(hosts) + + sidecar := &istio.Sidecar{ + WorkloadSelector: &istio.WorkloadSelector{ + Labels: map[string]string{}, + }, + Egress: []*istio.IstioEgressListener{ + { + // Bind: "0.0.0.0", + Hosts: hosts, + }, + }, + } + + // Fetch the Service instance + nsName := types.NamespacedName{ + Name: sf.Name, + Namespace: sf.Namespace, + } + + svc := &corev1.Service{} + if err := r.Client.Get(context.TODO(), nsName, svc); err != nil { + if errors.IsNotFound(err) { + log.Warningf("cannot find service %s for servicefence, skip sidecar generating", nsName) + return nil, nil + } else { + log.Errorf("get service %s error, %+v", nsName, err) + return nil, err + } + } + + // generate sidecar.spec.workloadSelector + // priority: sf.spec.workloadSelector.labels > sf.spec.workloadSelector.fromService + if sf.Spec.WorkloadSelector != nil && len(sf.Spec.WorkloadSelector.Labels) > 0 { + // sidecar.WorkloadSelector.Labels = sf.Spec.WorkloadSelector.Labels + for k, v := range sf.Spec.WorkloadSelector.Labels { + sidecar.WorkloadSelector.Labels[k] = v + } + } else if sf.Spec.WorkloadSelector != nil && sf.Spec.WorkloadSelector.FromService { + // sidecar.WorkloadSelector.Labels = svc.Spec.Selector + for k, v := range svc.Spec.Selector { + sidecar.WorkloadSelector.Labels[k] = v + } + } else { + // compatible with old version lazyload + sidecar.WorkloadSelector.Labels[env.Config.Global.Service] = svc.Name + } + + spec, err := util.ProtoToMap(sidecar) + if err != nil { + return nil, err + } + ret := &v1alpha3.Sidecar{ + ObjectMeta: metav1.ObjectMeta{ + Name: sf.Name, + Namespace: sf.Namespace, + }, + Spec: spec, + } + return ret, nil +} + +func (r *ServicefenceReconciler) Subscribe(host string, destination interface{}) { // FIXME not used? + svc, namespace, ok := util.IsK8SService(host) + if !ok { + return + } + + sf := &lazyloadv1alpha1.ServiceFence{} + if err := r.Client.Get(context.TODO(), types.NamespacedName{Name: svc, Namespace: namespace}, sf); err != nil { + return + } + istioRev := model.IstioRevFromLabel(sf.Labels) + if !r.env.RevInScope(istioRev) { + return + } + + for k := range sf.Status.Visitor { + i := strings.Index(k, "/") + if i < 0 { + continue + } + + visitorSf := &lazyloadv1alpha1.ServiceFence{} + visitorNamespace := k[:i] + visitorName := k[i+1:] + + err := r.Client.Get(context.TODO(), types.NamespacedName{Name: visitorName, Namespace: visitorNamespace}, visitorSf) + if err != nil { + return + } + visitorRev := model.IstioRevFromLabel(visitorSf.Labels) + if !r.env.RevInScope(visitorRev) { + return + } + + r.updateVisitedHostStatus(visitorSf) + + sidecar, err := r.newSidecar(visitorSf, r.env) + if sidecar == nil { + continue + } + if err != nil { + return + } + + // Set VisitedHost instance as the owner and controller + if err := controllerutil.SetControllerReference(visitorSf, sidecar, r.Scheme); err != nil { + return + } + model.PatchIstioRevLabel(&sidecar.Labels, visitorRev) + + // Check for existence + found := &v1alpha3.Sidecar{} + nsName := types.NamespacedName{Name: sidecar.Name, Namespace: sidecar.Namespace} + err = r.Client.Get(context.TODO(), nsName, found) + if err != nil { + if errors.IsNotFound(err) { + found = nil + err = nil + } else { + return + } + } + + if found == nil { + err = r.Client.Create(context.TODO(), sidecar) + if err != nil { + log.Errorf("create sidecar %+v met err %v", sidecar, err) + } + } else if scRev := model.IstioRevFromLabel(found.Labels); scRev != visitorRev { + log.Infof("existing sidecar %v istioRev %s but our %s, skip ...", + nsName, scRev, visitorRev) + } else { + if !reflect.DeepEqual(found.Spec, sidecar.Spec) { + sidecar.ResourceVersion = found.ResourceVersion + err = r.Client.Update(context.TODO(), sidecar) + + if err != nil { + log.Errorf("create sidecar %+v met err %v", sidecar, err) + } + } + } + return + } + return +} + +func getDestination(k string) []string { + if i := controllers.HostDestinationMapping.Get(k); i != nil { + if hs, ok := i.([]string); ok { + return hs + } + } + return nil +} + +// TODO: More rigorous verification +func isValidHost(h string) bool { + if strings.Contains(h, "global-sidecar") || + strings.Contains(h, ":") || + strings.Contains(h, "unknown") { + return false + } + return true +} + +func (r *ServicefenceReconciler) isDefaultAddNs(ns string) bool { + for _, defaultNs := range r.defaultAddNamespaces { + if defaultNs == ns { + return true + } + } + return false +} + +func (r *ServicefenceReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&lazyloadv1alpha1.ServiceFence{}). + Complete(r) +} diff --git a/staging/src/slime.io/slime/modules/lazyload/controllers/suite_test.go b/staging/src/slime.io/slime/modules/lazyload/controllers/suite_test.go new file mode 100644 index 00000000..a708afd3 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/controllers/suite_test.go @@ -0,0 +1,78 @@ +/* + + +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 controllers + +import ( + "path/filepath" + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + "sigs.k8s.io/controller-runtime/pkg/envtest/printer" + microserviceslimeiov1alpha1 "slime.io/slime/modules/lazyload/api/v1alpha1" + // +kubebuilder:scaffold:imports +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +var ( + cfg *rest.Config + k8sClient client.Client + testEnv *envtest.Environment +) + +func TestAPIs(t *testing.T) { + RegisterFailHandler(Fail) + + RunSpecsWithDefaultAndCustomReporters(t, + "Controller Suite", + []Reporter{printer.NewlineReporter{}}) +} + +var _ = BeforeSuite(func(done Done) { + By("bootstrapping test environment") + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")}, + } + + var err error + cfg, err = testEnv.Start() + Expect(err).ToNot(HaveOccurred()) + Expect(cfg).ToNot(BeNil()) + + err = microserviceslimeiov1alpha1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + // +kubebuilder:scaffold:scheme + + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(err).ToNot(HaveOccurred()) + Expect(k8sClient).ToNot(BeNil()) + + close(done) +}, 60) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + err := testEnv.Stop() + Expect(err).ToNot(HaveOccurred()) +}) diff --git a/staging/src/slime.io/slime/modules/lazyload/go.mod b/staging/src/slime.io/slime/modules/lazyload/go.mod new file mode 100644 index 00000000..d1a00099 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/go.mod @@ -0,0 +1,27 @@ +module slime.io/slime/modules/lazyload + +go 1.13 + +require ( + github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad + github.com/gogo/protobuf v1.3.2 + github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b + github.com/golang/protobuf v1.4.2 + github.com/onsi/ginkgo v1.11.0 + github.com/onsi/gomega v1.8.1 + github.com/prometheus/client_golang v1.0.0 + github.com/sirupsen/logrus v1.4.2 + istio.io/api v0.0.0-20210322145030-ec7ef4cd6eaf + k8s.io/api v0.20.2 + k8s.io/apimachinery v0.20.2 + k8s.io/client-go v0.17.2 + sigs.k8s.io/controller-runtime v0.5.0 + slime.io/slime/framework v0.0.0-00010101000000-000000000000 +) + +replace ( + k8s.io/api => k8s.io/api v0.17.2 + k8s.io/apimachinery => k8s.io/apimachinery v0.17.2 + + slime.io/slime/framework => ../../../../../../framework +) diff --git a/staging/src/slime.io/slime/modules/lazyload/go.sum b/staging/src/slime.io/slime/modules/lazyload/go.sum new file mode 100644 index 00000000..e6a9e89e --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/go.sum @@ -0,0 +1,557 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= +github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= +github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 h1:cqQfy1jclcSy/FwLjemeg3SR1yaINm74aQyupQ0Bl8M= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QLSV/BsnenAOcDXdX4cMv4wP0B/5QbPg= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e h1:p1yVGRW3nmb85p1Sh1ZJSDm4A4iKLS5QNbvUHMgGu/M= +github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= +github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad h1:EmNYJhPYy0pOFjCx2PrgtaBXmee0iUX9hLlxE1xHOJE= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M= +github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= +github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/zapr v0.1.0 h1:h+WVe9j6HAA01niTJPA/kKH0i7e0rLZBCwauQFcRE54= +github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= +github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= +github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= +github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= +github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= +github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= +github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk= +github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= +github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= +github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= +github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= +github.com/go-openapi/spec v0.19.3 h1:0XRyw8kguri6Yw4SxhsQA/atC88yqrk0+G4YhI2wabc= +github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= +github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= +github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= +github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7 h1:u4bArs140e9+AfE52mFHOXVFnOSBJBRlzTHrOPLOIhE= +github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho= +github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995/go.mod h1:lJgMEyOkYFkPcDKwRXegd+iM6E7matEszMG5HhwytU8= +github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk= +github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= +github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= +github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= +github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw= +github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.8.1 h1:C5Dqfs/LeauYDX0jJXIe2SWmwCbGzx9yF8C8xy3Lh34= +github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/orcaman/concurrent-map v0.0.0-20210106121528-16402b402231 h1:fa50YL1pzKW+1SsBnJDOHppJN9stOEwS+CRWyUtyYGU= +github.com/orcaman/concurrent-map v0.0.0-20210106121528-16402b402231/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.0.1 h1:xyiBuvkD2g5n7cYzx6u2sxQvsAy4QJsZFCzGVdzOXZ0= +gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= +gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.35.0 h1:TwIQcH3es+MojMVojxxfQ3l3OF2KzlRxML2xZq0kRo8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +istio.io/api v0.0.0-20210322145030-ec7ef4cd6eaf h1:wAkMg4dDRWkas2Gzkln5926OvHw3g/XbKsjICF/msQw= +istio.io/api v0.0.0-20210322145030-ec7ef4cd6eaf/go.mod h1:nsSFw1LIMmGL7r/+6fJI6FxeG/UGlLxRK8bkojIvBVs= +istio.io/gogo-genproto v0.0.0-20210113155706-4daf5697332f h1:9710FpGLvIJ1GGEbpuTh1smVBv+r8cJfR3G82ouSxIQ= +istio.io/gogo-genproto v0.0.0-20210113155706-4daf5697332f/go.mod h1:6BwTZRNbWS570wHX/uR1Wqk5e0157TofTAUMzT7N4+s= +k8s.io/api v0.17.2 h1:NF1UFXcKN7/OOv1uxdRz3qfra8AHsPav5M93hlV9+Dc= +k8s.io/api v0.17.2/go.mod h1:BS9fjjLc4CMuqfSO8vgbHPKMt5+SF0ET6u/RVDihTo4= +k8s.io/apiextensions-apiserver v0.17.2 h1:cP579D2hSZNuO/rZj9XFRzwJNYb41DbNANJb6Kolpss= +k8s.io/apiextensions-apiserver v0.17.2/go.mod h1:4KdMpjkEjjDI2pPfBA15OscyNldHWdBCfsWMDWAmSTs= +k8s.io/apimachinery v0.17.2 h1:hwDQQFbdRlpnnsR64Asdi55GyCaIP/3WQpMmbNBeWr4= +k8s.io/apimachinery v0.17.2/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= +k8s.io/apiserver v0.17.2/go.mod h1:lBmw/TtQdtxvrTk0e2cgtOxHizXI+d0mmGQURIHQZlo= +k8s.io/cli-runtime v0.17.2/go.mod h1:aa8t9ziyQdbkuizkNLAw3qe3srSyWh9zlSB7zTqRNPI= +k8s.io/client-go v0.17.2 h1:ndIfkfXEGrNhLIgkr0+qhRguSD3u6DCmonepn1O6NYc= +k8s.io/client-go v0.17.2/go.mod h1:QAzRgsa0C2xl4/eVpeVAZMvikCn8Nm81yqVx3Kk9XYI= +k8s.io/code-generator v0.17.2/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s= +k8s.io/component-base v0.17.2/go.mod h1:zMPW3g5aH7cHJpKYQ/ZsGMcgbsA/VyhEugF3QT1awLs= +k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= +k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= +k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLyy7rfbeuf1PYyBf973pgyU= +k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= +k8s.io/kubectl v0.17.2 h1:QZR8Q6lWiVRjwKslekdbN5WPMp53dS/17j5e+oi5XVU= +k8s.io/kubectl v0.17.2/go.mod h1:y4rfLV0n6aPmvbRCqZQjvOp3ezxsFgpqL+zF5jH/lxk= +k8s.io/metrics v0.17.2/go.mod h1:3TkNHET4ROd+NfzNxkjoVfQ0Ob4iZnaHmSEA4vYpwLw= +k8s.io/utils v0.0.0-20191114184206-e782cd3c129f h1:GiPwtSzdP43eI1hpPCbROQCCIgCuiMMNF8YUVLF3vJo= +k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= +modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= +modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= +modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= +modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= +sigs.k8s.io/controller-runtime v0.5.0 h1:CbqIy5fbUX+4E9bpnBFd204YAzRYlM9SWW77BbrcDQo= +sigs.k8s.io/controller-runtime v0.5.0/go.mod h1:REiJzC7Y00U+2YkMbT8wxgrsX5USpXKGhb2sCtAXiT8= +sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= +sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= +sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18= +sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI= diff --git a/staging/src/slime.io/slime/modules/lazyload/install/config/bookinfo.yaml b/staging/src/slime.io/slime/modules/lazyload/install/config/bookinfo.yaml new file mode 100644 index 00000000..bb5d77dd --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/install/config/bookinfo.yaml @@ -0,0 +1,343 @@ +# Copyright Istio Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +################################################################################################## +# This file defines the services, service accounts, and deployments for the Bookinfo sample. +# +# To apply all 4 Bookinfo services, their corresponding service accounts, and deployments: +# +# kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml +# +# Alternatively, you can deploy any resource separately: +# +# kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml -l service=reviews # reviews Service +# kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml -l account=reviews # reviews ServiceAccount +# kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml -l app=reviews,version=v3 # reviews-v3 Deployment +################################################################################################## + +################################################################################################## +# Details service +################################################################################################## +apiVersion: v1 +kind: Service +metadata: + name: details + labels: + app: details + service: details +spec: + ports: + - port: 9080 + name: http + selector: + app: details +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: bookinfo-details + labels: + account: details +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: details-v1 + labels: + app: details + version: v1 +spec: + replicas: 1 + selector: + matchLabels: + app: details + version: v1 + template: + metadata: + labels: + app: details + version: v1 + spec: + serviceAccountName: bookinfo-details + containers: + - name: details + image: docker.io/istio/examples-bookinfo-details-v1:1.16.2 + imagePullPolicy: IfNotPresent + ports: + - containerPort: 9080 + securityContext: + runAsUser: 1000 +--- +################################################################################################## +# Ratings service +################################################################################################## +apiVersion: v1 +kind: Service +metadata: + name: ratings + labels: + app: ratings + service: ratings +spec: + ports: + - port: 9080 + name: http + selector: + app: ratings +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: bookinfo-ratings + labels: + account: ratings +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: ratings-v1 + labels: + app: ratings + version: v1 +spec: + replicas: 1 + selector: + matchLabels: + app: ratings + version: v1 + template: + metadata: + labels: + app: ratings + version: v1 + spec: + serviceAccountName: bookinfo-ratings + containers: + - name: ratings + image: docker.io/istio/examples-bookinfo-ratings-v1:1.16.2 + imagePullPolicy: IfNotPresent + ports: + - containerPort: 9080 + securityContext: + runAsUser: 1000 +--- +################################################################################################## +# Reviews service +################################################################################################## +apiVersion: v1 +kind: Service +metadata: + name: reviews + labels: + app: reviews + service: reviews +spec: + ports: + - port: 9080 + name: http + selector: + app: reviews +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: bookinfo-reviews + labels: + account: reviews +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: reviews-v1 + labels: + app: reviews + version: v1 +spec: + replicas: 1 + selector: + matchLabels: + app: reviews + version: v1 + template: + metadata: + labels: + app: reviews + version: v1 + spec: + serviceAccountName: bookinfo-reviews + containers: + - name: reviews + image: docker.io/istio/examples-bookinfo-reviews-v1:1.16.2 + imagePullPolicy: IfNotPresent + env: + - name: LOG_DIR + value: "/tmp/logs" + ports: + - containerPort: 9080 + volumeMounts: + - name: tmp + mountPath: /tmp + - name: wlp-output + mountPath: /opt/ibm/wlp/output + securityContext: + runAsUser: 1000 + volumes: + - name: wlp-output + emptyDir: {} + - name: tmp + emptyDir: {} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: reviews-v2 + labels: + app: reviews + version: v2 +spec: + replicas: 1 + selector: + matchLabels: + app: reviews + version: v2 + template: + metadata: + labels: + app: reviews + version: v2 + spec: + serviceAccountName: bookinfo-reviews + containers: + - name: reviews + image: docker.io/istio/examples-bookinfo-reviews-v2:1.16.2 + imagePullPolicy: IfNotPresent + env: + - name: LOG_DIR + value: "/tmp/logs" + ports: + - containerPort: 9080 + volumeMounts: + - name: tmp + mountPath: /tmp + - name: wlp-output + mountPath: /opt/ibm/wlp/output + securityContext: + runAsUser: 1000 + volumes: + - name: wlp-output + emptyDir: {} + - name: tmp + emptyDir: {} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: reviews-v3 + labels: + app: reviews + version: v3 +spec: + replicas: 1 + selector: + matchLabels: + app: reviews + version: v3 + template: + metadata: + labels: + app: reviews + version: v3 + spec: + serviceAccountName: bookinfo-reviews + containers: + - name: reviews + image: docker.io/istio/examples-bookinfo-reviews-v3:1.16.2 + imagePullPolicy: IfNotPresent + env: + - name: LOG_DIR + value: "/tmp/logs" + ports: + - containerPort: 9080 + volumeMounts: + - name: tmp + mountPath: /tmp + - name: wlp-output + mountPath: /opt/ibm/wlp/output + securityContext: + runAsUser: 1000 + volumes: + - name: wlp-output + emptyDir: {} + - name: tmp + emptyDir: {} +--- +################################################################################################## +# Productpage services +################################################################################################## +apiVersion: v1 +kind: Service +metadata: + name: productpage + labels: + app: productpage + service: productpage +spec: + ports: + - port: 9080 + name: http + selector: + app: productpage +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: bookinfo-productpage + labels: + account: productpage +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: productpage-v1 + labels: + app: productpage + version: v1 +spec: + replicas: 1 + selector: + matchLabels: + app: productpage + version: v1 + template: + metadata: + labels: + app: productpage + version: v1 + spec: + serviceAccountName: bookinfo-productpage + containers: + - name: productpage + image: docker.io/istio/examples-bookinfo-productpage-v1:1.16.2 + imagePullPolicy: IfNotPresent + ports: + - containerPort: 9080 + volumeMounts: + - name: tmp + mountPath: /tmp + securityContext: + runAsUser: 1000 + volumes: + - name: tmp + emptyDir: {} +--- diff --git a/staging/src/slime.io/slime/modules/lazyload/install/config/prometheus.yaml b/staging/src/slime.io/slime/modules/lazyload/install/config/prometheus.yaml new file mode 100644 index 00000000..6528d455 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/install/config/prometheus.yaml @@ -0,0 +1,487 @@ +--- +# Source: prometheus/templates/server/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + component: "server" + app: prometheus + release: prometheus + chart: prometheus-13.6.0 + heritage: Helm + name: prometheus + namespace: istio-system + annotations: + {} +--- +# Source: prometheus/templates/server/cm.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + component: "server" + app: prometheus + release: prometheus + chart: prometheus-13.6.0 + heritage: Helm + name: prometheus + namespace: istio-system +data: + alerting_rules.yml: | + {} + alerts: | + {} + prometheus.yml: | + global: + evaluation_interval: 1m + scrape_interval: 15s + scrape_timeout: 10s + rule_files: + - /etc/config/recording_rules.yml + - /etc/config/alerting_rules.yml + - /etc/config/rules + - /etc/config/alerts + scrape_configs: + - job_name: prometheus + static_configs: + - targets: + - localhost:9090 + - bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token + job_name: kubernetes-apiservers + kubernetes_sd_configs: + - role: endpoints + relabel_configs: + - action: keep + regex: default;kubernetes;https + source_labels: + - __meta_kubernetes_namespace + - __meta_kubernetes_service_name + - __meta_kubernetes_endpoint_port_name + scheme: https + tls_config: + ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + insecure_skip_verify: true + - bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token + job_name: kubernetes-nodes + kubernetes_sd_configs: + - role: node + relabel_configs: + - action: labelmap + regex: __meta_kubernetes_node_label_(.+) + - replacement: kubernetes.default.svc:443 + target_label: __address__ + - regex: (.+) + replacement: /api/v1/nodes/$1/proxy/metrics + source_labels: + - __meta_kubernetes_node_name + target_label: __metrics_path__ + scheme: https + tls_config: + ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + insecure_skip_verify: true + - bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token + job_name: kubernetes-nodes-cadvisor + kubernetes_sd_configs: + - role: node + relabel_configs: + - action: labelmap + regex: __meta_kubernetes_node_label_(.+) + - replacement: kubernetes.default.svc:443 + target_label: __address__ + - regex: (.+) + replacement: /api/v1/nodes/$1/proxy/metrics/cadvisor + source_labels: + - __meta_kubernetes_node_name + target_label: __metrics_path__ + scheme: https + tls_config: + ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + insecure_skip_verify: true + - job_name: kubernetes-service-endpoints + kubernetes_sd_configs: + - role: endpoints + relabel_configs: + - action: keep + regex: true + source_labels: + - __meta_kubernetes_service_annotation_prometheus_io_scrape + - action: replace + regex: (https?) + source_labels: + - __meta_kubernetes_service_annotation_prometheus_io_scheme + target_label: __scheme__ + - action: replace + regex: (.+) + source_labels: + - __meta_kubernetes_service_annotation_prometheus_io_path + target_label: __metrics_path__ + - action: replace + regex: ([^:]+)(?::\d+)?;(\d+) + replacement: $1:$2 + source_labels: + - __address__ + - __meta_kubernetes_service_annotation_prometheus_io_port + target_label: __address__ + - action: labelmap + regex: __meta_kubernetes_service_label_(.+) + - action: replace + source_labels: + - __meta_kubernetes_namespace + target_label: kubernetes_namespace + - action: replace + source_labels: + - __meta_kubernetes_service_name + target_label: kubernetes_name + - action: replace + source_labels: + - __meta_kubernetes_pod_node_name + target_label: kubernetes_node + - job_name: kubernetes-service-endpoints-slow + kubernetes_sd_configs: + - role: endpoints + relabel_configs: + - action: keep + regex: true + source_labels: + - __meta_kubernetes_service_annotation_prometheus_io_scrape_slow + - action: replace + regex: (https?) + source_labels: + - __meta_kubernetes_service_annotation_prometheus_io_scheme + target_label: __scheme__ + - action: replace + regex: (.+) + source_labels: + - __meta_kubernetes_service_annotation_prometheus_io_path + target_label: __metrics_path__ + - action: replace + regex: ([^:]+)(?::\d+)?;(\d+) + replacement: $1:$2 + source_labels: + - __address__ + - __meta_kubernetes_service_annotation_prometheus_io_port + target_label: __address__ + - action: labelmap + regex: __meta_kubernetes_service_label_(.+) + - action: replace + source_labels: + - __meta_kubernetes_namespace + target_label: kubernetes_namespace + - action: replace + source_labels: + - __meta_kubernetes_service_name + target_label: kubernetes_name + - action: replace + source_labels: + - __meta_kubernetes_pod_node_name + target_label: kubernetes_node + scrape_interval: 5m + scrape_timeout: 30s + - honor_labels: true + job_name: prometheus-pushgateway + kubernetes_sd_configs: + - role: service + relabel_configs: + - action: keep + regex: pushgateway + source_labels: + - __meta_kubernetes_service_annotation_prometheus_io_probe + - job_name: kubernetes-services + kubernetes_sd_configs: + - role: service + metrics_path: /probe + params: + module: + - http_2xx + relabel_configs: + - action: keep + regex: true + source_labels: + - __meta_kubernetes_service_annotation_prometheus_io_probe + - source_labels: + - __address__ + target_label: __param_target + - replacement: blackbox + target_label: __address__ + - source_labels: + - __param_target + target_label: instance + - action: labelmap + regex: __meta_kubernetes_service_label_(.+) + - source_labels: + - __meta_kubernetes_namespace + target_label: kubernetes_namespace + - source_labels: + - __meta_kubernetes_service_name + target_label: kubernetes_name + - job_name: kubernetes-pods + kubernetes_sd_configs: + - role: pod + relabel_configs: + - action: keep + regex: true + source_labels: + - __meta_kubernetes_pod_annotation_prometheus_io_scrape + - action: replace + regex: (https?) + source_labels: + - __meta_kubernetes_pod_annotation_prometheus_io_scheme + target_label: __scheme__ + - action: replace + regex: (.+) + source_labels: + - __meta_kubernetes_pod_annotation_prometheus_io_path + target_label: __metrics_path__ + - action: replace + regex: ([^:]+)(?::\d+)?;(\d+) + replacement: $1:$2 + source_labels: + - __address__ + - __meta_kubernetes_pod_annotation_prometheus_io_port + target_label: __address__ + - action: labelmap + regex: __meta_kubernetes_pod_label_(.+) + - action: replace + source_labels: + - __meta_kubernetes_namespace + target_label: kubernetes_namespace + - action: replace + source_labels: + - __meta_kubernetes_pod_name + target_label: kubernetes_pod_name + - action: drop + regex: Pending|Succeeded|Failed + source_labels: + - __meta_kubernetes_pod_phase + - job_name: kubernetes-pods-slow + kubernetes_sd_configs: + - role: pod + relabel_configs: + - action: keep + regex: true + source_labels: + - __meta_kubernetes_pod_annotation_prometheus_io_scrape_slow + - action: replace + regex: (https?) + source_labels: + - __meta_kubernetes_pod_annotation_prometheus_io_scheme + target_label: __scheme__ + - action: replace + regex: (.+) + source_labels: + - __meta_kubernetes_pod_annotation_prometheus_io_path + target_label: __metrics_path__ + - action: replace + regex: ([^:]+)(?::\d+)?;(\d+) + replacement: $1:$2 + source_labels: + - __address__ + - __meta_kubernetes_pod_annotation_prometheus_io_port + target_label: __address__ + - action: labelmap + regex: __meta_kubernetes_pod_label_(.+) + - action: replace + source_labels: + - __meta_kubernetes_namespace + target_label: kubernetes_namespace + - action: replace + source_labels: + - __meta_kubernetes_pod_name + target_label: kubernetes_pod_name + - action: drop + regex: Pending|Succeeded|Failed + source_labels: + - __meta_kubernetes_pod_phase + scrape_interval: 5m + scrape_timeout: 30s + recording_rules.yml: | + {} + rules: | + {} +--- +# Source: prometheus/templates/server/clusterrole.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + component: "server" + app: prometheus + release: prometheus + chart: prometheus-13.6.0 + heritage: Helm + name: prometheus +rules: + - apiGroups: + - "" + resources: + - nodes + - nodes/proxy + - nodes/metrics + - services + - endpoints + - pods + - ingresses + - configmaps + verbs: + - get + - list + - watch + - apiGroups: + - "extensions" + - "networking.k8s.io" + resources: + - ingresses/status + - ingresses + verbs: + - get + - list + - watch + - nonResourceURLs: + - "/metrics" + verbs: + - get +--- +# Source: prometheus/templates/server/clusterrolebinding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + component: "server" + app: prometheus + release: prometheus + chart: prometheus-13.6.0 + heritage: Helm + name: prometheus +subjects: + - kind: ServiceAccount + name: prometheus + namespace: istio-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: prometheus +--- +# Source: prometheus/templates/server/service.yaml +apiVersion: v1 +kind: Service +metadata: + labels: + component: "server" + app: prometheus + release: prometheus + chart: prometheus-13.6.0 + heritage: Helm + name: prometheus + namespace: istio-system +spec: + ports: + - name: http + port: 9090 + protocol: TCP + targetPort: 9090 + selector: + component: "server" + app: prometheus + release: prometheus + sessionAffinity: None + type: "ClusterIP" +--- +# Source: prometheus/templates/server/deploy.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + component: "server" + app: prometheus + release: prometheus + chart: prometheus-13.6.0 + heritage: Helm + name: prometheus + namespace: istio-system +spec: + selector: + matchLabels: + component: "server" + app: prometheus + release: prometheus + replicas: 1 + template: + metadata: + annotations: + + sidecar.istio.io/inject: "false" + labels: + component: "server" + app: prometheus + release: prometheus + chart: prometheus-13.6.0 + heritage: Helm + spec: + serviceAccountName: prometheus + containers: + - name: prometheus-server-configmap-reload + image: "jimmidyson/configmap-reload:v0.5.0" + imagePullPolicy: "IfNotPresent" + args: + - --volume-dir=/etc/config + - --webhook-url=http://127.0.0.1:9090/-/reload + resources: + {} + volumeMounts: + - name: config-volume + mountPath: /etc/config + readOnly: true + + - name: prometheus-server + image: "prom/prometheus:v2.24.0" + imagePullPolicy: "IfNotPresent" + args: + - --storage.tsdb.retention.time=15d + - --config.file=/etc/config/prometheus.yml + - --storage.tsdb.path=/data + - --web.console.libraries=/etc/prometheus/console_libraries + - --web.console.templates=/etc/prometheus/consoles + - --web.enable-lifecycle + ports: + - containerPort: 9090 + readinessProbe: + httpGet: + path: /-/ready + port: 9090 + initialDelaySeconds: 0 + periodSeconds: 5 + timeoutSeconds: 4 + failureThreshold: 3 + successThreshold: 1 + livenessProbe: + httpGet: + path: /-/healthy + port: 9090 + initialDelaySeconds: 30 + periodSeconds: 15 + timeoutSeconds: 10 + failureThreshold: 3 + successThreshold: 1 + resources: + {} + volumeMounts: + - name: config-volume + mountPath: /etc/config + - name: storage-volume + mountPath: /data + subPath: "" + hostNetwork: false + dnsPolicy: ClusterFirst + securityContext: + fsGroup: 65534 + runAsGroup: 65534 + runAsNonRoot: true + runAsUser: 65534 + terminationGracePeriodSeconds: 300 + volumes: + - name: config-volume + configMap: + name: prometheus + - name: storage-volume + emptyDir: + {} diff --git a/staging/src/slime.io/slime/modules/lazyload/install/init/crds.yaml b/staging/src/slime.io/slime/modules/lazyload/install/init/crds.yaml new file mode 100644 index 00000000..d5e75177 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/install/init/crds.yaml @@ -0,0 +1,44 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: servicefences.microservice.slime.io +spec: + group: microservice.slime.io + names: + shortNames: + - svf + kind: ServiceFence + listKind: ServiceFenceList + plural: servicefences + singular: servicefence + scope: Namespaced + subresources: + status: { } + versions: + - name: v1alpha1 + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: slimeboots.config.netease.com +spec: + group: config.netease.com + names: + kind: SlimeBoot + listKind: SlimeBootList + plural: slimeboots + singular: slimeboot + scope: Namespaced + subresources: + status: {} + validation: + openAPIV3Schema: + type: object + x-kubernetes-preserve-unknown-fields: true + versions: + - name: v1alpha1 + served: true + storage: true + diff --git a/staging/src/slime.io/slime/modules/lazyload/install/init/deployment_slime-boot.yaml b/staging/src/slime.io/slime/modules/lazyload/install/init/deployment_slime-boot.yaml new file mode 100644 index 00000000..3c88c42f --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/install/init/deployment_slime-boot.yaml @@ -0,0 +1,63 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: slime-boot + namespace: mesh-operator +spec: + replicas: 1 + selector: + matchLabels: + name: slime-boot + template: + metadata: + labels: + name: slime-boot + spec: + serviceAccountName: slime-boot + containers: + - name: slime-boot + # Replace this with the built image name + image: docker.io/slimeio/slime-boot:v0.4.0_linux_amd64 + imagePullPolicy: Always + env: + - name: WATCH_NAMESPACE + value: "" + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: OPERATOR_NAME + value: "slime-boot" +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: slime-boot + namespace: mesh-operator +subjects: + - kind: ServiceAccount + name: slime-boot + namespace: mesh-operator +roleRef: + kind: Role + name: slime-boot + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: slime-boot + namespace: mesh-operator +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: slime-boot +subjects: + - kind: ServiceAccount + name: slime-boot + namespace: mesh-operator +roleRef: + kind: ClusterRole + name: cluster-admin + apiGroup: rbac.authorization.k8s.io diff --git a/staging/src/slime.io/slime/modules/lazyload/install/samples/lazyload/easy_install_lazyload.sh b/staging/src/slime.io/slime/modules/lazyload/install/samples/lazyload/easy_install_lazyload.sh new file mode 100644 index 00000000..fbeb6052 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/install/samples/lazyload/easy_install_lazyload.sh @@ -0,0 +1,22 @@ +#!/bin/bash +if [[ "$#" -eq 0 ]]; then + echo "No specified tag or commit. Use the latest tag." + tag_or_commit=$(curl -s https://api.github.com/repos/slime-io/lazyload/tags | grep 'name' | cut -d\" -f4 | head -1) + if [[ -z $tag_or_commit ]]; then + echo "Failed to get the latest tag. Exited." + exit 1 + fi + echo "The Latest tag: $tag_or_commit." +else + tag_or_commit=$1 + echo "Use specified tag or commit: $tag_or_commit" +fi + +crds_url="https://raw.githubusercontent.com/slime-io/lazyload/$tag_or_commit/install/init/crds.yaml" +deployment_slimeboot_url="https://raw.githubusercontent.com/slime-io/lazyload/$tag_or_commit/install/init/deployment_slime-boot.yaml" +slimeboot_lazyload_url="https://raw.githubusercontent.com/slime-io/lazyload/$tag_or_commit/install/samples/lazyload/slimeboot_cluster_accesslog.yaml" + +kubectl create ns mesh-operator +kubectl apply -f "${crds_url}" +kubectl apply -f "${deployment_slimeboot_url}" +kubectl apply -f "${slimeboot_lazyload_url}" diff --git a/staging/src/slime.io/slime/modules/lazyload/install/samples/lazyload/easy_uninstall_lazyload.sh b/staging/src/slime.io/slime/modules/lazyload/install/samples/lazyload/easy_uninstall_lazyload.sh new file mode 100644 index 00000000..37f699bd --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/install/samples/lazyload/easy_uninstall_lazyload.sh @@ -0,0 +1,23 @@ +#!/bin/bash +if [[ "$#" -eq 0 ]]; then + echo "No specified tag or commit. Use the latest tag." + tag_or_commit=$(curl -s https://api.github.com/repos/slime-io/lazyload/tags | grep 'name' | cut -d\" -f4 | head -1) + if [[ -z $tag_or_commit ]]; then + echo "Failed to get the latest tag. Exited." + exit 1 + fi + echo "The Latest tag: $tag_or_commit." +else + tag_or_commit=$1 + echo "Use specified tag or commit: $tag_or_commit" +fi + +crds_url="https://raw.githubusercontent.com/slime-io/lazyload/$tag_or_commit/install/init/crds.yaml" +deployment_slimeboot_url="https://raw.githubusercontent.com/slime-io/lazyload/$tag_or_commit/install/init/deployment_slime-boot.yaml" +slimeboot_lazyload_url="https://raw.githubusercontent.com/slime-io/lazyload/$tag_or_commit/install/samples/lazyload/slimeboot_cluster_accesslog.yaml" + +for i in $(kubectl get ns --no-headers |awk '{print $1}');do kubectl delete servicefence -n $i --all;done +kubectl delete -f "${slimeboot_lazyload_url}" +kubectl delete -f "${deployment_slimeboot_url}" +kubectl delete -f "${crds_url}" +kubectl delete ns mesh-operator diff --git a/staging/src/slime.io/slime/modules/lazyload/install/samples/lazyload/pvc_lazyload.yaml b/staging/src/slime.io/slime/modules/lazyload/install/samples/lazyload/pvc_lazyload.yaml new file mode 100644 index 00000000..49f1830f --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/install/samples/lazyload/pvc_lazyload.yaml @@ -0,0 +1,29 @@ +# hostPath pv for minikube demo +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: lazyload-claim + namespace: mesh-operator +spec: + storageClassName: manual + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 3Gi +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: lazyload-volumn + labels: + type: local +spec: + storageClassName: manual + capacity: + storage: 5Gi + accessModes: + - ReadWriteOnce + hostPath: + path: "/mnt/data" diff --git a/staging/src/slime.io/slime/modules/lazyload/install/samples/lazyload/servicefence_productpage.yaml b/staging/src/slime.io/slime/modules/lazyload/install/samples/lazyload/servicefence_productpage.yaml new file mode 100644 index 00000000..d0e0c395 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/install/samples/lazyload/servicefence_productpage.yaml @@ -0,0 +1,8 @@ +--- +apiVersion: microservice.slime.io/v1alpha1 +kind: ServiceFence +metadata: + name: productpage + namespace: default +spec: + enable: true diff --git a/staging/src/slime.io/slime/modules/lazyload/install/samples/lazyload/slimeboot_cluster_accesslog.yaml b/staging/src/slime.io/slime/modules/lazyload/install/samples/lazyload/slimeboot_cluster_accesslog.yaml new file mode 100644 index 00000000..43152af8 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/install/samples/lazyload/slimeboot_cluster_accesslog.yaml @@ -0,0 +1,48 @@ +--- +apiVersion: config.netease.com/v1alpha1 +kind: SlimeBoot +metadata: + name: lazyload + namespace: mesh-operator +spec: + image: + pullPolicy: Always + repository: docker.io/slimeio/slime-lazyload + tag: v0.3.0_linux_amd64 + module: + - name: lazyload # custom value + kind: lazyload # should be "lazyload" + enable: true + general: # replace previous "fence" field + wormholePort: # replace to your application service ports, and extend the list in case of multi ports + - "80" + - "9080" + global: + misc: + globalSidecarMode: cluster # inform the mode of global-sidecar + metricSourceType: accesslog # indicate the metric source + component: + globalSidecar: + enable: true + sidecarInject: + enable: true # should be true + # mode definition: + # "pod": sidecar auto-inject on pod level, need provide labels for injection + # "namespace": sidecar auto-inject on namespace level, no need to provide labels for injection + # if globalSidecarMode is cluster, global-sidecar will be deployed in slime namespace, which does not enable auto-inject on namespace level, mode can only be "pod". + # if globalSidecarMode is namespace, depending on the namespace definition, mode can be "pod" or "namespace". + mode: pod + labels: # optional, used for sidecarInject.mode = pod + sidecar.istio.io/inject: "true" + # istio.io/rev: canary # use control plane revisions + resources: + requests: + cpu: 200m + memory: 200Mi + limits: + cpu: 400m + memory: 400Mi + image: + repository: docker.io/slimeio/slime-global-sidecar + tag: v0.2.0-1b93bf7 + probePort: 8080 diff --git a/staging/src/slime.io/slime/modules/lazyload/install/samples/lazyload/slimeboot_cluster_prometheus.yaml b/staging/src/slime.io/slime/modules/lazyload/install/samples/lazyload/slimeboot_cluster_prometheus.yaml new file mode 100644 index 00000000..4aca5252 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/install/samples/lazyload/slimeboot_cluster_prometheus.yaml @@ -0,0 +1,54 @@ +--- +apiVersion: config.netease.com/v1alpha1 +kind: SlimeBoot +metadata: + name: lazyload + namespace: mesh-operator +spec: + image: + pullPolicy: Always + repository: docker.io/slimeio/slime-lazyload + tag: v0.3.0_linux_amd64 + module: + - name: lazyload # custom value + kind: lazyload # should be "lazyload" + enable: true + general: # replace previous "fence" field + wormholePort: # replace to your application service ports, and extend the list in case of multi ports + - "80" + - "9080" + global: + misc: + globalSidecarMode: cluster # inform the mode of global-sidecar + metric: # indicate the metric source + prometheus: + address: http://prometheus.istio-system:9090 + handlers: + destination: + query: | + sum(istio_requests_total{source_app="$source_app",reporter="destination"})by(destination_service) + type: Group + component: + globalSidecar: + enable: true + sidecarInject: + enable: true # should be true + # mode definition: + # "pod": sidecar auto-inject on pod level, need provide labels for injection + # "namespace": sidecar auto-inject on namespace level, no need to provide labels for injection + # if globalSidecarMode is cluster, global-sidecar will be deployed in slime namespace, which does not enable auto-inject on namespace level, mode can only be "pod". + # if globalSidecarMode is namespace, depending on the namespace definition, mode can be "pod" or "namespace". + mode: pod + labels: # optional, used for sidecarInject.mode = pod + sidecar.istio.io/inject: "true" + # istio.io/rev: canary # use control plane revisions + resources: + requests: + cpu: 200m + memory: 200Mi + limits: + cpu: 400m + memory: 400Mi + image: + repository: docker.io/slimeio/slime-global-sidecar + tag: v0.2.0-1b93bf7 diff --git a/staging/src/slime.io/slime/modules/lazyload/install/samples/lazyload/slimeboot_logrotate.yaml b/staging/src/slime.io/slime/modules/lazyload/install/samples/lazyload/slimeboot_logrotate.yaml new file mode 100644 index 00000000..87a83e7f --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/install/samples/lazyload/slimeboot_logrotate.yaml @@ -0,0 +1,59 @@ +--- +apiVersion: config.netease.com/v1alpha1 +kind: SlimeBoot +metadata: + name: lazyload + namespace: mesh-operator +spec: + image: + pullPolicy: Always + repository: docker.io/slimeio/slime-lazyload + tag: v0.3.0_linux_amd64 + module: + - name: lazyload # custom value + kind: lazyload # should be "lazyload" + enable: true + general: # replace previous "fence" field + wormholePort: + - "80" + - "9080" + global: + misc: + globalSidecarMode: cluster + metricSourceType: accesslog + log: + logRotate: true + logRotateConfig: + filePath: "/tmp/log/slime.log" + maxSizeMB: 100 + maxBackups: 10 + maxAgeDay: 10 + compress: true + component: + globalSidecar: + enable: true + type: cluster + sidecarInject: + enable: true # should be true + mode: pod # if type = cluster, can only be "pod"; if type = namespace, can be "pod" or "namespace" + labels: # optional, used for sidecarInject.mode = pod + sidecar.istio.io/inject: "true" + resources: + requests: + cpu: 200m + memory: 200Mi + limits: + cpu: 400m + memory: 400Mi + image: + repository: docker.io/slimeio/slime-global-sidecar + tag: v0.2.0-1b93bf7 + volumes: + - name: lazyload-storage + persistentVolumeClaim: + claimName: lazyload-claim + containers: + slime: + volumeMounts: + - mountPath: "/tmp/log" + name: lazyload-storage diff --git a/staging/src/slime.io/slime/modules/lazyload/install/samples/lazyload/slimeboot_namespace_accesslog.yaml b/staging/src/slime.io/slime/modules/lazyload/install/samples/lazyload/slimeboot_namespace_accesslog.yaml new file mode 100644 index 00000000..f36f3621 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/install/samples/lazyload/slimeboot_namespace_accesslog.yaml @@ -0,0 +1,49 @@ +--- +apiVersion: config.netease.com/v1alpha1 +kind: SlimeBoot +metadata: + name: lazyload + namespace: mesh-operator +spec: + image: + pullPolicy: Always + repository: docker.io/slimeio/slime-lazyload + tag: v0.3.0_linux_amd64 + module: + - name: lazyload # custom value + kind: lazyload # should be "lazyload" + enable: true + general: # replace previous "fence" field + wormholePort: # replace to your application service ports, and extend the list in case of multi ports + - "80" + - "9080" + namespace: # replace to your service's namespace which will use lazyload, and extend the list in case of multi namespaces + - default + global: + misc: + globalSidecarMode: namespace # inform the mode of global-sidecar + metricSourceType: accesslog # indicate the metric source + component: + globalSidecar: + enable: true + sidecarInject: + enable: true # should be true + # mode definition: + # "pod": sidecar auto-inject on pod level, need provide labels for injection + # "namespace": sidecar auto-inject on namespace level, no need to provide labels for injection + # if globalSidecarMode is cluster, global-sidecar will be deployed in slime namespace, which does not enable auto-inject on namespace level, mode can only be "pod". + # if globalSidecarMode is namespace, depending on the namespace definition, mode can be "pod" or "namespace". + mode: namespace + # labels: # optional, used for sidecarInject.mode = pod + # sidecar.istio.io/inject: "true" + # istio.io/rev: canary # using control plane revisions + resources: + requests: + cpu: 200m + memory: 200Mi + limits: + cpu: 400m + memory: 400Mi + image: + repository: docker.io/slimeio/slime-global-sidecar + tag: v0.2.0-1b93bf7 diff --git a/staging/src/slime.io/slime/modules/lazyload/install/samples/lazyload/slimeboot_namespace_prometheus.yaml b/staging/src/slime.io/slime/modules/lazyload/install/samples/lazyload/slimeboot_namespace_prometheus.yaml new file mode 100644 index 00000000..3f6831eb --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/install/samples/lazyload/slimeboot_namespace_prometheus.yaml @@ -0,0 +1,56 @@ +--- +apiVersion: config.netease.com/v1alpha1 +kind: SlimeBoot +metadata: + name: lazyload + namespace: mesh-operator +spec: + image: + pullPolicy: Always + repository: docker.io/slimeio/slime-lazyload + tag: v0.3.0_linux_amd64 + module: + - name: lazyload # custom value + kind: lazyload # should be "lazyload" + enable: true + general: # replace previous "fence" field + wormholePort: # replace to your application service ports, and extend the list in case of multi ports + - "80" + - "9080" + namespace: # replace to your service's namespace which will use lazyload, and extend the list in case of multi namespaces + - default + global: + misc: + globalSidecarMode: namespace # inform the mode of global-sidecar + metric: # indicate the metric source + prometheus: + address: http://prometheus.istio-system:9090 + handlers: + destination: + query: | + sum(istio_requests_total{source_app="$source_app",reporter="destination"})by(destination_service) + type: Group + component: + globalSidecar: + enable: true + sidecarInject: + enable: true # should be true + # mode definition: + # "pod": sidecar auto-inject on pod level, need provide labels for injection + # "namespace": sidecar auto-inject on namespace level, no need to provide labels for injection + # if globalSidecarMode is cluster, global-sidecar will be deployed in slime namespace, which does not enable auto-inject on namespace level, mode can only be "pod". + # if globalSidecarMode is namespace, depending on the namespace definition, mode can be "pod" or "namespace". + mode: namespace + # labels: # optional, used for sidecarInject.mode = pod + # sidecar.istio.io/inject: "true" + # istio.io/rev: canary # using control plane revisions + resources: + requests: + cpu: 200m + memory: 200Mi + limits: + cpu: 400m + memory: 400Mi + image: + repository: docker.io/slimeio/slime-global-sidecar + tag: v0.2.0-1b93bf7 diff --git a/staging/src/slime.io/slime/modules/lazyload/lazyload_tutorials.md b/staging/src/slime.io/slime/modules/lazyload/lazyload_tutorials.md new file mode 100644 index 00000000..3100ca92 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/lazyload_tutorials.md @@ -0,0 +1,1152 @@ +- [Lazyload Turotails](#lazyload-turotails) + - [Architecture](#architecture) + - [Install-and-Use](#install-and-use) + - [Cluster Mode](#cluster-mode) + - [Accesslog](#accesslog) + - [Prometheus](#prometheus) + - [Namespace Mode](#namespace-mode) + - [Accesslog](#accesslog-1) + - [Prometheus](#prometheus-1) + - [Introduction of features](#introduction-of-features) + - [Enable lazyload based on accesslog](#enable-lazyload-based-on-accesslog) + - [Automatic ServiceFence generation based on namespace/service label](#automatic-servicefence-generation-based-on-namespaceservice-label) + - [Custom undefined traffic dispatch](#custom-undefined-traffic-dispatch) + - [Support for adding static service dependencies](#support-for-adding-static-service-dependencies) + - [Dependency on specific services](#dependency-on-specific-services) + - [Dependency on all services in specific namespaces](#dependency-on-all-services-in--specific-namespaces) + - [Dependency on all services with specific labels](#dependency-on-all-services-with-specific-labels) + - [Support for custom service dependency aliases](#Support for custom service dependency aliases) + - [Logs output to local file and rotate](#logs-output-to-local-file-and-rotate) + - [Creating Storage Volumes](#creating-storage-volumes) + - [Declaring mount information in SlimeBoot](#declaring-mount-information-in-slimeboot) + - [Example](#example) + - [Install Istio (1.8+)](#install-istio-18) + - [Set Tag](#set-tag) + - [Install Slime](#install-slime) + - [Install Bookinfo](#install-bookinfo) + - [Enable Lazyload](#enable-lazyload) + - [First Visit and Observ](#first-visit-and-observ) + - [Second Visit and Observ](#second-visit-and-observ) + - [Uninstall](#uninstall) + - [Remarks](#remarks) + - [FAQ](#faq) + - [Istio versions supported?](#istio-versions-supported) + - [Why is it necessary to specify a service port?](#why-is-it-necessary-to-specify-a-service-port) + - [Why is it necessary to specify a list of namespaces for lazyload?](#why-is-it-necessary-to-specify-a-list-of-namespaces-for-lazyload) + - [~~Meaning of global-sidecar-pilot?~~ (component obsolete)](#meaning-of-global-sidecar-pilot-component-obsolete) + - [~~global sidecar does not start properly?~~ (Solved)](#global-sidecar-does-not-start-properly-solved) + + + + +# Lazyload Turotails + +## Architecture + + + +![](./media/lazyload-architecture-20211222.png) + +* The green arrows are the internal logic of lazyload controller, and the orange arrows are the internal logic of global-sidecar. + +Instruction: + +2. Deploy the Lazyload module, and Istiod will inject the standard sidecar (envoy) for the global-sidecar application + +2. Enable lazyload for Service A + + 2.1 Create ServiceFence A + + 2.2 Create Sidecar(Istio CRD) A, and initialize accrording to the static config of ServiceFence.spec + + 2.3 ApiServer senses sidecar A creation + +3. Istiod gets the content of Sidecar A + +4. Istiod pushed new configuration to sidecar of Service A + +5. Service A sends first request to Service B. sidecar A does not has information about Service B, then request is sent to global-sidecar. + +6. Global-sidecar operations + + 6.1 Inbound traffic is intercepted, and in accesslog mode, sidecar generates an accesslog containing the service invocation relationship + + 6.2 The global-sidecar application converts the access target to Service B based on the request header and other information + + 6.3 Outbound traffic interception, where sidecar has all the service configuration information, finds the Service B target information and sends the request + +7. Request sends to Service B + +8. Global-sidecar reports relationships through access log or prometheus metric + +9. Lazyload controller gets the relationships + +10. lazyload controller updates lazyload configuration + + 10.1 Update ServiceFence A adding infomation about Service B + + 10.2 Update Sidecar A,adding egress.hosts of Service B + + 10.3 ApiServer senses Sidecar A update + +11. Istiod gets the content of Sidecar A + +12. Istiod pushed new configuration to sidecar of Service A + +13. Service A sends request to Service B directly + + + +## Install-and-Use + +To use lazyload module, add `fence` to `SlimeBoot.spec.module` and `enable: true`, and specify how the global-sidecar component is used, like follows + +```yaml +apiVersion: config.netease.com/v1alpha1 +kind: SlimeBoot +metadata: + name: lazyload + namespace: mesh-operator +spec: + module: + - name: lazyload # custom value + kind: lazyload # should be "lazyload" + enable: true + general: # replace previous "fence" field + # other config + component: + globalSidecar: + enable: true + # other config +``` + + + +Depending on how the global-sidecar is deployed and the source of the metrics on which the service depends, there are four modes. + + + +### Cluster Mode + +In this mode, all namespaces in the service mesh can use Lazyload, no need to explicitly specify a list of namespace like the namespace mode. This mode deploys a global-sidecar application, in the namespace of the lazyload controller, defaulting to mesh-operator. + +#### Accesslog + +The source of the metrics is the global-sidecar's accesslog. + +> [Full Example](./install/samples/lazyload/slimeboot_cluster_accesslog.yaml) + +```yaml +--- +apiVersion: config.netease.com/v1alpha1 +kind: SlimeBoot +metadata: + name: lazyload + namespace: mesh-operator +spec: + image: + pullPolicy: Always + repository: docker.io/slimeio/slime-lazyload + tag: {{your_lazyload_tag}} + module: + - name: lazyload # custom value + kind: lazyload # should be "lazyload" + enable: true + general: # replace previous "fence" field + wormholePort: # replace to your application service ports, and extend the list in case of multi ports + - "{{your_port}}" + global: + misc: + globalSidecarMode: cluster # inform the lazyload controller of the global-sidecar mode + metricSourceType: accesslog # infrom the metric source + component: + globalSidecar: + enable: true + type: cluster # inform the slime-boot operator of the global-sidecar mode + sidecarInject: + enable: true # must be true + mode: pod # if type = cluster, can only be "pod"; if type = namespace, can be "pod" or "namespace" + labels: # optional, used for sidecarInject.mode = pod, indicate the labels for sidecar auto inject + {{your_istio_autoinject_labels}} + resources: + requests: + cpu: 200m + memory: 200Mi + limits: + cpu: 400m + memory: 400Mi + image: + repository: docker.io/slimeio/slime-global-sidecar + tag: {{your_global-sidecar_tag}} +``` + + + +#### Prometheus + +The source of the metrics is Prometheus. + +> [Full Example](./install/samples/lazyload/slimeboot_cluster_prometheus.yaml) + +```yaml +--- +apiVersion: config.netease.com/v1alpha1 +kind: SlimeBoot +metadata: + name: lazyload + namespace: mesh-operator +spec: + image: + pullPolicy: Always + repository: docker.io/slimeio/slime-lazyload + tag: {{your_lazyload_tag}} + module: + - name: lazyload # custom value + kind: lazyload # should be "lazyload" + enable: true + general: # replace previous "fence" field + wormholePort: # replace to your application service ports, and extend the list in case of multi ports + - "{{your_port}}" + global: + misc: + globalSidecarMode: cluster # inform the lazyload controller of the global-sidecar mode + metric: # indicate the metric source + prometheus: + address: {{your_prometheus_address}} + handlers: + destination: + query: | + sum(istio_requests_total{source_app="$source_app",reporter="destination"})by(destination_service) + type: Group + component: + globalSidecar: + enable: true + type: cluster # inform the slime-boot operator of the global-sidecar mode + sidecarInject: + enable: true # must be true + mode: pod # if type = cluster, can only be "pod"; if type = namespace, can be "pod" or "namespace" + labels: # optional, used for sidecarInject.mode = pod, indicate the labels for sidecar auto inject + {{your_istio_autoinject_labels}} + resources: + requests: + cpu: 200m + memory: 200Mi + limits: + cpu: 400m + memory: 400Mi + image: + repository: docker.io/slimeio/slime-global-sidecar + tag: {{your_global-sidecar_tag}} +``` + + + +### Namespace Mode + +This pattern deploys a global-sidecar application in each namespace where lazyload is intended to be used. Underwriting requests for each namespace are sent to the global-sidecar application under the same namespace. + +#### Accesslog + +The source of the metrics is the global-sidecar's accesslog. + +> [Full Example](./install/samples/lazyload/slimeboot_namespace_accesslog.yaml) + +```yaml +--- +apiVersion: config.netease.com/v1alpha1 +kind: SlimeBoot +metadata: + name: lazyload + namespace: mesh-operator +spec: + image: + pullPolicy: Always + repository: docker.io/slimeio/slime-lazyload + tag: {{your_lazyload_tag}} + module: + - name: lazyload # custom value + kind: lazyload # should be "lazyload" + enable: true + general: # replace previous "fence" field + wormholePort: # replace to your application service ports, and extend the list in case of multi ports + - "{{your_port}}" + namespace: # replace to your service's namespace which will use lazyload, and extend the list in case of multi namespaces + - {{your_namespace}} + global: + misc: + metricSourceType: accesslog # indicate the metric source + component: + globalSidecar: + enable: true + type: namespace # inform the slime-boot operator of the global-sidecar mode + sidecarInject: + enable: true # must be true + mode: namespace # if type = cluster, can only be "pod"; if type = namespace, can be "pod" or "namespace" + #labels: # optional, used for sidecarInject.mode = pod + #sidecar.istio.io/inject: "true" + resources: + requests: + cpu: 200m + memory: 200Mi + limits: + cpu: 400m + memory: 400Mi + image: + repository: docker.io/slimeio/slime-global-sidecar + tag: {{your_global-sidecar_tag}} +``` + + + +#### Prometheus + +The source of the metrics is Prometheus. + +>[Full Example](./install/samples/lazyload/slimeboot_namespace_prometheus.yaml) + +```yaml +--- +apiVersion: config.netease.com/v1alpha1 +kind: SlimeBoot +metadata: + name: lazyload + namespace: mesh-operator +spec: + image: + pullPolicy: Always + repository: docker.io/slimeio/slime-lazyload + tag: {{your_lazyload_tag}} + module: + - name: lazyload # custom value + kind: lazyload # should be "lazyload" + enable: true + general: # replace previous "fence" field + wormholePort: # replace to your application service ports, and extend the list in case of multi ports + - "{{your_port}}" + namespace: # replace to your service's namespace which will use lazyload, and extend the list in case of multi namespaces + - {{your_namespace}} + metric: # indicate the metric source + prometheus: + address: http://prometheus.istio-system:9090 + handlers: + destination: + query: | + sum(istio_requests_total{source_app="$source_app",reporter="destination"})by(destination_service) + type: Group + component: + globalSidecar: + enable: true + type: namespace # inform the slime-boot operator of the global-sidecar mode + sidecarInject: + enable: true # must be true + mode: namespace # if type = cluster, can only be "pod"; if type = namespace, can be "pod" or "namespace" + #labels: # optional, used for sidecarInject.mode = pod + #sidecar.istio.io/inject: "true" + resources: + requests: + cpu: 200m + memory: 200Mi + limits: + cpu: 400m + memory: 400Mi + image: + repository: docker.io/slimeio/slime-global-sidecar + tag: {{your_global-sidecar_tag}} +``` + + + + + + + +## Introduction of features + +### Enable lazyload based on accesslog + +Specifying the SlimeBoot CR resource `spec.module.global.misc.metricSourceType` equal to `accesslog` will use Accesslog to get the service relationship, and equal to `prometheus` will use Prometheus. + +Approximate process of obtaining service call relationships using Accesslog: + +- When slime-boot creates global-sidecar, it finds `metricSourceType: accesslog` and generates an additional configmap with static_resources containing the address information for the lazyload controller to process accesslog. The static_resources is then added to the global-sidecar configuration by an envoyfilter, so that the global-sidecar accesslog will be sent to the lazyload controller +- The global-sidecar generates an accesslog, containing information about the caller and callee services. Global-sidecar sends the information to the lazyload controller +- The lazyload controller analyzes the accesslog and gets the new service call relationship + +The subsequent process, which involves modifying servicefence and sidecar, is the same as the process for handling the prometheus metric. + +Example + +```yaml +spec: + module: + - name: lazyload # custom value + kind: lazyload # should be "lazyload" + enable: true + general: # replace previous "fence" field + wormholePort: # replace to your application svc ports + - "9080" + global: + misc: + metricSourceType: accesslog +``` + +[Full sample](./install/samples/lazyload/slimeboot_cluster_accesslog.yaml) + + + +### Support for enabling lazyload for services manually or automatically + +Support for enabling lazyload for services, either manually or automatically, via the `autoFence` parameter. Enabling lazyload here refers to the creation of the serviceFence resource, which generates the Sidecar CR. + +Support for specifying whether lazyload is globally enabled in automatic mode via the `defaultFence` parameter. + +The configuration is as follows + +```yaml +--- +apiVersion: config.netease.com/v1alpha1 +kind: SlimeBoot +metadata: + name: lazyload + namespace: mesh-operator +spec: + module: + - name: lazyload + kind: lazyload + enable: true + general: + autoFence: true # true for automatic mode, false for manual mode, default for manual mode + defaultFence: true # Default behaviour in auto mode, true to create servicefence, false to not create, default not to create + # ... +``` + + + +#### Auto mode + +Auto mode is entered when the `autoFence` parameter is `true`. The range of services enabled for lazyload in auto mode is adjusted by three dimensions. + +Service Level - label `slime.io/serviceFenced` + +* `false`: not auto enable +* `true`: auto enable + +* other values or empty: use namespace level configuration + +Namespace Level - label `slime.io/serviceFenced` + +* `false`: not auto enable for this namespace +* `true`: auto enable for this namespace +* other values or empty: use global level configuration + +Global Level - `defaultFence` param of lazyload module + +- `false`: not auto enable for all +- `true`: auto enable for all + +Priority: Service Level > Namespace Level > Global Level + + + +Note: ServiceFence that are auto generated are labeled with `app.kubernetes.io/created-by=fence-controller`, which enables state association changes. ServiceFence that do not match this Label are considered to be manually configured and are not affected by the above Label. + + + +**Example** + +> Namespace `testns` has 3 services, `svc1`, `svc2`, `svc3` + +* When `autoFence` is `true` and `defaultFence` is `true`, three ServiceFence for the above services is auto generated +* Label ns with `slime.io/serviceFenced: "false"`, all ServiceFence disappear +* Label `svc1` with `slime.io/serviceFenced: "true"` , create ServiceFence for `svc1` +* Delete the labels on Namespace and Service: created the three ServiceFence + + + +**Sample configuration** + +```yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + istio-injection: enabled + slime.io/serviceFenced: "false" + name: testns +--- +apiVersion: v1 +kind: Service +metadata: + annotations: {} + labels: + app: svc1 + service: svc1 + slime.io/serviceFenced: "true" + name: svc1 + namespace: testns +``` + + + +#### Manual mode + +When the `autoFence` parameter is `false`, lazyload is enabled in manual mode, requiring the user to create the ServiceFence resource manually. This enablement is Service level. + + + +### Custom undefined traffic dispatch + +By default, lazyload/fence sends (default or undefined) traffic that envoy cannot match the route to the global sidecar to deal with the problem of missing service data temprorarily, which is inevitably faced by "lazy loading". This solution is limited by technical details, and cannot handle traffic whose target (e.g. domain name) is outside the cluster, see [[Configuration Lazy Loading]: Failed to access external service #3](https://github.com/slime-io/slime/issues/3). + +Based on this background, this feature was designed to be used in more flexible business scenarios as well. The general idea is to assign different default traffic to different targets for correct processing by means of domain matching. + + + +Sample configuration. + +```yaml +module: + - name: lazyload + kind: lazyload + enable: true + general: + wormholePort: + - "80" + - "8080" + dispatches: # new field + - name: 163 + domains: + - "www.163.com" + cluster: "outbound|80||egress1.testns.svc.cluster.local" # standard istio cluster format: |||, normally direction is outbound and subset is empty + - name: baidu + domains: + - "*.baidu.com" + - "baidu.*" + cluster: "{{ (print .Values.foo \ ". \" .Values.namespace ) }}" # you can use template to construct cluster dynamically + - name: sohu + domains: + - "*.sohu.com" + - "sodu.*" + cluster: "_GLOBAL_SIDECAR" # a special name which will be replaced with actual global sidecar cluster + - name: default + domains: + - "*" + cluster: "PassthroughCluster" # a special istio cluster which will passthrough the traffic according to orgDest info. It's the default behavior of native istio. +``` + +> In this example, we dispatch a portion of the traffic to the specified cluster; let another part go to the global sidecar; and then for the rest of the traffic, let it keep the native istio behavior: passthrough. + + + +**Note**: + +* In custom assignment scenarios, if you want to keep the original logic "all other undefined traffic goes to global sidecar", you need to explicitly configure the second from bottom item as above. + + + +### Support for adding static service dependencies + +In addition to updating service dependencies from the slime metric based on dynamic metrics, Lazyload also supports adding static service dependencies via `serviceFence.spec`. Three breakdown scenarios are supported: dependency on specific services, dependency on all services in specific namespaces, dependency on all services with specific labels. + +It is worth noting that static service dependencies, like dynamic service dependencies, also support the determination of the real backend of a service based on VirtualService rules and automatically extend the scope of Fence. Details at [ServiceFence Instruction](./README.md#ServiceFence-Instruction) + + + + + +#### Dependency on specific services + +For scenarios where a lazyload enabled service statically depends on one or more other services, the configuration can be added directly to the sidecar crd at initialization time. + +In the following example, a static dependency on the `reviews.default` service is added for a lazyload enabled service. + +```yaml +# servicefence +spec: + enable: true + host: + reviews.default.svc.cluster.local: # static dependenct of reviews.default service + stable: + +# related sidecar +spec: + egress: + - hosts: + - '*/reviews.default.svc.cluster.local' + - istio-system/* + - mesh-operator/* +``` + + + +#### Dependency on all services in specific namespaces + +For scenarios where a lazyload enabled service statically depends on all services in one or more other namespaces, the configuration can be added directly to the sidecar crd at initialization time. + +In the following example, a static dependency on all services in the `test` namespace is added for a lazyload enabled service. + +```yaml +# servicefence +spec: + enable: true + host: + test/*: {} # static dependency of all services in test namespace + +# related sidecar +spec: + egress: + - hosts: + - test/* + - istio-system/* + - mesh-operator/* +``` + + + +#### Dependency on all services with specific labels + +For scenarios where a lazyload enabled service has static dependencies on all services with a label or multiple labels, the configuration can be added directly to the sidecar crd at initialization time. + +In the example below, static dependencies are added for all services with `app=details` and for all services with `app=reviews, group=default` for the lazyloading enabled service. + +```yaml +# servicefence +spec: + enable: true + labelSelector: # Match service label, multiple selectors are 'or' relationship + - selector: + app: details + - selector: # labels in one selector are 'and' relationship + app: reviews + group: default + +# related sidecar +spec: + egress: + - hosts: + - '*/details.default.svc.cluster.local' # with label "app=details" + - '*/details.test.svc.cluster.local' # with label "app=details" + - '*/reviews.default.svc.cluster.local' # with label "app=details" and "group=default" + - istio-system/* + - mesh-operator/* +``` + + + + + +### Support for custom service dependency aliases + +In some scenarios, we want Lazyload to add some additional dependent services in based on the known dependent service. + +Users can configure `general.domainAliases` to provide custom conversion relationships to achieve the requirements. `general.domainAliases` consists of one or many `domainAlias`. `domainAlias` consists of a matching rule `pattern` and a transformation rule `templates`. `pattern` contains only one matching rule, while `templates` can contain multiple conversion rules. + +For example, we want to add `. .svc.cluster.local` with an additional `. .mailsaas` service dependency, you can configure it like this + +```yaml +apiVersion: config.netease.com/v1alpha1 +kind: SlimeBoot +metadata: + name: lazyload + namespace: mesh-operator +spec: + module: + - name: lazyload-test + kind: lazyload + enable: true + general: + wormholePort: # replace to your application service ports, and extend the list in case of multi ports + - "9080" + domainAliases: + - pattern: '(?P[^\.]+)\.(?P[^\.]+)\.svc\.cluster\.local$' + templates: + - "$namespace.$service.mailsaas" + #... +``` + +Servicefence will look like this + +```yaml +apiVersion: microservice.slime.io/v1alpha1 +kind: ServiceFence +metadata: + name: ratings + namespace: default +spec: + enable: true + host: + details.default.svc.cluster.local: # static dependent service + stable: {} +status: + domains: + default.details.mailsaas: # static dependent service converted result + hosts: + - default.details.mailsaas + default.productpage.mailsaas: # dynamic dependent service converted result + hosts: + - default.productpage.mailsaas + details.default.svc.cluster.local: + hosts: + - details.default.svc.cluster.local + productpage.default.svc.cluster.local: + hosts: + - productpage.default.svc.cluster.local + metricStatus: + '{destination_service="productpage.default.svc.cluster.local"}': "1" # dynamic dependent service +``` + +Sidecar will look like this + +```yaml +apiVersion: networking.istio.io/v1beta1 +kind: Sidecar +metadata: + name: ratings + namespace: default +spec: + egress: + - hosts: + - '*/default.details.mailsaas' # static dependent service converted result + - '*/default.productpage.mailsaas' # dynamic dependent service converted result + - '*/details.default.svc.cluster.local' + - '*/productpage.default.svc.cluster.local' + - istio-system/* + - mesh-operator/* + workloadSelector: + labels: + app: ratings +``` + + + + + +### Logs output to local file and rotate + +slime logs are output to stdout by default, specifying `spec.module.global.log.logRotate` equal to `true` in the SlimeBoot CR resource will output the logs locally and start the log rotation, no longer to standard output. + +The rotation configuration is also adjustable, and the default configuration is as follows, which can be overridden by displaying the individual values in the specified logRotateConfig. + +```yaml +spec: + module: + - name: lazyload + enable: true + fence: + wormholePort: # replace to your application svc ports + - "9080" + global: + log: + logRotate: true + logRotateConfig: + filePath: "/tmp/log/slime.log" + maxSizeMB: 100 + maxBackups: 10 + maxAgeDay: 10 + compress: true +``` + +It is usually required to be used with storage volumes. After the storage volume is prepared, specify `spec.volumes` and `spec.containers.slime.volumeMounts` in the SlimeBoot CR resource to show the path where the storage volume will be mounted to the log local file. + +Here is the full demo based on the minikube kubernetes scenario. + +#### Creating Storage Volumes + +Create a hostpath type storage volume based on the /mnt/data + +```yaml +# hostPath pv for minikube demo +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: lazyload-claim + namespace: mesh-operator +spec: + storageClassName: manual + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 3Gi +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: lazyload-volumn + labels: + type: local +spec: + storageClassName: manual + capacity: + storage: 5Gi + accessModes: + - ReadWriteOnce + hostPath: + path: "/mnt/data" +``` + +#### Declaring mount information in SlimeBoot + +Specify in the SlimeBoot CR resource that the storage volume will be mounted to the "/tmp/log" path of the pod, so that the slime logs will be persisted to the /mnt/data path and will be automatically rotated. + +```yaml +--- +apiVersion: config.netease.com/v1alpha1 +kind: SlimeBoot +metadata: + name: lazyload + namespace: mesh-operator +spec: + image: + pullPolicy: Always + repository: docker.io/slimeio/slime-lazyload + tag: master-e5f2d83-dirty_1b68486 + module: + - name: lazyload # custom value + kind: lazyload # should be "lazyload" + enable: true + general: # replace previous "fence" field + wormholePort: # replace to your application svc ports + - "9080" + global: + log: + logRotate: true + logRotateConfig: + filePath: "/tmp/log/slime.log" + maxSizeMB: 100 + maxBackups: 10 + maxAgeDay: 10 + compress: true +#... + volumes: + - name: lazyload-storage + persistentVolumeClaim: + claimName: lazyload-claim + containers: + slime: + volumeMounts: + - mountPath: "/tmp/log" + name: lazyload-storage +``` + +[Full Example](./install/samples/lazyload/slimeboot_logrotate.yaml) + + + +## Example + +### Install Istio (1.8+) + + + +### Set Tag + +$latest_tag equals the latest tag. The shell scripts and yaml files uses this version as default. + +```sh +$ export latest_tag=$(curl -s https://api.github.com/repos/slime-io/lazyload/tags | grep 'name' | cut -d\" -f4 | head -1) +``` + + + +### Install Slime + +```sh +$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/slime-io/lazyload/$latest_tag/install/samples/lazyload/easy_install_lazyload.sh)" +``` + +Confirm all components are running. + +```sh +$ kubectl get slimeboot -n mesh-operator +NAME AGE +lazyload 12s +$ kubectl get pod -n mesh-operator +NAME READY STATUS RESTARTS AGE +global-sidecar-7dd48b65c8-gc7g4 2/2 Running 0 18s +lazyload-85987bbd4b-djshs 1/1 Running 0 18s +slime-boot-6f778b75cd-4v675 1/1 Running 0 26s +``` + + + +### Install Bookinfo + +Change the namespace of current-context to which bookinfo will deploy first. Here we use default namespace. + +```sh +$ kubectl label namespace default istio-injection=enabled +$ kubectl apply -f "https://raw.githubusercontent.com/slime-io/lazyload/$latest_tag/install/config/bookinfo.yaml" +``` + +Confirm all pods are running. + +```sh +$ kubectl get po -n default +NAME READY STATUS RESTARTS AGE +details-v1-79f774bdb9-6vzj6 2/2 Running 0 60s +productpage-v1-6b746f74dc-vkfr7 2/2 Running 0 59s +ratings-v1-b6994bb9-klg48 2/2 Running 0 59s +reviews-v1-545db77b95-z5ql9 2/2 Running 0 59s +reviews-v2-7bf8c9648f-xcvd6 2/2 Running 0 60s +reviews-v3-84779c7bbc-gb52x 2/2 Running 0 60s +``` + +Then we can visit productpage from pod/ratings, executing `curl productpage:9080/productpage`. + +You can also create gateway and visit productpage from outside, like what shows in [Open the application to outside traffic](https://istio.io/latest/docs/setup/getting-started/#ip). + + + +### Enable Lazyload + +Create ServiceFence for productpage. Two ways: + +- Create ServiceFence manually + +```sh +$ kubectl apply -f "https://raw.githubusercontent.com/slime-io/lazyload/$latest_tag/install/samples/lazyload/servicefence_productpage.yaml" +``` + +- Update service to automatically create ServiceFence + +```sh +$ kubectl label service productpage -n default slime.io/serviceFenced=true +``` + + + +Confirm servicefence and sidecar already exist. + +```sh +$ kubectl get servicefence -n default +NAME AGE +productpage 12s +$ kubectl get sidecar -n default +NAME AGE +productpage 22s +$ kubectl get servicefence productpage -n default -oyaml +apiVersion: microservice.slime.io/v1alpha1 +kind: ServiceFence +metadata: + creationTimestamp: "2021-12-23T06:21:14Z" + generation: 1 + labels: + app.kubernetes.io/created-by: fence-controller + name: productpage + namespace: default + resourceVersion: "10662886" + uid: f21e7d2b-4ab3-4de0-9b3d-131b6143d9db +spec: + enable: true +status: {} +$ kubectl get sidecar productpage -n default -oyaml +apiVersion: networking.istio.io/v1beta1 +kind: Sidecar +metadata: + creationTimestamp: "2021-12-23T06:21:14Z" + generation: 1 + name: productpage + namespace: default + ownerReferences: + - apiVersion: microservice.slime.io/v1alpha1 + blockOwnerDeletion: true + controller: true + kind: ServiceFence + name: productpage + uid: f21e7d2b-4ab3-4de0-9b3d-131b6143d9db + resourceVersion: "10662887" + uid: 85f9dc11-6d83-4b84-8d1b-14bd031cc57b +spec: + egress: + - hosts: + - istio-system/* + - mesh-operator/* + workloadSelector: + labels: + app: productpage +``` + + + +### First Visit and Observ + +Visit the productpage website, and use `kubectl logs -f productpage-xxx -c istio-proxy -n default` to observe the access log of productpage. + +``` +[2021-12-23T06:24:55.527Z] "GET /details/0 HTTP/1.1" 200 - via_upstream - "-" 0 178 12 12 "-" "curl/7.52.1" "7ec25152-ca8e-923b-a736-49838ce316f4" "details:9080" "172.17.0.10:80" outbound|9080||global-sidecar.mesh-operator.svc.cluster.local 172.17.0.11:45194 10.102.66.227:9080 172.17.0.11:40210 - - + +[2021-12-23T06:24:55.552Z] "GET /reviews/0 HTTP/1.1" 200 - via_upstream - "-" 0 295 30 29 "-" "curl/7.52.1" "7ec25152-ca8e-923b-a736-49838ce316f4" "reviews:9080" "172.17.0.10:80" outbound|9080||global-sidecar.mesh-operator.svc.cluster.local 172.17.0.11:45202 10.104.97.115:9080 172.17.0.11:40880 - - + +[2021-12-23T06:24:55.490Z] "GET /productpage HTTP/1.1" 200 - via_upstream - "-" 0 4183 93 93 "-" "curl/7.52.1" "7ec25152-ca8e-923b-a736-49838ce316f4" "productpage:9080" "172.17.0.11:9080" inbound|9080|| 127.0.0.6:48621 172.17.0.11:9080 172.17.0.7:41458 outbound_.9080_._.productpage.default.svc.cluster.local default +``` + +It is clearly that the banckend of productpage is global-sidecar.mesh-operator.svc.cluster.local. + + + +Observe servicefence productpage. + +```sh +$ kubectl get servicefence productpage -n default -oyaml +apiVersion: microservice.slime.io/v1alpha1 +kind: ServiceFence +metadata: + creationTimestamp: "2021-12-23T06:21:14Z" + generation: 1 + labels: + app.kubernetes.io/created-by: fence-controller + name: productpage + namespace: default + resourceVersion: "10663136" + uid: f21e7d2b-4ab3-4de0-9b3d-131b6143d9db +spec: + enable: true +status: + domains: + details.default.svc.cluster.local: + hosts: + - details.default.svc.cluster.local + reviews.default.svc.cluster.local: + hosts: + - reviews.default.svc.cluster.local + metricStatus: + '{destination_service="details.default.svc.cluster.local"}': "1" + '{destination_service="reviews.default.svc.cluster.local"}': "1" +``` + + + +Observe sidecar productpage. + +```YAML +$ kubectl get sidecar productpage -n default -oyaml +apiVersion: networking.istio.io/v1beta1 +kind: Sidecar +metadata: + creationTimestamp: "2021-12-23T06:21:14Z" + generation: 2 + name: productpage + namespace: default + ownerReferences: + - apiVersion: microservice.slime.io/v1alpha1 + blockOwnerDeletion: true + controller: true + kind: ServiceFence + name: productpage + uid: f21e7d2b-4ab3-4de0-9b3d-131b6143d9db + resourceVersion: "10663141" + uid: 85f9dc11-6d83-4b84-8d1b-14bd031cc57b +spec: + egress: + - hosts: + - '*/details.default.svc.cluster.local' + - '*/reviews.default.svc.cluster.local' + - istio-system/* + - mesh-operator/* + workloadSelector: + labels: + app: productpage +``` + +Details and reviews are already added into sidecar! + + + +### Second Visit and Observ + +Visit the productpage website again, and use `kubectl logs -f productpage-xxx -c istio-proxy -n default` to observe the access log of productpage. + +``` +[2021-12-23T06:26:47.700Z] "GET /details/0 HTTP/1.1" 200 - via_upstream - "-" 0 178 13 12 "-" "curl/7.52.1" "899e918c-e44c-9dc2-9629-d8db191af972" "details:9080" "172.17.0.13:9080" outbound|9080||details.default.svc.cluster.local 172.17.0.11:50020 10.102.66.227:9080 172.17.0.11:42180 - default + +[2021-12-23T06:26:47.718Z] "GET /reviews/0 HTTP/1.1" 200 - via_upstream - "-" 0 375 78 77 "-" "curl/7.52.1" "899e918c-e44c-9dc2-9629-d8db191af972" "reviews:9080" "172.17.0.16:9080" outbound|9080||reviews.default.svc.cluster.local 172.17.0.11:58986 10.104.97.115:9080 172.17.0.11:42846 - default + +[2021-12-23T06:26:47.690Z] "GET /productpage HTTP/1.1" 200 - via_upstream - "-" 0 5179 122 121 "-" "curl/7.52.1" "899e918c-e44c-9dc2-9629-d8db191af972" "productpage:9080" "172.17.0.11:9080" inbound|9080|| 127.0.0.6:51799 172.17.0.11:9080 172.17.0.7:41458 outbound_.9080_._.productpage.default.svc.cluster.local default +``` + +The backends are details and reviews now. + + + +### Uninstall + +Uninstall bookinfo. + +```sh +$ kubectl delete -f "https://raw.githubusercontent.com/slime-io/lazyload/$latest_tag/install/config/bookinfo.yaml" +``` + +Uninstall slime. + +```sh +$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/slime-io/lazyload/$latest_tag/install/samples/lazyload/easy_uninstall_lazyload.sh)" +``` + + + +### Remarks + +If you want to use customize shell scripts or yaml files, please set $custom_tag_or_commit. + +```sh +$ export custom_tag_or_commit=xxx +``` + +If command includes a yaml file, please use $custom_tag_or_commit instead of $latest_tag. + +```sh +#$ kubectl apply -f "https://raw.githubusercontent.com/slime-io/lazyload/$latest_tag/install/config/bookinfo.yaml" +$ kubectl apply -f "https://raw.githubusercontent.com/slime-io/lazyload/$custom_tag_or_commit/install/config/bookinfo.yaml" +``` + +If command includes a shell script, please add $custom_tag_or_commit as a parameter to the shell script. + +```sh +#$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/slime-io/lazyload/$latest_tag/install/samples/smartlimiter/easy_install_limiter.sh)" +$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/slime-io/lazyload/$latest_tag/install/samples/smartlimiter/easy_install_limiter.sh)" $custom_tag_or_commit +``` + + + + + +## FAQ + +### Istio versions supported? + +Istio 1.8 onwards is supported, see [A note on the incompatibility of lazyload with some versions of istio](https://github.com/slime-io/slime/issues/145) for a detailed compatibility note. + + + +### Why is it necessary to specify a service port? (Deprecated soon) + +No service port information is specified, as usually when lazyload is enabled, the default content of the sidecar only contains service information for istio-system and mesh-operator, and some specific ports usually have no services exposed. Due to Istio's mechanism, only listener's that are meaninglessly routed in the pocket will be removed, and there is no guarantee that a placeholder listener will exist. + +For example, if the service in bookinfo is exposed on port `9080`, and lazyload is enabled for the productpage service, the productpage `9080` listener will be removed. When you access `details:9080` after that, without the listener, you will go directly to the Passthrough logic and will not be able to go to the global-sidecar, and you will not be able to fetch the service dependencies. + + + +### Why is it necessary to specify a list of namespaces for lazyload? (Deprecated soon) + +Due to the large number of short domain access scenarios, different namespace information needs to be replenished in different namespaces. So lazyload will create separate envoyfilters under these specified namespaces, supplemented with the appropriate namespace information. + + + +### ~~Meaning of global-sidecar-pilot?~~ (component obsolete) + +~~Because the role of global-sidecar is different from normal sidecar, it needs some custom logic, such as the bottom envoyfilter does not take effect for global-sidecar or it will dead-loop, etc. global-sidecar does not directly use the full configuration of the original pilot of the cluster. The global-sidecar-pilot will get the full configuration from the original pilot of the cluster, then fine-tune it and push it to the global-sidecar. the existing global-sidecar-pilot is based on istiod 1.7.~~ + +~~Note: In order to reduce learning costs and enhance compatibility, we are considering removing the global-sidecar-pilot, when there will no longer be a customized pilot, fully compatible with the community version, and expect to implement this feature in the next major release.~~ + + + +### ~~global sidecar does not start properly?~~ (Solved) + +~~Global sidecar starts with an error `Internal:Error adding/updating listener(s) 0.0.0.0_15021: cannot bind '0.0.0.0:15021': Address already in use`.~~ + +~~This error is usually caused by port conflict. The global-sidecar is a sidecar running in gateway mode, which binds to the real port. Specifically, istio-ingressgateway is using port 15021, which will cause the lds update of global-sidecar to fail, and can be solved by changing port 15021 of ingressgateway to another value.~~ + +~~Note: This problem is currently solved by port planning, and will be freed from this limitation in the next major release.~~ + diff --git a/staging/src/slime.io/slime/modules/lazyload/lazyload_tutorials_zh.md b/staging/src/slime.io/slime/modules/lazyload/lazyload_tutorials_zh.md new file mode 100644 index 00000000..808d3fa3 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/lazyload_tutorials_zh.md @@ -0,0 +1,1155 @@ +- [懒加载教程](#懒加载教程) + - [架构](#架构) + - [安装和使用](#安装和使用) + - [Cluster模式](#cluster模式) + - [Accesslog](#accesslog) + - [Prometheus](#prometheus) + - [Namespace模式](#namespace模式) + - [accesslog metric](#accesslog-metric) + - [prometheus metric](#prometheus-metric) + - [特性介绍](#特性介绍) + - [基于Accesslog开启懒加载](#基于accesslog开启懒加载) + - [基于namespace/service label自动生成ServiceFence](#基于namespaceservice-label自动生成servicefence) + - [自定义兜底流量分派](#自定义兜底流量分派) + - [静态服务依赖关系添加](#静态服务依赖关系添加) + - [依赖某个服务](#依赖某个服务) + - [依赖某个namespace所有服务](#依赖某个namespace所有服务) + - [依赖具有某个label的所有服务](#依赖具有某个label的所有服务) + - [支持自定义服务依赖别名](#支持自定义服务依赖别名) + - [日志输出到本地并轮转](#日志输出到本地并轮转) + - [创建存储卷](#创建存储卷) + - [在SlimeBoot中声明挂载信息](#在slimeboot中声明挂载信息) + - [示例](#示例) + - [安装 istio (1.8+)](#安装-istio-18) + - [设定tag](#设定tag) + - [安装 slime](#安装-slime) + - [安装bookinfo](#安装bookinfo) + - [开启懒加载](#开启懒加载) + - [首次访问观察](#首次访问观察) + - [再次访问观察](#再次访问观察) + - [卸载](#卸载) + - [补充说明](#补充说明) + - [FAQ](#faq) + - [支持的Istio版本?](#支持的istio版本) + - [为什么必须指定服务端口?](#为什么必须指定服务端口) + - [为什么必须指定使用懒加载的namespace列表?](#为什么必须指定使用懒加载的namespace列表) + - [~~global-sidecar-pilot的意义?~~(组件已废弃)](#global-sidecar-pilot的意义组件已废弃) + - [~~global sidecar不能正常启动?~~(已解决)](#global-sidecar不能正常启动已解决) + + + + + +# 懒加载教程 + +## 架构 + + + +![](./media/lazyload-architecture-20211222_zh.png) + +注:绿色箭头为lazyload controller内部逻辑,黄色箭头为global-sidecar内部逻辑。 + +过程说明: + +1. 部署Lazyload模块,自动创建global-sidecar应用,Istiod会为global-sidecar应用添加标准sidecar(envoy) + +3. 为服务A启用懒加载 + + 3.1 创建ServiceFence A + + 3.2 创建Sidecar(Istio CRD)A,根据静态配置(ServiceFence.spec)初始化 + + 3.3 ApiServer感知到Sidecar A创建 + +4. Istiod从ApiServer获取Sidecar A的内容 + +5. Istiod下发Sidecar A限定范围内的配置给Service A的sidecar + +6. Service A发起访问Service B,由于Service A没有Service B的配置信息,请求发到global-sidecar的sidecar + +6. global-sidecar处理 + + 6.1 入流量拦截,如果是accesslog模式,sidecar会生成包含服务调用关系的accesslog + + 6.2 global-sidecar应用根据请求头等信息,转换访问目标为Service B + + 6.3 出流量拦截,sidecar拥有所有服务配置信息,找到Service B目标信息,发出请求 + +7. 请求正确到达Service B + +8. global-sidecar通过accesslog方式上报调用关系Service A->Service B,如果是prometheus模式,则由应用方的sidecar生成、上报metric + +9. lazyload controller获取到调用关系 + +10. lazyload更新懒加载配置 + + 10.1 更新ServiceFence A,添加关于B的metric + + 10.2 更新Sidecar A,egress.hosts添加B信息 + + 10.3 ApiServer 感知到Sidecar A更新 + +11. Istiod从ApiServer获取Sidecar A新内容 + +12. Istiod下发Sidecar A限定范围内的配置给Service A的sidecar,新增了B的xDS内容 + +13. 后续调用,Service A直接访问Service B成功 + + + +## 安装和使用 + +请先按照安装slime-boot小节的指引安装slime-boot。使用懒加载功能需要在`SlimeBoot.spec.module`中添加`fence`,并且`enable: true`,同时指定global-sidecar组件的使用方式。如下 + +```yaml +apiVersion: config.netease.com/v1alpha1 +kind: SlimeBoot +metadata: + name: lazyload + namespace: mesh-operator +spec: + module: + - name: lazyload + kind: lazyload + enable: true + general: + # other config + component: + globalSidecar: + enable: true + # other config +``` + + + +根据global-sidecar部署方式与服务依赖的指标的来源不同,可分为四种模式。 + +### Cluster模式 + +在该模式下,服务网格中的所有namespace都可以使用懒加载,无需像Namespace模式一样,显式指定使用懒加载的命令空间列表。 + +该模式会部署一个global-sidecar应用,位于lazyload controller的namespace下,默认为mesh-operator。所有兜底请求都会发到这个global-sidecar应用。 + +#### Accesslog + +指标来源为global-sidecar的accesslog。 + +> [完整样例](./install/samples/lazyload/slimeboot_cluster_accesslog.yaml) + +```yaml +--- +apiVersion: config.netease.com/v1alpha1 +kind: SlimeBoot +metadata: + name: lazyload + namespace: mesh-operator +spec: + image: + pullPolicy: Always + repository: docker.io/slimeio/slime-lazyload + tag: {{your_lazyload_tag}} + module: + - name: lazyload # custom value + kind: lazyload # should be "lazyload" + enable: true + general: # replace previous "fence" field + wormholePort: # replace to your application service ports, and extend the list in case of multi ports + - "{{your_port}}" + global: + misc: + globalSidecarMode: cluster # inform the lazyload controller of the global-sidecar mode + metricSourceType: accesslog # infrom the metric source + component: + globalSidecar: + enable: true + type: cluster # inform the slime-boot operator of the global-sidecar mode + sidecarInject: + enable: true # must be true + mode: pod # if type = cluster, can only be "pod"; if type = namespace, can be "pod" or "namespace" + labels: # optional, used for sidecarInject.mode = pod, indicate the labels for sidecar auto inject + {{your_istio_autoinject_labels}} + resources: + requests: + cpu: 200m + memory: 200Mi + limits: + cpu: 400m + memory: 400Mi + image: + repository: docker.io/slimeio/slime-global-sidecar + tag: {{your_global-sidecar_tag}} +``` + + + +#### Prometheus + +指标来源为Prometheus。 + +> [完整样例](./install/samples/lazyload/slimeboot_cluster_prometheus.yaml) + +```yaml +--- +apiVersion: config.netease.com/v1alpha1 +kind: SlimeBoot +metadata: + name: lazyload + namespace: mesh-operator +spec: + image: + pullPolicy: Always + repository: docker.io/slimeio/slime-lazyload + tag: {{your_lazyload_tag}} + module: + - name: lazyload # custom value + kind: lazyload # should be "lazyload" + enable: true + general: # replace previous "fence" field + wormholePort: # replace to your application service ports, and extend the list in case of multi ports + - "{{your_port}}" + global: + misc: + globalSidecarMode: cluster # inform the lazyload controller of the global-sidecar mode + metric: # indicate the metric source + prometheus: + address: {{your_prometheus_address}} + handlers: + destination: + query: | + sum(istio_requests_total{source_app="$source_app",reporter="destination"})by(destination_service) + type: Group + component: + globalSidecar: + enable: true + type: cluster # inform the slime-boot operator of the global-sidecar mode + sidecarInject: + enable: true # must be true + mode: pod # if type = cluster, can only be "pod"; if type = namespace, can be "pod" or "namespace" + labels: # optional, used for sidecarInject.mode = pod, indicate the labels for sidecar auto inject + {{your_istio_autoinject_labels}} + resources: + requests: + cpu: 200m + memory: 200Mi + limits: + cpu: 400m + memory: 400Mi + image: + repository: docker.io/slimeio/slime-global-sidecar + tag: {{your_global-sidecar_tag}} +``` + + + +### Namespace模式 + +该模式需要显式指定使用懒加载的命名空间列表,位置是SlimeBoot的`spec.module.general.namespace`。该模式会在每个打算启用懒加载的namespace下部署一个global-sidecar应用。每个namespace的兜底请求都会发到同namespace下的global-sidecar应用。 + +#### accesslog metric + +指标来源为global-sidecar的accesslog。 + +> [完整样例](./install/samples/lazyload/slimeboot_namespace_accesslog.yaml) + +```yaml +--- +apiVersion: config.netease.com/v1alpha1 +kind: SlimeBoot +metadata: + name: lazyload + namespace: mesh-operator +spec: + image: + pullPolicy: Always + repository: docker.io/slimeio/slime-lazyload + tag: {{your_lazyload_tag}} + module: + - name: lazyload # custom value + kind: lazyload # should be "lazyload" + enable: true + general: # replace previous "fence" field + wormholePort: # replace to your application service ports, and extend the list in case of multi ports + - "{{your_port}}" + namespace: # replace to your service's namespace which will use lazyload, and extend the list in case of multi namespaces + - {{your_namespace}} + global: + misc: + metricSourceType: accesslog # indicate the metric source + component: + globalSidecar: + enable: true + type: namespace # inform the slime-boot operator of the global-sidecar mode + sidecarInject: + enable: true # must be true + mode: namespace # if type = cluster, can only be "pod"; if type = namespace, can be "pod" or "namespace" + #labels: # optional, used for sidecarInject.mode = pod + #sidecar.istio.io/inject: "true" + resources: + requests: + cpu: 200m + memory: 200Mi + limits: + cpu: 400m + memory: 400Mi + image: + repository: docker.io/slimeio/slime-global-sidecar + tag: {{your_global-sidecar_tag}} +``` + + + +#### prometheus metric + +指标来源为Prometheus。 + +>[完整样例](./install/samples/lazyload/slimeboot_namespace_prometheus.yaml) + +```yaml +--- +apiVersion: config.netease.com/v1alpha1 +kind: SlimeBoot +metadata: + name: lazyload + namespace: mesh-operator +spec: + image: + pullPolicy: Always + repository: docker.io/slimeio/slime-lazyload + tag: {{your_lazyload_tag}} + module: + - name: lazyload # custom value + kind: lazyload # should be "lazyload" + enable: true + general: # replace previous "fence" field + wormholePort: # replace to your application service ports, and extend the list in case of multi ports + - "{{your_port}}" + namespace: # replace to your service's namespace which will use lazyload, and extend the list in case of multi namespaces + - {{your_namespace}} + metric: # indicate the metric source + prometheus: + address: http://prometheus.istio-system:9090 + handlers: + destination: + query: | + sum(istio_requests_total{source_app="$source_app",reporter="destination"})by(destination_service) + type: Group + component: + globalSidecar: + enable: true + type: namespace # inform the slime-boot operator of the global-sidecar mode + sidecarInject: + enable: true # must be true + mode: namespace # if type = cluster, can only be "pod"; if type = namespace, can be "pod" or "namespace" + #labels: # optional, used for sidecarInject.mode = pod + #sidecar.istio.io/inject: "true" + resources: + requests: + cpu: 200m + memory: 200Mi + limits: + cpu: 400m + memory: 400Mi + image: + repository: docker.io/slimeio/slime-global-sidecar + tag: {{your_global-sidecar_tag}} +``` + + + + + +## 特性介绍 + +### 基于Accesslog开启懒加载 + +指定SlimeBoot CR资源中`spec.module.global.misc.metricSourceType`等于`accesslog`会使用Accesslog获取服务调用关系,等于`prometheus`则使用Prometheus。 + +使用Accesslog获取服务调用关系的大概过程: + +- slime-boot在创建global-sidecar时,发现`metricSourceType: accesslog`,额外生成一个configmap,内容是包含lazyload controller处理accesslog的地址信息的static_resources。再通过一个envoyfilter,将static_resources加入global-sidecar配置中,使得global-sidecar的accesslog会发送到lazyload controller +- global-sidecar完成兜底转发时会生成accesslog,包含了调用方和被调用方服务信息。global-sidecar将信息发送给lazyload controller +- lazyload controller分析accesslog,获取到新的服务调用关系 + +随后的过程,就是修改servicefence和sidecar,和处理prometheus metric的过程一致。 + +样例 + +```yaml +spec: + module: + - name: lazyload # custom value + kind: lazyload # should be "lazyload" + enable: true + general: # replace previous "fence" field + wormholePort: # replace to your application svc ports + - "9080" + global: + misc: + metricSourceType: accesslog +``` + +[完整样例](./install/samples/lazyload/slimeboot_cluster_accesslog.yaml) + + + +### 支持为服务手动或自动启用懒加载 + +支持通过`autoFence`参数,指定启用懒加载是手动模式、自动模式。这里的启用懒加载,指的是创建serviceFence资源,从而生成Sidecar CR。 + +支持通过`defaultFence`参数,指定自动模式下,是否全局启用懒加载。 + +配置方式如下 + +```yaml +--- +apiVersion: config.netease.com/v1alpha1 +kind: SlimeBoot +metadata: + name: lazyload + namespace: mesh-operator +spec: + module: + - name: lazyload + kind: lazyload + enable: true + general: + autoFence: true # true为自动模式,false为手动模式,默认为手动模式 + defaultFence: true # 自动模式下默认行为,true为创建servicefence,false为不创建,默认不创建 + # ... +``` + + + +#### 自动模式 + +当`autoFence`参数为`true`时,进入自动模式。自动模式下,启用懒加载的服务范围,通过三个维度调整。 + +Service级别 - label `slime.io/serviceFenced` + +* `false`: 不自动启用 +* `true`: 自动启用 + +* 其他值或缺省: 使用Namespace级别配置 + +Namespace级别 - label `slime.io/serviceFenced` + +* `false`: 该namespace下的所有服务都不自动启用 +* `true`:该namespace下的所有服务都自动启用 +* 其他值或缺省: 使用全局级别配置 + +全局级别 - lazyload的`defaultFence`参数 + +- `false`: 全局不自动启用 +- `true`:全局自动启用 + +优先级:Service级别 > Namespace级别 > 全局级别 + + + +注:对于自动生成的ServiceFence资源 ,会通过标准Label `app.kubernetes.io/created-by=fence-controller`来记录,实现了状态关联变更。 而不匹配该Label的ServiceFence资源,视为手动配置,不受以上Label影响。 + + + +**举例** + +> Namespace `testns`下有三个服务: `svc1`, `svc2`, `svc3` + +* 当`autoFence`为`true`且`defaultFence`为`true`时,自动生成以上三个服务的ServiceFence +* 给Namespace testns加上Label `slime.io/serviceFenced: "false"`, 所有ServiceFence消失 +* 给`svc1`打上 `slime.io/serviceFenced: "true"` label: 服务`svc1`的ServiceFence创建 +* 删掉Namespace和Service上的label:恢复三个ServiceFence + + + +**配置样例** + +```yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + istio-injection: enabled + slime.io/serviceFenced: "false" + name: testns +--- +apiVersion: v1 +kind: Service +metadata: + annotations: {} + labels: + app: svc1 + service: svc1 + slime.io/serviceFenced: "true" + name: svc1 + namespace: testns +``` + + + +#### 手动模式 + +当`autoFence`参数为`false`时,启用懒加载为手动模式,需要用户手动创建ServiceFence资源。这种启用是Service级别的。 + + + + + +### 自定义兜底流量分派 + +lazyload/fence默认会将envoy无法匹配路由(缺省)的流量兜底发送到global sidecar,应对短暂服务数据缺失的问题,这是“懒加载”所必然面对的。 该方案因为技术细节上的局限性,对于目标(如域名)是集群外的流量,无法正常处理,详见 [[Configuration Lazy Loading]: Failed to access external service #3](https://github.com/slime-io/slime/issues/3)。 + +基于这个背景,设计了本特性,同时也能用于更灵活的业务场景。 大致思路是通过域名匹配的方式将不同的缺省流量分派到不同的目标做正确处理。 + + + +配置样例: + +```yaml +module: + - name: lazyload + kind: lazyload + enable: true + general: + wormholePort: + - "80" + - "8080" + dispatches: # new field + - name: 163 + domains: + - "www.163.com" + cluster: "outbound|80||egress1.testns.svc.cluster.local" # standard istio cluster format: |||, normally direction is outbound and subset is empty + - name: baidu + domains: + - "*.baidu.com" + - "baidu.*" + cluster: "{{ (print .Values.foo \".\" .Values.namespace ) }}" # you can use template to construct cluster dynamically + - name: sohu + domains: + - "*.sohu.com" + - "sodu.*" + cluster: "_GLOBAL_SIDECAR" # a special name which will be replaced with actual global sidecar cluster + - name: default + domains: + - "*" + cluster: "PassthroughCluster" # a special istio cluster which will passthrough the traffic according to orgDest info. It's the default behavior of native istio. +``` + +> 在本例中,我们把一部分流量分派给了指定的cluster; 另一部分让它走global sidecar; 然后对其余的流量,让它保持原生istio的行为: passthrough + + + +**注意**: + +* 自定义分派场景,如果希望保持原有逻辑 “其他所有未定义流量走global sidecar” 的话,需要显式配置如上的倒数第二条 + + + + + +### 静态服务依赖关系添加 + +懒加载除了从slime metric处根据动态指标更新服务依赖关系,还支持通过`serviceFence.spec`添加静态服务依赖关系。支持三种细分场景:依赖某个服务、依赖某个namespace所有服务、依赖具有某个label的所有服务。 + +值得注意的是,静态服务依赖关系与动态服务依赖关系一样,支持根据VirtualService规则判断服务的真实后端,并自动扩大Fence的范围。详见[ServiceFence说明](./README_zh.md#ServiceFence%E8%AF%B4%E6%98%8E) + + + + + +#### 依赖某个服务 + +适用于启用懒加载的服务静态依赖另外一个或多个服务的场景,可以在初始化时直接将配置加入sidecar crd。 + +下面的样例中,为启用懒加载的服务添加对`reviews.default`服务的静态依赖关系。 + +```yaml +# servicefence +spec: + enable: true + host: + reviews.default.svc.cluster.local: # static dependenct of reviews.default service + stable: + +# related sidecar +spec: + egress: + - hosts: + - '*/reviews.default.svc.cluster.local' + - istio-system/* + - mesh-operator/* +``` + + + +#### 依赖某个namespace所有服务 + +适用于启用懒加载的服务静态依赖另外一个或多个namespace中所有服务的场景,可以在初始化时直接将配置加入sidecar crd。 + +下面的样例中,为启用懒加载的服务添加对`test`命名空间中所有服务的静态依赖关系。 + +```yaml +# servicefence +spec: + enable: true + host: + test/*: {} # static dependency of all services in test namespace + +# related sidecar +spec: + egress: + - hosts: + - test/* + - istio-system/* + - mesh-operator/* +``` + + + +#### 依赖具有某个label的所有服务 + +适用于启用懒加载的服务静态依赖具有某个label或多个label的所有服务的场景,可以在初始化时直接将配置加入sidecar crd。 + +下面的样例中,为启用懒加载的服务添加拥有`app=details`的所有服务,以及拥有`app=reviews, group=default`的所有服务的静态依赖关系。 + +```yaml +# servicefence +spec: + enable: true + labelSelector: # Match service label, multiple selectors are 'or' relationship + - selector: + app: details + - selector: # labels in one selector are 'and' relationship + app: reviews + group: default + +# related sidecar +spec: + egress: + - hosts: + - '*/details.default.svc.cluster.local' # with label "app=details" + - '*/details.test.svc.cluster.local' # with label "app=details" + - '*/reviews.default.svc.cluster.local' # with label "app=details" and "group=default" + - istio-system/* + - mesh-operator/* +``` + + + + + +### 支持自定义服务依赖别名 + +在某些场景,我们希望懒加载根据已知的服务依赖,添加一些额外的服务依赖进去。 + +用户可以通过配置`general.domainAliases`,提供自定义的转换关系,实现需求。`general.domainAliases`包含多个`domainAlias`,每个`domainAlias`由匹配规则`pattern`和转换规则`templates`组成。`pattern`只包含一个匹配规则,`templates`则可以包含多个转换规则。 + +举个例子,我们希望添加`..svc.cluster.local`时,额外添加`..mailsaas`的服务依赖,则可以这么配置 + +```yaml +apiVersion: config.netease.com/v1alpha1 +kind: SlimeBoot +metadata: + name: lazyload + namespace: mesh-operator +spec: + module: + - name: lazyload-test + kind: lazyload + enable: true + general: + wormholePort: # replace to your application service ports, and extend the list in case of multi ports + - "9080" + domainAliases: + - pattern: '(?P[^\.]+)\.(?P[^\.]+)\.svc\.cluster\.local$' + templates: + - "$namespace.$service.mailsaas" + #... +``` + +对应的servicefence会这样 + +```yaml +apiVersion: microservice.slime.io/v1alpha1 +kind: ServiceFence +metadata: + name: ratings + namespace: default +spec: + enable: true + host: + details.default.svc.cluster.local: # static dependent service + stable: {} +status: + domains: + default.details.mailsaas: # static dependent service converted result + hosts: + - default.details.mailsaas + default.productpage.mailsaas: # dynamic dependent service converted result + hosts: + - default.productpage.mailsaas + details.default.svc.cluster.local: + hosts: + - details.default.svc.cluster.local + productpage.default.svc.cluster.local: + hosts: + - productpage.default.svc.cluster.local + metricStatus: + '{destination_service="productpage.default.svc.cluster.local"}': "1" # dynamic dependent service +``` + +sidecar则是这样 + +```yaml +apiVersion: networking.istio.io/v1beta1 +kind: Sidecar +metadata: + name: ratings + namespace: default +spec: + egress: + - hosts: + - '*/default.details.mailsaas' # static dependent service converted result + - '*/default.productpage.mailsaas' # dynamic dependent service converted result + - '*/details.default.svc.cluster.local' + - '*/productpage.default.svc.cluster.local' + - istio-system/* + - mesh-operator/* + workloadSelector: + labels: + app: ratings +``` + + + + + +### 日志输出到本地并轮转 + +slime的日志默认输出到标准输出,指定SlimeBoot CR资源中`spec.module.global.log.logRotate`等于`true`会将日志输出到本地并启动日志轮转,不再输出到标准输出。 + +轮转配置也是可调整的,默认的配置如下,可以通过显示指定logRotateConfig中的各个值进行覆盖。 + +```yaml +spec: + module: + - name: lazyload # custom value + kind: lazyload # should be "lazyload" + enable: true + general: # replace previous "fence" field + wormholePort: # replace to your application svc ports + - "9080" + global: + log: + logRotate: true + logRotateConfig: + filePath: "/tmp/log/slime.log" + maxSizeMB: 100 + maxBackups: 10 + maxAgeDay: 10 + compress: true +``` + +通常需要配合存储卷使用,在存储卷准备完毕后,指定SlimeBoot CR资源中的`spec.volumes`和`spec.containers.slime.volumeMounts`来显示将存储卷挂载到日志本地文件所在的路径。 + +以下是基于minikube kubernetes场景下的完整demo + +#### 创建存储卷 + +基于/mnt/data路径创建hostpath类型的存储卷 + +```yaml +# hostPath pv for minikube demo +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: lazyload-claim + namespace: mesh-operator +spec: + storageClassName: manual + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 3Gi +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: lazyload-volumn + labels: + type: local +spec: + storageClassName: manual + capacity: + storage: 5Gi + accessModes: + - ReadWriteOnce + hostPath: + path: "/mnt/data" +``` + +#### 在SlimeBoot中声明挂载信息 + +在SlimeBoot CR资源中指定了存储卷会挂载到pod的"/tmp/log"路径,这样slime的日志会持久化到/mnt/data路径下,并且会自动轮转。 + +```yaml +--- +apiVersion: config.netease.com/v1alpha1 +kind: SlimeBoot +metadata: + name: lazyload + namespace: mesh-operator +spec: + image: + pullPolicy: Always + repository: docker.io/slimeio/slime-lazyload + tag: master-e5f2d83-dirty_1b68486 + module: + - name: lazyload # custom value + kind: lazyload # should be "lazyload" + enable: true + general: # replace previous "fence" field + wormholePort: + - "9080" + global: + log: + logRotate: true + logRotateConfig: + filePath: "/tmp/log/slime.log" + maxSizeMB: 100 + maxBackups: 10 + maxAgeDay: 10 + compress: true +#... + volumes: + - name: lazyload-storage + persistentVolumeClaim: + claimName: lazyload-claim + containers: + slime: + volumeMounts: + - mountPath: "/tmp/log" + name: lazyload-storage +``` + +[完整样例](./install/samples/lazyload/slimeboot_logrotate.yaml) + + + + + +## 示例 + +为bookinfo的productpage服务开启懒加载 + +### 安装 istio (1.8+) + + + +### 设定tag + +$latest_tag获取最新tag。默认执行的shell脚本和yaml文件均是$latest_tag版本。 + +```sh +$ export latest_tag=$(curl -s https://api.github.com/repos/slime-io/lazyload/tags | grep 'name' | cut -d\" -f4 | head -1) +``` + + + +### 安装 slime + +```shell +$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/slime-io/lazyload/$latest_tag/install/samples/lazyload/easy_install_lazyload.sh)" +``` + +确认所有组件已正常运行 + +```sh +$ kubectl get slimeboot -n mesh-operator +NAME AGE +lazyload 12s +$ kubectl get pod -n mesh-operator +NAME READY STATUS RESTARTS AGE +global-sidecar-7dd48b65c8-gc7g4 2/2 Running 0 18s +lazyload-85987bbd4b-djshs 1/1 Running 0 18s +slime-boot-6f778b75cd-4v675 1/1 Running 0 26s +``` + + + +### 安装bookinfo + + 创建前请将current-context中namespace切换到你想部署bookinfo的namespace,使bookinfo创建在其中。此处以default为例。 + +```sh +$ kubectl label namespace default istio-injection=enabled +$ kubectl apply -f "https://raw.githubusercontent.com/slime-io/lazyload/$latest_tag/install/config/bookinfo.yaml" +``` + +创建完后,状态如下 + +```sh +$ kubectl get po -n default +NAME READY STATUS RESTARTS AGE +details-v1-79f774bdb9-6vzj6 2/2 Running 0 60s +productpage-v1-6b746f74dc-vkfr7 2/2 Running 0 59s +ratings-v1-b6994bb9-klg48 2/2 Running 0 59s +reviews-v1-545db77b95-z5ql9 2/2 Running 0 59s +reviews-v2-7bf8c9648f-xcvd6 2/2 Running 0 60s +reviews-v3-84779c7bbc-gb52x 2/2 Running 0 60s +``` + +此样例中可以在pod/ratings中发起对productpage的访问,`curl productpage:9080/productpage`。 + +另外也可参考 [对外开放应用程序](https://istio.io/latest/zh/docs/setup/getting-started/#ip) 给应用暴露外访接口。 + + + +### 开启懒加载 + +创建servicefence,为productpage服务启用懒加载,共有两种方式: + +- 直接手动创建servicefence + +```sh +$ kubectl apply -f "https://raw.githubusercontent.com/slime-io/lazyload/$latest_tag/install/samples/lazyload/servicefence_productpage.yaml" +``` + +- 修改service自动创建servicefence + +```sh +$ kubectl label service productpage -n default slime.io/serviceFenced=true +``` + + + +确认生成servicefence和sidecar对象。 + +```sh +$ kubectl get servicefence -n default +NAME AGE +productpage 12s +$ kubectl get sidecar -n default +NAME AGE +productpage 22s +$ kubectl get servicefence productpage -n default -oyaml +apiVersion: microservice.slime.io/v1alpha1 +kind: ServiceFence +metadata: + creationTimestamp: "2021-12-23T06:21:14Z" + generation: 1 + labels: + app.kubernetes.io/created-by: fence-controller + name: productpage + namespace: default + resourceVersion: "10662886" + uid: f21e7d2b-4ab3-4de0-9b3d-131b6143d9db +spec: + enable: true +status: {} +$ kubectl get sidecar productpage -n default -oyaml +apiVersion: networking.istio.io/v1beta1 +kind: Sidecar +metadata: + creationTimestamp: "2021-12-23T06:21:14Z" + generation: 1 + name: productpage + namespace: default + ownerReferences: + - apiVersion: microservice.slime.io/v1alpha1 + blockOwnerDeletion: true + controller: true + kind: ServiceFence + name: productpage + uid: f21e7d2b-4ab3-4de0-9b3d-131b6143d9db + resourceVersion: "10662887" + uid: 85f9dc11-6d83-4b84-8d1b-14bd031cc57b +spec: + egress: + - hosts: + - istio-system/* + - mesh-operator/* + workloadSelector: + labels: + app: productpage +``` + + + +### 首次访问观察 + +第一次访问productpage,并使用`kubectl logs -f productpage-xxx -c istio-proxy -n default`观察访问日志。 + +``` +[2021-12-23T06:24:55.527Z] "GET /details/0 HTTP/1.1" 200 - via_upstream - "-" 0 178 12 12 "-" "curl/7.52.1" "7ec25152-ca8e-923b-a736-49838ce316f4" "details:9080" "172.17.0.10:80" outbound|9080||global-sidecar.mesh-operator.svc.cluster.local 172.17.0.11:45194 10.102.66.227:9080 172.17.0.11:40210 - - + +[2021-12-23T06:24:55.552Z] "GET /reviews/0 HTTP/1.1" 200 - via_upstream - "-" 0 295 30 29 "-" "curl/7.52.1" "7ec25152-ca8e-923b-a736-49838ce316f4" "reviews:9080" "172.17.0.10:80" outbound|9080||global-sidecar.mesh-operator.svc.cluster.local 172.17.0.11:45202 10.104.97.115:9080 172.17.0.11:40880 - - + +[2021-12-23T06:24:55.490Z] "GET /productpage HTTP/1.1" 200 - via_upstream - "-" 0 4183 93 93 "-" "curl/7.52.1" "7ec25152-ca8e-923b-a736-49838ce316f4" "productpage:9080" "172.17.0.11:9080" inbound|9080|| 127.0.0.6:48621 172.17.0.11:9080 172.17.0.7:41458 outbound_.9080_._.productpage.default.svc.cluster.local default +``` + +可以看出,此次outbound后端访问global-sidecar.mesh-operator.svc.cluster.local。 + +观察servicefence + +```sh +$ kubectl get servicefence productpage -n default -oyaml +apiVersion: microservice.slime.io/v1alpha1 +kind: ServiceFence +metadata: + creationTimestamp: "2021-12-23T06:21:14Z" + generation: 1 + labels: + app.kubernetes.io/created-by: fence-controller + name: productpage + namespace: default + resourceVersion: "10663136" + uid: f21e7d2b-4ab3-4de0-9b3d-131b6143d9db +spec: + enable: true +status: + domains: + details.default.svc.cluster.local: + hosts: + - details.default.svc.cluster.local + reviews.default.svc.cluster.local: + hosts: + - reviews.default.svc.cluster.local + metricStatus: + '{destination_service="details.default.svc.cluster.local"}': "1" + '{destination_service="reviews.default.svc.cluster.local"}': "1" +``` + + + +观察sidecar + +```sh +$ kubectl get sidecar productpage -n default -oyaml +apiVersion: networking.istio.io/v1beta1 +kind: Sidecar +metadata: + creationTimestamp: "2021-12-23T06:21:14Z" + generation: 2 + name: productpage + namespace: default + ownerReferences: + - apiVersion: microservice.slime.io/v1alpha1 + blockOwnerDeletion: true + controller: true + kind: ServiceFence + name: productpage + uid: f21e7d2b-4ab3-4de0-9b3d-131b6143d9db + resourceVersion: "10663141" + uid: 85f9dc11-6d83-4b84-8d1b-14bd031cc57b +spec: + egress: + - hosts: + - '*/details.default.svc.cluster.local' + - '*/reviews.default.svc.cluster.local' + - istio-system/* + - mesh-operator/* + workloadSelector: + labels: + app: productpage +``` + +reviews 和 details 被自动加入! + + + +### 再次访问观察 + +第二次访问productpage,观察productpage应用日志 + +``` +[2021-12-23T06:26:47.700Z] "GET /details/0 HTTP/1.1" 200 - via_upstream - "-" 0 178 13 12 "-" "curl/7.52.1" "899e918c-e44c-9dc2-9629-d8db191af972" "details:9080" "172.17.0.13:9080" outbound|9080||details.default.svc.cluster.local 172.17.0.11:50020 10.102.66.227:9080 172.17.0.11:42180 - default + +[2021-12-23T06:26:47.718Z] "GET /reviews/0 HTTP/1.1" 200 - via_upstream - "-" 0 375 78 77 "-" "curl/7.52.1" "899e918c-e44c-9dc2-9629-d8db191af972" "reviews:9080" "172.17.0.16:9080" outbound|9080||reviews.default.svc.cluster.local 172.17.0.11:58986 10.104.97.115:9080 172.17.0.11:42846 - default + +[2021-12-23T06:26:47.690Z] "GET /productpage HTTP/1.1" 200 - via_upstream - "-" 0 5179 122 121 "-" "curl/7.52.1" "899e918c-e44c-9dc2-9629-d8db191af972" "productpage:9080" "172.17.0.11:9080" inbound|9080|| 127.0.0.6:51799 172.17.0.11:9080 172.17.0.7:41458 outbound_.9080_._.productpage.default.svc.cluster.local default +``` + +可以看到,outbound日志的后端访问信息变为details.default.svc.cluster.local和reviews.default.svc.cluster.local,直接访问成功。 + + + +### 卸载 + +卸载bookinfo + +```sh +$ kubectl delete -f "https://raw.githubusercontent.com/slime-io/lazyload/$latest_tag/install/config/bookinfo.yaml" +``` + +卸载slime相关 + +```sh +$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/slime-io/lazyload/$latest_tag/install/samples/lazyload/easy_uninstall_lazyload.sh)" +``` + + + +### 补充说明 + +如想要使用其他tag或commit_id的shell脚本和yaml文件,请显示指定$custom_tag_or_commit。 + +```sh +$ export custom_tag_or_commit=xxx +``` + +执行的命令涉及到yaml文件,用$custom_tag_or_commit替换$latest_tag,如下 + +```sh +#$ kubectl apply -f "https://raw.githubusercontent.com/slime-io/lazyload/$latest_tag/install/config/bookinfo.yaml" +$ kubectl apply -f "https://raw.githubusercontent.com/slime-io/lazyload/$custom_tag_or_commit/install/config/bookinfo.yaml" +``` + +执行的命令涉及到shell文件,将$custom_tag_or_commit作为shell文件的参数,如下 + +```sh +#$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/slime-io/lazyload/$latest_tag/install/samples/smartlimiter/easy_install_limiter.sh)" +$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/slime-io/lazyload/$latest_tag/install/samples/smartlimiter/easy_install_limiter.sh)" $custom_tag_or_commit +``` + + + + + +## FAQ + +### 支持的Istio版本? + +Istio 1.8以后均支持,具体的兼容性说明详见[A note on the incompatibility of lazyload with some versions of istio](https://github.com/slime-io/slime/issues/145) + + + +### 为什么必须指定服务端口?(预计新版本将无需指定) + +不指定服务端口信息,由于通常启用懒加载时,sidecar的默认内容只包含istio-system和mesh-operator的服务信息,一些特定端口往往没有服务暴露。由于Istio的机制,只有兜底路由的listener无意义会被移除,无法保证占位listener的存在。 + +举个例子,bookinfo中服务暴露在"9080"端口,为productpage服务启用懒加载后,productpage的"9080" listener会被移除。再访问`details:9080`时,没有listener,会直接走到Passthrough逻辑,无法转到global-sidecar,也就实现不了服务依赖关系的获取。 + + + +### 为什么必须指定使用懒加载的namespace列表?(预计新版本将无需指定) + +由于短域名访问的场景大量存在,不同namespace下需要补充不同的namespace信息。所以懒加载会在这些指定的namespace下分别创建envoyfilter,补充合适的namespace信息。 + + + +### ~~global-sidecar-pilot的意义?~~(组件已废弃) + +~~由于global-sidecar的作用不同于普通sidecar,需要一些定制逻辑,比如兜底envoyfilter不对global-sidecar生效否则会死循环等,global-sidecar并不能直接使用集群原有pilot的全量配置。global-sidecar-pilot会从集群原有pilot处获取全量配置后,会进行微调,再推送给global-sidecar。现有global-sidecar-pilot是基于istiod 1.7改造的。~~ + +~~注:为了降低学习成本、增强兼容性,我们正在考虑去除global-sidecar-pilot,届时不再有定制化的pilot,完全兼容社区版本,预计在下一个大版本中实现此功能。~~ + + + +### ~~global sidecar不能正常启动?~~(已解决) + +~~global sidecar启动报错`Internal:Error adding/updating listener(s) 0.0.0.0_15021: cannot bind '0.0.0.0:15021': Address already in use`,这错误通常是端口冲突导致。global-sidecar是以gateway模式运行的sidecar,它会绑定到真实端口上。具体来说是istio-ingressgateway使用了15021端口,这会导致global-sidecar的lds更新失败,修改ingressgateway的15021端口为其他值可解决。~~ + +~~注:目前是通过端口规划来解决此问题,下个大版本中会摆脱这个局限。~~ diff --git a/staging/src/slime.io/slime/modules/lazyload/main.go b/staging/src/slime.io/slime/modules/lazyload/main.go new file mode 100644 index 00000000..89920cf7 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/main.go @@ -0,0 +1,27 @@ +/* + + +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 ( + "slime.io/slime/framework/model/module" + "slime.io/slime/modules/lazyload/model" + modmodule "slime.io/slime/modules/lazyload/module" +) + +func main() { + module.Main(model.ModuleName, []module.Module{&modmodule.Module{}}) +} diff --git a/staging/src/slime.io/slime/modules/lazyload/media/lazyload-architecture-2021-10-19-en.png b/staging/src/slime.io/slime/modules/lazyload/media/lazyload-architecture-2021-10-19-en.png new file mode 100755 index 00000000..1e00be83 Binary files /dev/null and b/staging/src/slime.io/slime/modules/lazyload/media/lazyload-architecture-2021-10-19-en.png differ diff --git a/staging/src/slime.io/slime/modules/lazyload/media/lazyload-architecture-2021-10-19.png b/staging/src/slime.io/slime/modules/lazyload/media/lazyload-architecture-2021-10-19.png new file mode 100755 index 00000000..286b8db0 Binary files /dev/null and b/staging/src/slime.io/slime/modules/lazyload/media/lazyload-architecture-2021-10-19.png differ diff --git a/staging/src/slime.io/slime/modules/lazyload/media/lazyload-architecture-20211222.png b/staging/src/slime.io/slime/modules/lazyload/media/lazyload-architecture-20211222.png new file mode 100755 index 00000000..b1dcd043 Binary files /dev/null and b/staging/src/slime.io/slime/modules/lazyload/media/lazyload-architecture-20211222.png differ diff --git a/staging/src/slime.io/slime/modules/lazyload/media/lazyload-architecture-20211222_zh.png b/staging/src/slime.io/slime/modules/lazyload/media/lazyload-architecture-20211222_zh.png new file mode 100755 index 00000000..fc1f92de Binary files /dev/null and b/staging/src/slime.io/slime/modules/lazyload/media/lazyload-architecture-20211222_zh.png differ diff --git a/staging/src/slime.io/slime/modules/lazyload/media/lazyload-vs-update.png b/staging/src/slime.io/slime/modules/lazyload/media/lazyload-vs-update.png new file mode 100644 index 00000000..cc4da418 Binary files /dev/null and b/staging/src/slime.io/slime/modules/lazyload/media/lazyload-vs-update.png differ diff --git a/staging/src/slime.io/slime/modules/lazyload/media/ll.png b/staging/src/slime.io/slime/modules/lazyload/media/ll.png new file mode 100644 index 00000000..f4ced871 Binary files /dev/null and b/staging/src/slime.io/slime/modules/lazyload/media/ll.png differ diff --git a/staging/src/slime.io/slime/modules/lazyload/model/model.go b/staging/src/slime.io/slime/modules/lazyload/model/model.go new file mode 100644 index 00000000..1f684d49 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/model/model.go @@ -0,0 +1,10 @@ +package model + +import ( + "github.com/sirupsen/logrus" + frameworkmodel "slime.io/slime/framework/model" +) + +const ModuleName = "lazyload" + +var ModuleLog = logrus.WithField(frameworkmodel.LogFieldKeyModule, ModuleName) diff --git a/staging/src/slime.io/slime/modules/lazyload/module/module.go b/staging/src/slime.io/slime/modules/lazyload/module/module.go new file mode 100644 index 00000000..23a15357 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/module/module.go @@ -0,0 +1,92 @@ +package module + +import ( + "os" + + "github.com/golang/protobuf/proto" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + istioapi "slime.io/slime/framework/apis" + "slime.io/slime/framework/bootstrap" + basecontroller "slime.io/slime/framework/controllers" + "slime.io/slime/framework/model/module" + lazyloadapiv1alpha1 "slime.io/slime/modules/lazyload/api/v1alpha1" + "slime.io/slime/modules/lazyload/controllers" + modmodel "slime.io/slime/modules/lazyload/model" +) + +var log = modmodel.ModuleLog + +type Module struct { + config lazyloadapiv1alpha1.Fence +} + +func (mo *Module) Kind() string { + return modmodel.ModuleName +} + +func (mo *Module) Config() proto.Message { + return &mo.config +} + +func (mo *Module) InitScheme(scheme *runtime.Scheme) error { + for _, f := range []func(*runtime.Scheme) error{ + clientgoscheme.AddToScheme, + lazyloadapiv1alpha1.AddToScheme, + istioapi.AddToScheme, + } { + if err := f(scheme); err != nil { + return err + } + } + return nil +} + +func (mo *Module) Clone() module.Module { + ret := *mo + return &ret +} + +func (mo *Module) InitManager(mgr manager.Manager, env bootstrap.Environment, cbs module.InitCallbacks) error { + cfg := &mo.config + + sfReconciler := controllers.NewReconciler(cfg, mgr, env) + + var builder basecontroller.ObjectReconcilerBuilder + + // auto generate ServiceFence or not + if cfg == nil || cfg.AutoFence { + builder = builder.Add(basecontroller.ObjectReconcileItem{ + Name: "Namespace", + ApiType: &corev1.Namespace{}, + R: reconcile.Func(sfReconciler.ReconcileNamespace), + }).Add(basecontroller.ObjectReconcileItem{ + Name: "Service", + ApiType: &corev1.Service{}, + R: reconcile.Func(sfReconciler.ReconcileService), + }) + } + + builder = builder.Add(basecontroller.ObjectReconcileItem{ + Name: "ServiceFence", + R: sfReconciler, + }).Add(basecontroller.ObjectReconcileItem{ + Name: "VirtualService", + R: &basecontroller.VirtualServiceReconciler{ + Env: &env, + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }, + }) + + if err := builder.Build(mgr); err != nil { + log.Errorf("unable to create controller,%+v", err) + os.Exit(1) + } + + return nil +} diff --git a/staging/src/slime.io/slime/modules/lazyload/pkg/proxy/http.go b/staging/src/slime.io/slime/modules/lazyload/pkg/proxy/http.go new file mode 100644 index 00000000..63b21d46 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/pkg/proxy/http.go @@ -0,0 +1,132 @@ +package proxy + +import ( + "context" + "fmt" + "io" + "net" + "net/http" + "strconv" + "strings" + "time" + + log "github.com/sirupsen/logrus" +) + +const ( + HeaderSourceNs = "Slime-Source-Ns" + HeaderOrigDest = "Slime-Orig-Dest" +) + +type HealthzProxy struct{} + +func (p *HealthzProxy) ServeHTTP(w http.ResponseWriter, req *http.Request) { + // health check + if req.RequestURI == "/healthz/live" || req.RequestURI == "/healthz/ready" { + w.Write([]byte("Healthy!")) + return + } +} + +type Proxy struct { + WormholePort int +} + +func (p *Proxy) ServeHTTP(w http.ResponseWriter, req *http.Request) { + var ( + reqCtx = req.Context() + reqHost = req.Host + origDest, origDestIp string + origDestPort = p.WormholePort + ) + log.Debugf("proxy received request, reqHost: %s", reqHost) + + // try to complete short name + if values := req.Header[HeaderSourceNs]; len(values) > 0 && values[0] != "" { + req.Header.Del(HeaderSourceNs) + if !strings.Contains(reqHost, ".") { + // short name + ns := values[0] + if idx := strings.LastIndex(reqHost, ":"); idx >= 0 { + reqHost = fmt.Sprintf("%s.%s%s", reqHost[:idx], ns, reqHost[idx:]) + } else { + reqHost = fmt.Sprintf("%s.%s", reqHost, ns) + } + } + log.Debugf("handle request header [Slime-Source-Ns]: %s", values[0]) + } + + if values := req.Header[HeaderOrigDest]; len(values) > 0 { + origDest = values[0] + req.Header.Del(HeaderOrigDest) + + if idx := strings.LastIndex(origDest, ":"); idx >= 0 { + origDestIp = origDest[:idx] + if v, err := strconv.Atoi(origDest[idx+1:]); err != nil { + http.Error(w, fmt.Sprintf("invalid header %s value: %s", HeaderOrigDest, origDest), http.StatusBadRequest) + return + } else { + origDestPort = v + } + } else { + origDestIp = origDest + } + log.Debugf("handle request header [Slime-Orig-Dest]: %s", values[0]) + } + + if origDest == "" { + if idx := strings.LastIndex(reqHost, ":"); idx >= 0 { + origDestIp = reqHost[:idx] + } else { + origDestIp = reqHost + } + } + log.Debugf("proxy forward request to: %s:%d", origDestIp, origDestPort) + + if req.URL.Scheme == "" { + req.URL.Scheme = "http" + } + req.URL.Host = reqHost + req.Host = reqHost + req.RequestURI = "" + newCtx, _ := context.WithCancel(reqCtx) + req = req.WithContext(newCtx) + + dialer := &net.Dialer{ + // Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + } + transport := &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + addr = fmt.Sprintf("%s:%d", origDestIp, origDestPort) + return dialer.DialContext(ctx, network, addr) + }, + MaxIdleConns: 100, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + } + client := &http.Client{ + Transport: transport, + } + + resp, err := client.Do(req) + if err != nil { + select { + case <-reqCtx.Done(): + default: + log.Infof("do req get err %v", err) + http.Error(w, "", http.StatusInternalServerError) + } + return + } + + for k, vv := range resp.Header { + for _, v := range vv { + w.Header().Add(k, v) + } + } + w.WriteHeader(resp.StatusCode) + _, _ = io.Copy(w, resp.Body) +} diff --git a/staging/src/slime.io/slime/modules/lazyload/publish.sh b/staging/src/slime.io/slime/modules/lazyload/publish.sh new file mode 100644 index 00000000..b7507830 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/publish.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +export MOD=lazyload +../../../../../../bin/publish.sh "$@" diff --git a/staging/src/slime.io/slime/modules/lazyload/test/e2e/e2e_test.go b/staging/src/slime.io/slime/modules/lazyload/test/e2e/e2e_test.go new file mode 100644 index 00000000..462b4c94 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/test/e2e/e2e_test.go @@ -0,0 +1,48 @@ +package e2e + +import ( + "fmt" + "os" + "path/filepath" + "testing" + + framework2 "slime.io/slime/framework/test/e2e/framework" + "slime.io/slime/framework/test/e2e/framework/testfiles" + + "github.com/golang/glog" + "github.com/onsi/ginkgo" + "github.com/onsi/ginkgo/config" + "github.com/onsi/ginkgo/reporters" + "github.com/onsi/gomega" +) + +func init() { + framework2.RegisterFlags() + if framework2.TestContext.RepoRoot != "" { + testfiles.AddFileSource(testfiles.RootFileSource{Root: framework2.TestContext.RepoRoot}) + } +} + +func TestE2E(t *testing.T) { + RunE2ETests(t) +} + +func RunE2ETests(t *testing.T) { + gomega.RegisterFailHandler(ginkgo.Fail) + + var r []ginkgo.Reporter + ReportDir := "reports" + + if framework2.TestContext.ReportDir != "" { + ReportDir = framework2.TestContext.ReportDir + } + + if err := os.Mkdir(ReportDir, os.ModePerm); err != nil && !os.IsExist(err) { + glog.Fatalf("Failed creating report directory %s ", ReportDir) + } + + r = append(r, reporters.NewJUnitReporter(filepath.Join(ReportDir, fmt.Sprintf("service_%02d.xml", config.GinkgoConfig.ParallelNode)))) + + framework2.Logf("Starting e2e run %q on ginkgo node %d \n", framework2.RunId, config.GinkgoConfig.ParallelNode) + ginkgo.RunSpecsWithDefaultAndCustomReporters(t, "e2e test suite", r) +} diff --git a/staging/src/slime.io/slime/modules/lazyload/test/e2e/lazyload_test.go b/staging/src/slime.io/slime/modules/lazyload/test/e2e/lazyload_test.go new file mode 100644 index 00000000..69a5ac36 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/test/e2e/lazyload_test.go @@ -0,0 +1,378 @@ +package e2e + +import ( + "os" + "os/exec" + "path/filepath" + "strings" + "time" + + "github.com/onsi/ginkgo" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + + "slime.io/slime/framework/test/e2e/framework" + e2epod "slime.io/slime/framework/test/e2e/framework/pod" + "slime.io/slime/framework/test/e2e/framework/testfiles" +) + +var _ = ginkgo.Describe("Slime e2e test", func() { + f := framework.NewDefaultFramework("lazyload") + f.SkipNamespaceCreation = true + + ginkgo.It("clean resource", func() { + var cmd *exec.Cmd + cmd = exec.Command("bash", "lazyload_test_clean.sh") + _, err := cmd.Output() + framework.ExpectNoError(err) + }) + + ginkgo.It("rev: strictRev=true lazyload works", func() { + _, err := f.CreateNamespace(nsSlime, nil) + framework.ExpectNoError(err) + _, err = f.CreateNamespace(nsApps, map[string]string{istioRevKey: substituteValue("istioRevValue", istioRevValue)}) + framework.ExpectNoError(err) + + strictRev := true + createSlimeBoot(f) + createSlimeModuleLazyload(f, strictRev) + createExampleApps(f) + createServiceFence(f, strictRev) + updateSidecar(f) + verifyAccessLogs(f) + deleteTestResource() + }) + + ginkgo.It("no-rev: strictRev=false lazyload works", func() { + _, err := f.CreateNamespace(nsSlime, nil) + framework.ExpectNoError(err) + _, err = f.CreateNamespace(nsApps, map[string]string{istioRevKey: substituteValue("istioRevValue", istioRevValue)}) + framework.ExpectNoError(err) + + strictRev := false + createSlimeBoot(f) + createSlimeModuleLazyload(f, strictRev) + createExampleApps(f) + createServiceFence(f, strictRev) + updateSidecar(f) + verifyAccessLogs(f) + deleteTestResource() + }) +}) + +func createSlimeBoot(f *framework.Framework) { + cs := f.ClientSet + + // install slimeboot + crdYaml := readFile(test, "init/crds.yaml") + framework.RunKubectlOrDieInput("", crdYaml, "create", "-f", "-") + defer func() { + testResourceToDelete = append(testResourceToDelete, &TestResource{Namespace: "", Contents: crdYaml}) + }() + deploySlimeBootYaml := readFile(test, "init/deployment_slime-boot.yaml") + deploySlimeBootYaml = strings.ReplaceAll(deploySlimeBootYaml, "{{slimebootTag}}", substituteValue("slimeBootTag", slimebootTag)) + framework.RunKubectlOrDieInput(nsSlime, deploySlimeBootYaml, "create", "-f", "-") + defer func() { + testResourceToDelete = append(testResourceToDelete, &TestResource{Namespace: nsSlime, Contents: deploySlimeBootYaml}) + }() + + slimebootDeploymentInstalled := false + + for i := 0; i < 10; i++ { + pods, err := cs.CoreV1().Pods(nsSlime).List(metav1.ListOptions{}) + framework.ExpectNoError(err) + if len(pods.Items) == 0 { + time.Sleep(500 * time.Millisecond) + continue + } + for _, pod := range pods.Items { + err = e2epod.WaitTimeoutForPodReadyInNamespace(cs, pod.Name, nsSlime, framework.PodStartTimeout) + framework.ExpectNoError(err) + if strings.Contains(pod.Name, slimebootName) { + slimebootDeploymentInstalled = true + } + } + break + } + if !slimebootDeploymentInstalled { + framework.Failf("deployment slime-boot installation failed\n") + } + ginkgo.By("deployment slimeboot installs successfully") +} + +func createSlimeModuleLazyload(f *framework.Framework, strictRev bool) { + cs := f.ClientSet + + slimebootLazyloadYaml := readFile(test, "samples/lazyload/slimeboot_lazyload.yaml") + slimebootLazyloadYaml = strings.ReplaceAll(slimebootLazyloadYaml, "{{lazyloadTag}}", substituteValue("lazyloadTag", lazyloadTag)) + slimebootLazyloadYaml = strings.ReplaceAll(slimebootLazyloadYaml, "{{globalSidecarTag}}", substituteValue("globalSidecarTag", globalSidecarTag)) + slimebootLazyloadYaml = strings.ReplaceAll(slimebootLazyloadYaml, "{{globalSidecarPilotTag}}", substituteValue("globalSidecarPilotTag", globalSidecarPilotTag)) + slimebootLazyloadYaml = strings.ReplaceAll(slimebootLazyloadYaml, "{{istioRevValue}}", substituteValue("istioRevValue", istioRevValue)) + if strictRev { + slimebootLazyloadYaml = strings.ReplaceAll(slimebootLazyloadYaml, "{{strictRev}}", "true") + } else { + slimebootLazyloadYaml = strings.ReplaceAll(slimebootLazyloadYaml, "{{strictRev}}", "false") + } + framework.RunKubectlOrDieInput(nsSlime, slimebootLazyloadYaml, "create", "-f", "-") + defer func() { + testResourceToDelete = append(testResourceToDelete, &TestResource{Namespace: nsSlime, Contents: slimebootLazyloadYaml}) + }() + + // check + lazyloadDeploymentInstalled := false + globalSidecarPilotInstalled := false + globalSidecarInstalled := false + + for i := 0; i < 60; i++ { + pods, err := cs.CoreV1().Pods(nsSlime).List(metav1.ListOptions{}) + framework.ExpectNoError(err) + if len(pods.Items) != 3 { + time.Sleep(500 * time.Millisecond) + continue + } + for _, pod := range pods.Items { + err = e2epod.WaitTimeoutForPodReadyInNamespace(cs, pod.Name, nsSlime, framework.PodStartTimeout) + framework.ExpectNoError(err) + if strings.Contains(pod.Name, "lazyload") { + lazyloadDeploymentInstalled = true + } + if strings.Contains(pod.Name, "global-sidecar-pilot") { + globalSidecarPilotInstalled = true + } + } + break + } + + for i := 0; i < 60; i++ { + pods, err := f.ClientSet.CoreV1().Pods(nsApps).List(metav1.ListOptions{}) + framework.ExpectNoError(err) + if len(pods.Items) == 0 { + time.Sleep(500 * time.Millisecond) + continue + } + for _, pod := range pods.Items { + err = e2epod.WaitTimeoutForPodReadyInNamespace(cs, pod.Name, nsApps, framework.PodStartTimeout) + framework.ExpectNoError(err) + if strings.Contains(pod.Name, "global-sidecar") { + globalSidecarInstalled = true + } + } + break + } + + if !lazyloadDeploymentInstalled { + framework.Failf("deployment lazyload installation failed\n") + } + if !globalSidecarPilotInstalled { + framework.Failf("global-sidecar-pilot installation failed\n") + } + if !globalSidecarInstalled { + framework.Failf("global-sidecar installation failed\n") + } + ginkgo.By("slimemodule lazyload installs successfully") +} + +func createExampleApps(f *framework.Framework) { + cs := f.ClientSet + + exampleAppsYaml := readFile(test, "config/bookinfo.yaml") + framework.RunKubectlOrDieInput(nsApps, exampleAppsYaml, "create", "-f", "-") + defer func() { + testResourceToDelete = append(testResourceToDelete, &TestResource{Namespace: nsApps, Contents: exampleAppsYaml}) + }() + + // check + for i := 0; i < 60; i++ { + pods, err := cs.CoreV1().Pods(nsApps).List(metav1.ListOptions{}) + framework.ExpectNoError(err) + if len(pods.Items) != 6 { + time.Sleep(500 * time.Millisecond) + continue + } + for _, pod := range pods.Items { + err = e2epod.WaitTimeoutForPodReadyInNamespace(cs, pod.Name, nsApps, framework.PodStartTimeout) + framework.ExpectNoError(err) + } + break + } + ginkgo.By("example apps install successfully") +} + +func createServiceFence(f *framework.Framework, strictRev bool) { + // create CR ServiceFence + serviceFenceYaml := readFile(test, "samples/lazyload/servicefence_productpage.yaml") + serviceFenceYaml = strings.ReplaceAll(serviceFenceYaml, "{{istioRevKey}}", substituteValue("istioRevKey", istioRevKey)) + if strictRev { + serviceFenceYaml = strings.ReplaceAll(serviceFenceYaml, "{{istioRevValue}}", substituteValue("strictRev", istioRevValue)) + } else { + serviceFenceYaml = strings.ReplaceAll(serviceFenceYaml, "{{istioRevValue}}", "") + } + framework.RunKubectlOrDieInput(nsApps, serviceFenceYaml, "create", "-f", "-") + defer func() { + testResourceToDelete = append(testResourceToDelete, &TestResource{Namespace: nsApps, Contents: serviceFenceYaml}) + }() + + // check + svfGvr := schema.GroupVersionResource{ + Group: svfGroup, + Version: svfVersion, + Resource: svfResource, + } + + svfCreated := false + for i := 0; i < 60; i++ { + svf, err := f.DynamicClient.Resource(svfGvr).Namespace(nsApps).Get(svfName, metav1.GetOptions{}) + if err != nil { + time.Sleep(500 * time.Millisecond) + continue + } + if (strictRev && svf.GetLabels()[istioRevKey] != istioRevValue) || (!strictRev && svf.GetLabels()[istioRevKey] != "") { + framework.Failf("The created servicefence has wrong istioRev label %s.\n", svf.GetLabels()[istioRevKey]) + } + svfCreated = true + break + } + if svfCreated != true { + framework.Failf("Failed to create servicefence.\n") + } + + sidecarGvr := schema.GroupVersionResource{ + Group: sidecarGroup, + Version: sidecarVersion, + Resource: sidecarResource, + } + + sidecarCreated := false + for i := 0; i < 60; i++ { + sidecar, err := f.DynamicClient.Resource(sidecarGvr).Namespace(nsApps).Get(sidecarName, metav1.GetOptions{}) + if err != nil { + time.Sleep(500 * time.Millisecond) + continue + } + if (strictRev && sidecar.GetLabels()[istioRevKey] != istioRevValue) || (!strictRev && sidecar.GetLabels()[istioRevKey] != "") { + framework.Failf("The created sidecar has wrong istioRev label %s.\n", sidecar.GetLabels()[istioRevKey]) + } + sidecarCreated = true + break + } + if sidecarCreated != true { + framework.Failf("Failed to create sidecar.\n") + } + + ginkgo.By("serviceFence and Sidecar create successfully") +} + +func updateSidecar(f *framework.Framework) { + pods, err := f.ClientSet.CoreV1().Pods(nsApps).List(metav1.ListOptions{}) + framework.ExpectNoError(err) +ExecLoop: + for _, pod := range pods.Items { + if strings.Contains(pod.Name, "ratings") { + total, success := 0, 0 + for { + total++ + _, _, err = f.ExecShellInPodWithFullOutput(pod.Name, nsApps, "curl \"productpage:9080/productpage\"") + if err == nil { + success++ + if success >= 30 { + break ExecLoop + } + } + if total < 120 { + time.Sleep(500 * time.Millisecond) + continue + } + break ExecLoop + // framework.ExpectNoError(err) + } + } + } + + sidecarGvr := schema.GroupVersionResource{ + Group: sidecarGroup, + Version: sidecarVersion, + Resource: sidecarResource, + } + sidecarUpdated := false +VerifyLoop: + for i := 0; i < 120; i++ { + sidecar, err := f.DynamicClient.Resource(sidecarGvr).Namespace(nsApps).Get(sidecarName, metav1.GetOptions{}) + framework.ExpectNoError(err) + egress, _, err := unstructured.NestedSlice(sidecar.Object, "spec", "egress") + framework.ExpectNoError(err) + hosts, _, err := unstructured.NestedStringSlice(egress[0].(map[string]interface{}), "hosts") + framework.ExpectNoError(err) + for _, host := range hosts { + if strings.Contains(host, "details") || strings.Contains(host, "reviews") { + sidecarUpdated = true + break VerifyLoop + } + } + time.Sleep(500 * time.Millisecond) + } + if !sidecarUpdated { + framework.Failf("sidecar updated failed\n") + } + ginkgo.By("sidecar updated successfully") +} + +func verifyAccessLogs(f *framework.Framework) { + cs := f.ClientSet + pods, err := cs.CoreV1().Pods(nsApps).List(metav1.ListOptions{}) + framework.ExpectNoError(err) + for _, pod := range pods.Items { + if strings.Contains(pod.Name, "productpage") { + times := 0 + for { + logs, err := e2epod.GetPodLogs(cs, nsApps, pod.Name, "istio-proxy") + framework.ExpectNoError(err) + if strings.Contains(logs, "outbound|9080||details") || strings.Contains(logs, "outbound|9080||reviews") { + break + } else { + times++ + } + if times > 60 { + framework.Failf("access log verified failed\n") + } else { + time.Sleep(500 * time.Millisecond) + } + } + break + } + } + ginkgo.By("access log verified successfully") +} + +func substituteValue(value, defaultValue string) string { + if os.Getenv(value) != "" { + return os.Getenv(value) + } + return defaultValue +} + +func readFile(test, file string) string { + from := filepath.Join(test, file) + data, err := testfiles.Read(from) + if err != nil { + framework.ExpectNoError(err, "failed to read file %s/%s", test, file) + } + return string(data) +} + +func deleteTestResource() { + for i := len(testResourceToDelete) - 1; i >= 0; i-- { + cleanupKubectlInputs(testResourceToDelete[i].Namespace, testResourceToDelete[i].Contents) + time.Sleep(500 * time.Millisecond) + } +} + +// Stops everything from filePath from namespace ns and checks if everything matching selectors from the given namespace is correctly stopped. +// Aware of the kubectl example files map. +func cleanupKubectlInputs(ns string, fileContents string, selectors ...string) { + ginkgo.By("using delete to clean up resources") + // support backward compatibility : file paths or raw json - since we are removing file path + // dependencies from this test. + framework.RunKubectlOrDieInput(ns, fileContents, "delete", "--grace-period=0", "--force", "-f", "-") + // assertCleanup(ns, selectors...) +} diff --git a/staging/src/slime.io/slime/modules/lazyload/test/e2e/lazyload_test_clean.sh b/staging/src/slime.io/slime/modules/lazyload/test/e2e/lazyload_test_clean.sh new file mode 100644 index 00000000..448dbdc5 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/test/e2e/lazyload_test_clean.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# use sed cmd change {{xx}} to xx in yaml files +# the value of xx has no effect on cleaning resource + +sed -e 's/{{istioRevKey}}/istioRevKey/g' -e 's/{{istioRevValue}}/istioRevValue/g' ./testdata/install/samples/lazyload/servicefence_productpage.yaml > tmp_servicefence_productpage.yaml +kubectl delete -f tmp_servicefence_productpage.yaml + +sed -e 's/{{lazyloadTag}}/lazyloadTag/g' -e 's/{{istioRevValue}}/istioRevValue/g' -e 's/{{strictRev}}/strictRev/g' -e 's/{{globalSidecarTag}}/globalSidecarTag/g' -e 's/{{globalSidecarPilotTag}}/globalSidecarPilotTag/g' ./testdata/install/samples/lazyload/slimeboot_lazyload.yaml > tmp_slimeboot_lazyload.yaml +kubectl delete -f tmp_slimeboot_lazyload.yaml + +kubectl delete -f ./testdata/install/config/bookinfo.yaml + +sed -e 's/{{slimebootTag}}/slimebootTag/g' ./testdata/install/init/deployment_slime-boot.yaml > tmp_deployment_slime-boot.yaml +kubectl delete -f tmp_deployment_slime-boot.yaml + +kubectl delete -f ./testdata/install/init/crds.yaml + +kubectl delete ns example-apps + +kubectl delete ns mesh-operator + +rm -f tmp_* diff --git a/staging/src/slime.io/slime/modules/lazyload/test/e2e/reports/service_01.xml b/staging/src/slime.io/slime/modules/lazyload/test/e2e/reports/service_01.xml new file mode 100644 index 00000000..7e67607d --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/test/e2e/reports/service_01.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/staging/src/slime.io/slime/modules/lazyload/test/e2e/testdata/install/config/bookinfo.yaml b/staging/src/slime.io/slime/modules/lazyload/test/e2e/testdata/install/config/bookinfo.yaml new file mode 100644 index 00000000..bb5d77dd --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/test/e2e/testdata/install/config/bookinfo.yaml @@ -0,0 +1,343 @@ +# Copyright Istio Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +################################################################################################## +# This file defines the services, service accounts, and deployments for the Bookinfo sample. +# +# To apply all 4 Bookinfo services, their corresponding service accounts, and deployments: +# +# kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml +# +# Alternatively, you can deploy any resource separately: +# +# kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml -l service=reviews # reviews Service +# kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml -l account=reviews # reviews ServiceAccount +# kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml -l app=reviews,version=v3 # reviews-v3 Deployment +################################################################################################## + +################################################################################################## +# Details service +################################################################################################## +apiVersion: v1 +kind: Service +metadata: + name: details + labels: + app: details + service: details +spec: + ports: + - port: 9080 + name: http + selector: + app: details +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: bookinfo-details + labels: + account: details +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: details-v1 + labels: + app: details + version: v1 +spec: + replicas: 1 + selector: + matchLabels: + app: details + version: v1 + template: + metadata: + labels: + app: details + version: v1 + spec: + serviceAccountName: bookinfo-details + containers: + - name: details + image: docker.io/istio/examples-bookinfo-details-v1:1.16.2 + imagePullPolicy: IfNotPresent + ports: + - containerPort: 9080 + securityContext: + runAsUser: 1000 +--- +################################################################################################## +# Ratings service +################################################################################################## +apiVersion: v1 +kind: Service +metadata: + name: ratings + labels: + app: ratings + service: ratings +spec: + ports: + - port: 9080 + name: http + selector: + app: ratings +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: bookinfo-ratings + labels: + account: ratings +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: ratings-v1 + labels: + app: ratings + version: v1 +spec: + replicas: 1 + selector: + matchLabels: + app: ratings + version: v1 + template: + metadata: + labels: + app: ratings + version: v1 + spec: + serviceAccountName: bookinfo-ratings + containers: + - name: ratings + image: docker.io/istio/examples-bookinfo-ratings-v1:1.16.2 + imagePullPolicy: IfNotPresent + ports: + - containerPort: 9080 + securityContext: + runAsUser: 1000 +--- +################################################################################################## +# Reviews service +################################################################################################## +apiVersion: v1 +kind: Service +metadata: + name: reviews + labels: + app: reviews + service: reviews +spec: + ports: + - port: 9080 + name: http + selector: + app: reviews +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: bookinfo-reviews + labels: + account: reviews +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: reviews-v1 + labels: + app: reviews + version: v1 +spec: + replicas: 1 + selector: + matchLabels: + app: reviews + version: v1 + template: + metadata: + labels: + app: reviews + version: v1 + spec: + serviceAccountName: bookinfo-reviews + containers: + - name: reviews + image: docker.io/istio/examples-bookinfo-reviews-v1:1.16.2 + imagePullPolicy: IfNotPresent + env: + - name: LOG_DIR + value: "/tmp/logs" + ports: + - containerPort: 9080 + volumeMounts: + - name: tmp + mountPath: /tmp + - name: wlp-output + mountPath: /opt/ibm/wlp/output + securityContext: + runAsUser: 1000 + volumes: + - name: wlp-output + emptyDir: {} + - name: tmp + emptyDir: {} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: reviews-v2 + labels: + app: reviews + version: v2 +spec: + replicas: 1 + selector: + matchLabels: + app: reviews + version: v2 + template: + metadata: + labels: + app: reviews + version: v2 + spec: + serviceAccountName: bookinfo-reviews + containers: + - name: reviews + image: docker.io/istio/examples-bookinfo-reviews-v2:1.16.2 + imagePullPolicy: IfNotPresent + env: + - name: LOG_DIR + value: "/tmp/logs" + ports: + - containerPort: 9080 + volumeMounts: + - name: tmp + mountPath: /tmp + - name: wlp-output + mountPath: /opt/ibm/wlp/output + securityContext: + runAsUser: 1000 + volumes: + - name: wlp-output + emptyDir: {} + - name: tmp + emptyDir: {} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: reviews-v3 + labels: + app: reviews + version: v3 +spec: + replicas: 1 + selector: + matchLabels: + app: reviews + version: v3 + template: + metadata: + labels: + app: reviews + version: v3 + spec: + serviceAccountName: bookinfo-reviews + containers: + - name: reviews + image: docker.io/istio/examples-bookinfo-reviews-v3:1.16.2 + imagePullPolicy: IfNotPresent + env: + - name: LOG_DIR + value: "/tmp/logs" + ports: + - containerPort: 9080 + volumeMounts: + - name: tmp + mountPath: /tmp + - name: wlp-output + mountPath: /opt/ibm/wlp/output + securityContext: + runAsUser: 1000 + volumes: + - name: wlp-output + emptyDir: {} + - name: tmp + emptyDir: {} +--- +################################################################################################## +# Productpage services +################################################################################################## +apiVersion: v1 +kind: Service +metadata: + name: productpage + labels: + app: productpage + service: productpage +spec: + ports: + - port: 9080 + name: http + selector: + app: productpage +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: bookinfo-productpage + labels: + account: productpage +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: productpage-v1 + labels: + app: productpage + version: v1 +spec: + replicas: 1 + selector: + matchLabels: + app: productpage + version: v1 + template: + metadata: + labels: + app: productpage + version: v1 + spec: + serviceAccountName: bookinfo-productpage + containers: + - name: productpage + image: docker.io/istio/examples-bookinfo-productpage-v1:1.16.2 + imagePullPolicy: IfNotPresent + ports: + - containerPort: 9080 + volumeMounts: + - name: tmp + mountPath: /tmp + securityContext: + runAsUser: 1000 + volumes: + - name: tmp + emptyDir: {} +--- diff --git a/staging/src/slime.io/slime/modules/lazyload/test/e2e/testdata/install/init/crds.yaml b/staging/src/slime.io/slime/modules/lazyload/test/e2e/testdata/install/init/crds.yaml new file mode 100644 index 00000000..64e56c3c --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/test/e2e/testdata/install/init/crds.yaml @@ -0,0 +1,101 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: servicefences.microservice.slime.io +spec: + group: microservice.slime.io + names: + shortNames: + - svf + kind: ServiceFence + listKind: ServiceFenceList + plural: servicefences + singular: servicefence + scope: Namespaced + subresources: + status: { } + versions: + - name: v1alpha1 + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: smartlimiters.microservice.slime.io +spec: + group: microservice.slime.io + names: + kind: SmartLimiter + listKind: SmartLimiterList + plural: smartlimiters + singular: smartlimiter + scope: Namespaced + subresources: + status: {} + version: v1alpha1 + versions: + - name: v1alpha1 + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: envoyplugins.microservice.slime.io +spec: + group: microservice.slime.io + names: + shortNames: + - evp + kind: EnvoyPlugin + listKind: EnvoyPluginList + plural: envoyplugins + singular: envoyplugin + scope: Namespaced + versions: + - name: v1alpha1 + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: pluginmanagers.microservice.slime.io +spec: + group: microservice.slime.io + names: + shortNames: + - plm + kind: PluginManager + listKind: PluginManagerList + plural: pluginmanagers + singular: pluginmanager + scope: Namespaced + versions: + - name: v1alpha1 + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: slimeboots.config.netease.com +spec: + group: config.netease.com + names: + kind: SlimeBoot + listKind: SlimeBootList + plural: slimeboots + singular: slimeboot + scope: Namespaced + subresources: + status: {} + validation: + openAPIV3Schema: + type: object + x-kubernetes-preserve-unknown-fields: true + versions: + - name: v1alpha1 + served: true + storage: true diff --git a/staging/src/slime.io/slime/modules/lazyload/test/e2e/testdata/install/init/deployment_slime-boot.yaml b/staging/src/slime.io/slime/modules/lazyload/test/e2e/testdata/install/init/deployment_slime-boot.yaml new file mode 100644 index 00000000..989d33c9 --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/test/e2e/testdata/install/init/deployment_slime-boot.yaml @@ -0,0 +1,63 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: slime-boot + namespace: mesh-operator +spec: + replicas: 1 + selector: + matchLabels: + name: slime-boot + template: + metadata: + labels: + name: slime-boot + spec: + serviceAccountName: slime-boot + containers: + - name: slime-boot + # Replace this with the built image name + image: docker.io/slimeio/slime-boot:{{slimebootTag}} + imagePullPolicy: Always + env: + - name: WATCH_NAMESPACE + value: "" + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: OPERATOR_NAME + value: "slime-boot" +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: slime-boot + namespace: mesh-operator +subjects: + - kind: ServiceAccount + name: slime-boot + namespace: mesh-operator +roleRef: + kind: Role + name: slime-boot + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: slime-boot + namespace: mesh-operator +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: slime-boot +subjects: + - kind: ServiceAccount + name: slime-boot + namespace: mesh-operator +roleRef: + kind: ClusterRole + name: cluster-admin + apiGroup: rbac.authorization.k8s.io diff --git a/staging/src/slime.io/slime/modules/lazyload/test/e2e/testdata/install/samples/lazyload/servicefence_productpage.yaml b/staging/src/slime.io/slime/modules/lazyload/test/e2e/testdata/install/samples/lazyload/servicefence_productpage.yaml new file mode 100644 index 00000000..8743d4cd --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/test/e2e/testdata/install/samples/lazyload/servicefence_productpage.yaml @@ -0,0 +1,10 @@ +--- +apiVersion: microservice.slime.io/v1alpha1 +kind: ServiceFence +metadata: + name: productpage + labels: + {{istioRevKey}}: {{istioRevValue}} + #namespace: default +spec: + enable: true diff --git a/staging/src/slime.io/slime/modules/lazyload/test/e2e/testdata/install/samples/lazyload/slimeboot_lazyload.yaml b/staging/src/slime.io/slime/modules/lazyload/test/e2e/testdata/install/samples/lazyload/slimeboot_lazyload.yaml new file mode 100644 index 00000000..4ea5ed5f --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/test/e2e/testdata/install/samples/lazyload/slimeboot_lazyload.yaml @@ -0,0 +1,56 @@ +--- +apiVersion: config.netease.com/v1alpha1 +kind: SlimeBoot +metadata: + name: lazyload + namespace: mesh-operator +spec: + image: + pullPolicy: Always + repository: docker.io/slimeio/slime-lazyload + tag: {{lazyloadTag}} + module: + - name: lazyload + enable: true + fence: + wormholePort: # replace to your application svc ports + - "9080" + metric: + prometheus: + address: http://prometheus.istio-system:9090 + handlers: + destination: + query: | + sum(istio_requests_total{source_app="$source_app",reporter="destination"})by(destination_service) + type: Group + global: + istioRev: {{istioRevValue}} + strictRev: {{strictRev}} + component: + globalSidecar: + enable: true + type: namespaced + namespace: + - example-apps # 替换为bookinfo安装的ns + resources: + requests: + cpu: 200m + memory: 200Mi + limits: + cpu: 400m + memory: 200Mi + image: + repository: istio/proxyv2 + tag: {{globalSidecarTag}} + pilot: + enable: true + resources: + requests: + cpu: 200m + memory: 200Mi + limits: + cpu: 400m + memory: 800Mi + image: + repository: docker.io/slimeio/pilot + tag: {{globalSidecarPilotTag}} diff --git a/staging/src/slime.io/slime/modules/lazyload/test/e2e/values.go b/staging/src/slime.io/slime/modules/lazyload/test/e2e/values.go new file mode 100644 index 00000000..1225f60a --- /dev/null +++ b/staging/src/slime.io/slime/modules/lazyload/test/e2e/values.go @@ -0,0 +1,37 @@ +package e2e + +// the values normally you do not need to change +var ( + testResourceToDelete []*TestResource + svfGroup = "microservice.slime.io" + svfVersion = "v1alpha1" + svfResource = "servicefences" + svfName = "productpage" + sidecarGroup = "networking.istio.io" + sidecarVersion = "v1beta1" + sidecarResource = "sidecars" + sidecarName = "productpage" + istioRevKey = "istio.io/rev" +) + +// the values you can change +// these are only example values, you can change them according to your situation +// you can also use env to override these default values avoiding changing values.go all the time, see func substituteValue in lazyload_test.go +// latest image tag link https://github.com/slime-io/slime/wiki/Slime-Project-Tag-and-Image-Tag-Mapping-Table +var ( + nsSlime = "mesh-operator" // namespace deployed slime + nsApps = "example-apps" // namespace deployed demo apps + test = "test/e2e/testdata/install" // testdata path + slimebootName = "slime-boot" // consistent with your deployment_slime-boot.yaml + istioRevValue = "1-10-2" // istio revision + slimebootTag = "v0.2.3-5bf313f" // slime-boot image tag, using the link above as reference + lazyloadTag = "master-2946c4a" // lazyload image tag, using the link above as reference + globalSidecarTag = "1.7.0" // global-sidecar image tag, using the link above as reference + globalSidecarPilotTag = "globalPilot-7.0-v0.0.3-713c611962" // global-sidecar-pilot image tag, using the link above as reference +) + +type TestResource struct { + Namespace string + Contents string + Selectors []string +} diff --git a/staging/src/slime.io/slime/modules/limiter/.idea/.gitignore b/staging/src/slime.io/slime/modules/limiter/.idea/.gitignore new file mode 100644 index 00000000..73f69e09 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/staging/src/slime.io/slime/modules/limiter/.idea/limiter.iml b/staging/src/slime.io/slime/modules/limiter/.idea/limiter.iml new file mode 100644 index 00000000..5e764c4f --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/.idea/limiter.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/staging/src/slime.io/slime/modules/limiter/.idea/modules.xml b/staging/src/slime.io/slime/modules/limiter/.idea/modules.xml new file mode 100644 index 00000000..52eb5e8a --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/staging/src/slime.io/slime/modules/limiter/.idea/vcs.xml b/staging/src/slime.io/slime/modules/limiter/.idea/vcs.xml new file mode 100644 index 00000000..94a25f7f --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/staging/src/slime.io/slime/modules/limiter/Dockerfile b/staging/src/slime.io/slime/modules/limiter/Dockerfile new file mode 100644 index 00000000..13899f90 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/Dockerfile @@ -0,0 +1,9 @@ +# Use distroless as minimal base image to package the manager binary +# Refer to https://github.com/GoogleContainerTools/distroless for more details +FROM ubuntu:bionic +WORKDIR / +COPY manager.exe ./manager + +ENTRYPOINT ["/manager"] + + diff --git a/staging/src/slime.io/slime/modules/limiter/README.md b/staging/src/slime.io/slime/modules/limiter/README.md new file mode 100644 index 00000000..ed303652 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/README.md @@ -0,0 +1,67 @@ +- [Overview](#overview) + - [Background](#background) + - [Features](#features) + - [Function](#function) + - [Thinking](#thinking) + - [Architecture](#architecture) + - [Sample](#sample) + - [Dependencies](#dependencies) +# Overview + +[中文](./README_ZH.md) + +## Background + +In Service Mesh, in order to configure rate limit for a service, users have to face an unusually complex `EnvoyFilter` rate limit configuration. To solve this problem, this project introduces `SmartLimiter`, which can automatically convert user-submitted `SmartLimiter` into `EnvoyFilter`. [Installation and Use](./document/smartlimiter.md#installation-and-usage) + +## Features + +1. easy to use, just submit `SmartLimiter` to achieve the purpose of service rate limit. +2. adaptive rate limit, dynamic triggering of rate limit rules according to `metric`. +3. Cover many scenarios, support global shared rate limit, global average rate limit, and single rate limit. + +## Function +1. single rate limit, each load of the service will have its own rate limit counter. +2. global shared rate limit, all loads of a service share a single rate limit counter. +3. global average rate limit, which distributes the rate limit counters equally among all loads. +see [function](./document/smartlimiter.md#smartlimiter) + +## Thinking + +To get users out of the tedious `EnvoyFilter` configuration, we define an easy `API` using `kubernetes` `CRD` mechanism, the `SmartLimiter` resource within `kubernetes`. After a user submits a `SmartLimiter` to a `kubernetes` cluster, the `SmartLimiter Controler` generates an `EnvoyFilter` in conjunction with the `SmartLimiter` spec and service metrics. + +## Architecture + +The main architecture of adaptive rate limit is divided into two parts, one part includes the logical transformation of `SmartLimiter` to `EnvoyFilter`, and the other part includes the acquisition of monitoring data within the cluster, including service metrics such as `CPU`, `Memory`, `POD` counts, etc., as detailed in [architecture]() + + + +## Sample + +When the total amount of `cpu` consumed by all loads of the `reviews` service is greater than 10, trigger a rate limit so that each load's port 9080 can only handle 10 requests per second, see [example](./document/smartlimiter.md#example) + +~~~yaml +apiVersion: microservice.slime.io/v1alpha2 +kind: SmartLimiter +metadata: + name: review + namespace: default +spec: + sets: + _base: + descriptor: + - action: + fill_interval: + seconds: 1 + quota: "10" + strategy: "single" + condition: "{{._base.cpu.sum}}>10" + target: + port: 9080 +~~~ + +## Dependencies + +1. In order to complete the adaptive function, we need to get the basic metrics of the service, so this service depends on `prometheus`, for details on how to build a simple `prometheus`, see [prometheus](./document/smartlimiter.md#installing-prometheus) +2. In order to complete the global shared rate limitation, we need a global counter, we introduced `RLS`, about `RLS` see [RLS](./document/smartlimiter.md#installing-rls--redis) + diff --git a/staging/src/slime.io/slime/modules/limiter/README_ZH.md b/staging/src/slime.io/slime/modules/limiter/README_ZH.md new file mode 100644 index 00000000..850249bd --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/README_ZH.md @@ -0,0 +1,67 @@ +- [自适应限流概述](#自适应限流概述) + - [背景](#背景) + - [特点](#特点) + - [功能](#功能) + - [思路](#思路) + - [架构](#架构) + - [样例](#样例) + - [依赖](#依赖) +# 自适应限流概述 + +[EN](./README.md) + +## 背景 + +在网格中,为了配置限流规则用户不得不面对复杂的`EnvoyFilter`配置,为了解决这个问题,我们们推出了自适应限流组件`slime/limiter`。用户只需要提交符合我们定义的`SmartLimiter`,即可完成灵活的服务限流要求。[安装和使用](./document/smartlimiter_zh.md#安装和使用) + +## 特点 + +1. 方便使用,只需提交`SmartLimiter`资源即可达到服务限流的目的。 +2. 自适应限流,根据`pod`的资源使用量动态的触发限流规则。 +3. 覆盖场景多,支持全局共享限流,全局均分限流,单机限流。 + + +## 功能 +1. 单机限流,每个pod单独的计数器 +2. 全局共享限流,所有pod共享一个全局计数器 +3. 全局均分限流,所有pod均分计数。 +[功能](./document/smartlimiter_zh.md#smartlimiter) + +## 思路 + +为了让用户从复杂的`EnvoyFilter`配置中脱离出来,我们利用`kubernetes`的`CRD`机制定义了一套简便的`API`,即`kubernetes`内的`SmartLimiter`资源。用户只需要按照`SmartLimiter`的规范提交一个`CR`,就会在集群中自动生成一个`EnvoyFilter` + + +## 架构 + +自适应限流的主体架构分为两个部分,一部分包括`SmartLimiter`到`EnvoyFilter`的逻辑转化,另一部分包括集群内监控数据的获取,包括服务的`CPU`, `Memory`,`POD`数量等数据。 + + + +## 样例 + +`SmartLimiter`的CRD定义的比较接近自然语义,例如,希望当`reviews`服务的`v1`版本的服务消耗`cpu`总量大于10的时候,触发限流,让其每个`POD`的9080端口的服务每秒钟只能处理10次请求。 [实践](./document/smartlimiter_zh.md#实践) + +~~~yaml +apiVersion: microservice.slime.io/v1alpha2 +kind: SmartLimiter +metadata: + name: review + namespace: default +spec: + sets: + v1: + descriptor: + - action: + fill_interval: + seconds: 1 + quota: "10" + strategy: "single" + condition: "{{.v1.cpu.sum}}>10" + target: + port: 9080 +~~~ + +## 依赖 +1. 依赖 `Prometheus` , [prometheus安装](./document/smartlimiter_zh.md#安装-prometheus) +2. 依赖 `RLS` ,可选,[RLS安装](./document/smartlimiter_zh.md#安装-rls--redis) diff --git a/staging/src/slime.io/slime/modules/limiter/api/v1alpha2/groupversion_info.go b/staging/src/slime.io/slime/modules/limiter/api/v1alpha2/groupversion_info.go new file mode 100644 index 00000000..081785f3 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/api/v1alpha2/groupversion_info.go @@ -0,0 +1,36 @@ +/* + + +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 v1alpha2 contains API Schema definitions for the microservice.slime.io v1alpha2 API group +// +kubebuilder:object:generate=true +// +groupName=microservice.slime.io +package v1alpha2 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "microservice.slime.io", Version: "v1alpha2"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/staging/src/slime.io/slime/modules/limiter/api/v1alpha2/limiter_module.pb.go b/staging/src/slime.io/slime/modules/limiter/api/v1alpha2/limiter_module.pb.go new file mode 100644 index 00000000..a6f29026 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/api/v1alpha2/limiter_module.pb.go @@ -0,0 +1,161 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: limiter_module.proto + +package v1alpha2 + +import ( + fmt "fmt" + math "math" + time "time" + + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + _ "github.com/gogo/protobuf/types" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = proto.Marshal + _ = fmt.Errorf + _ = math.Inf + _ = time.Kitchen +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type Limiter_RateLimitBackend int32 + +const ( + Limiter_netEaseLocalFlowControl Limiter_RateLimitBackend = 0 + Limiter_envoyLocalRateLimit Limiter_RateLimitBackend = 1 +) + +var Limiter_RateLimitBackend_name = map[int32]string{ + 0: "netEaseLocalFlowControl", + 1: "envoyLocalRateLimit", +} + +var Limiter_RateLimitBackend_value = map[string]int32{ + "netEaseLocalFlowControl": 0, + "envoyLocalRateLimit": 1, +} + +func (x Limiter_RateLimitBackend) String() string { + return proto.EnumName(Limiter_RateLimitBackend_name, int32(x)) +} + +func (Limiter_RateLimitBackend) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_4827d40f7d98bcf0, []int{0, 0} +} + +type Limiter struct { + Backend Limiter_RateLimitBackend `protobuf:"varint,3,opt,name=backend,proto3,enum=slime.microservice.limiter.v1alpha2.Limiter_RateLimitBackend" json:"backend,omitempty"` + Refresh *time.Duration `protobuf:"bytes,4,opt,name=refresh,proto3,stdduration" json:"refresh,omitempty"` + DisableGlobalRateLimit bool `protobuf:"varint,5,opt,name=disableGlobalRateLimit,proto3" json:"disableGlobalRateLimit,omitempty"` + DisableAdaptive bool `protobuf:"varint,6,opt,name=disableAdaptive,proto3" json:"disableAdaptive,omitempty"` + EnableServiceEntry bool `protobuf:"varint,7,opt,name=enableServiceEntry,proto3" json:"enableServiceEntry,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Limiter) Reset() { *m = Limiter{} } +func (m *Limiter) String() string { return proto.CompactTextString(m) } +func (*Limiter) ProtoMessage() {} +func (*Limiter) Descriptor() ([]byte, []int) { + return fileDescriptor_4827d40f7d98bcf0, []int{0} +} + +func (m *Limiter) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Limiter.Unmarshal(m, b) +} + +func (m *Limiter) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Limiter.Marshal(b, m, deterministic) +} + +func (m *Limiter) XXX_Merge(src proto.Message) { + xxx_messageInfo_Limiter.Merge(m, src) +} + +func (m *Limiter) XXX_Size() int { + return xxx_messageInfo_Limiter.Size(m) +} + +func (m *Limiter) XXX_DiscardUnknown() { + xxx_messageInfo_Limiter.DiscardUnknown(m) +} + +var xxx_messageInfo_Limiter proto.InternalMessageInfo + +func (m *Limiter) GetBackend() Limiter_RateLimitBackend { + if m != nil { + return m.Backend + } + return Limiter_netEaseLocalFlowControl +} + +func (m *Limiter) GetRefresh() *time.Duration { + if m != nil { + return m.Refresh + } + return nil +} + +func (m *Limiter) GetDisableGlobalRateLimit() bool { + if m != nil { + return m.DisableGlobalRateLimit + } + return false +} + +func (m *Limiter) GetDisableAdaptive() bool { + if m != nil { + return m.DisableAdaptive + } + return false +} + +func (m *Limiter) GetEnableServiceEntry() bool { + if m != nil { + return m.EnableServiceEntry + } + return false +} + +func init() { + proto.RegisterEnum("slime.microservice.limiter.v1alpha2.Limiter_RateLimitBackend", Limiter_RateLimitBackend_name, Limiter_RateLimitBackend_value) + proto.RegisterType((*Limiter)(nil), "slime.microservice.limiter.v1alpha2.Limiter") +} + +func init() { proto.RegisterFile("limiter_module.proto", fileDescriptor_4827d40f7d98bcf0) } + +var fileDescriptor_4827d40f7d98bcf0 = []byte{ + // 352 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x52, 0x41, 0x4b, 0xe3, 0x40, + 0x14, 0xde, 0xec, 0x76, 0x9b, 0x65, 0x16, 0x76, 0xcb, 0xec, 0x62, 0x63, 0x05, 0x2d, 0xf5, 0x12, + 0x90, 0x4e, 0xb0, 0x82, 0xe0, 0xc1, 0x83, 0xd5, 0xaa, 0x87, 0x9e, 0xe2, 0x41, 0xf0, 0x22, 0x93, + 0xe4, 0x35, 0x1d, 0x9c, 0xe4, 0x85, 0xc9, 0x24, 0xd2, 0x7f, 0xe2, 0xbf, 0xf3, 0x77, 0x78, 0x13, + 0x67, 0x92, 0x0a, 0x45, 0xc1, 0xdb, 0x9b, 0xf7, 0x7d, 0xdf, 0x7b, 0xdf, 0xfb, 0x18, 0xf2, 0x5f, + 0x8a, 0x4c, 0x68, 0x50, 0xf7, 0x19, 0x26, 0x95, 0x04, 0x56, 0x28, 0xd4, 0x48, 0xf7, 0x4b, 0x29, + 0x32, 0x60, 0x99, 0x88, 0x15, 0x96, 0xa0, 0x6a, 0x11, 0x03, 0x6b, 0x88, 0xac, 0x3e, 0xe4, 0xb2, + 0x58, 0xf2, 0xc9, 0x60, 0x9c, 0x0a, 0xbd, 0xac, 0x22, 0x16, 0x63, 0x16, 0xa4, 0x98, 0x62, 0x60, + 0xb4, 0x51, 0xb5, 0x30, 0x2f, 0xf3, 0x30, 0x95, 0x9d, 0x39, 0xd8, 0x4d, 0x11, 0x53, 0x09, 0xef, + 0xac, 0xa4, 0x52, 0x5c, 0x0b, 0xcc, 0x2d, 0x3e, 0x7a, 0xf9, 0x4e, 0xdc, 0xb9, 0xdd, 0x41, 0x6f, + 0x89, 0x1b, 0xf1, 0xf8, 0x01, 0xf2, 0xc4, 0xfb, 0x31, 0x74, 0xfc, 0x3f, 0x93, 0x53, 0xf6, 0x05, + 0x47, 0xac, 0x91, 0xb3, 0x90, 0x6b, 0x30, 0xf5, 0xd4, 0x0e, 0x09, 0xdb, 0x69, 0xf4, 0x84, 0xb8, + 0x0a, 0x16, 0x0a, 0xca, 0xa5, 0xd7, 0x19, 0x3a, 0xfe, 0xef, 0xc9, 0x36, 0xb3, 0xb6, 0x58, 0x6b, + 0x8b, 0x5d, 0x34, 0xb6, 0xa6, 0x9d, 0xa7, 0xe7, 0x3d, 0x27, 0x6c, 0xf9, 0xf4, 0x98, 0x6c, 0x25, + 0xa2, 0xe4, 0x91, 0x84, 0x2b, 0x89, 0x11, 0x97, 0xeb, 0x25, 0xde, 0xcf, 0xa1, 0xe3, 0xff, 0x0a, + 0x3f, 0x41, 0xa9, 0x4f, 0xfe, 0x36, 0xc8, 0x59, 0xc2, 0x0b, 0x2d, 0x6a, 0xf0, 0xba, 0x46, 0xb0, + 0xd9, 0xa6, 0x8c, 0x50, 0xc8, 0xdf, 0x3a, 0x37, 0xf6, 0xc0, 0x59, 0xae, 0xd5, 0xca, 0x73, 0x0d, + 0xf9, 0x03, 0x64, 0x74, 0x4d, 0x7a, 0x9b, 0x97, 0xd2, 0x1d, 0xd2, 0xcf, 0x41, 0xcf, 0x78, 0x09, + 0x73, 0x8c, 0xb9, 0xbc, 0x94, 0xf8, 0x78, 0x8e, 0xb9, 0x56, 0x28, 0x7b, 0xdf, 0x68, 0x9f, 0xfc, + 0x83, 0xbc, 0xc6, 0x95, 0x81, 0xd6, 0xd2, 0x9e, 0x33, 0x1d, 0xdf, 0x1d, 0xd8, 0x7c, 0x05, 0x06, + 0xa6, 0x08, 0xec, 0x77, 0x28, 0x83, 0x26, 0xe3, 0x80, 0x17, 0x22, 0x68, 0x73, 0x8e, 0xba, 0x26, + 0xac, 0xa3, 0xd7, 0x00, 0x00, 0x00, 0xff, 0xff, 0x35, 0xe3, 0xc2, 0x81, 0x3d, 0x02, 0x00, 0x00, +} diff --git a/staging/src/slime.io/slime/modules/limiter/api/v1alpha2/limiter_module.proto b/staging/src/slime.io/slime/modules/limiter/api/v1alpha2/limiter_module.proto new file mode 100644 index 00000000..c73a0665 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/api/v1alpha2/limiter_module.proto @@ -0,0 +1,24 @@ +/* +* @Author: wangchenyu +* @Date: 2022/2/23 +*/ + +syntax = "proto3"; +package slime.microservice.limiter.v1alpha2; + +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "google/protobuf/duration.proto"; + +option go_package = "slime.io/slime/modules/limiter/api/v1alpha2"; + +message Limiter { + enum RateLimitBackend { + netEaseLocalFlowControl = 0; + envoyLocalRateLimit = 1; + } + RateLimitBackend backend = 3; + google.protobuf.Duration refresh = 4 [(gogoproto.stdduration) = true]; + bool disableGlobalRateLimit = 5; + bool disableAdaptive = 6; + bool enableServiceEntry = 7; +} diff --git a/staging/src/slime.io/slime/modules/limiter/api/v1alpha2/smart_limiter.pb.go b/staging/src/slime.io/slime/modules/limiter/api/v1alpha2/smart_limiter.pb.go new file mode 100644 index 00000000..a527a9b2 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/api/v1alpha2/smart_limiter.pb.go @@ -0,0 +1,616 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: smart_limiter.proto + +package v1alpha2 + +import ( + fmt "fmt" + math "math" + + proto "github.com/gogo/protobuf/proto" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = proto.Marshal + _ = fmt.Errorf + _ = math.Inf +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type SmartLimiterSpec struct { + // subset rate-limit,the key is subset name. + Sets map[string]*SmartLimitDescriptors `protobuf:"bytes,1,rep,name=sets,proto3" json:"sets,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // rls service + Rls string `protobuf:"bytes,2,opt,name=rls,proto3" json:"rls,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SmartLimiterSpec) Reset() { *m = SmartLimiterSpec{} } +func (m *SmartLimiterSpec) String() string { return proto.CompactTextString(m) } +func (*SmartLimiterSpec) ProtoMessage() {} +func (*SmartLimiterSpec) Descriptor() ([]byte, []int) { + return fileDescriptor_452a0625a4f6276b, []int{0} +} + +func (m *SmartLimiterSpec) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SmartLimiterSpec.Unmarshal(m, b) +} + +func (m *SmartLimiterSpec) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SmartLimiterSpec.Marshal(b, m, deterministic) +} + +func (m *SmartLimiterSpec) XXX_Merge(src proto.Message) { + xxx_messageInfo_SmartLimiterSpec.Merge(m, src) +} + +func (m *SmartLimiterSpec) XXX_Size() int { + return xxx_messageInfo_SmartLimiterSpec.Size(m) +} + +func (m *SmartLimiterSpec) XXX_DiscardUnknown() { + xxx_messageInfo_SmartLimiterSpec.DiscardUnknown(m) +} + +var xxx_messageInfo_SmartLimiterSpec proto.InternalMessageInfo + +func (m *SmartLimiterSpec) GetSets() map[string]*SmartLimitDescriptors { + if m != nil { + return m.Sets + } + return nil +} + +func (m *SmartLimiterSpec) GetRls() string { + if m != nil { + return m.Rls + } + return "" +} + +type SmartLimiterStatus struct { + RatelimitStatus map[string]*SmartLimitDescriptors `protobuf:"bytes,1,rep,name=ratelimitStatus,proto3" json:"ratelimitStatus,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + MetricStatus map[string]string `protobuf:"bytes,2,rep,name=metricStatus,proto3" json:"metricStatus,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SmartLimiterStatus) Reset() { *m = SmartLimiterStatus{} } +func (m *SmartLimiterStatus) String() string { return proto.CompactTextString(m) } +func (*SmartLimiterStatus) ProtoMessage() {} +func (*SmartLimiterStatus) Descriptor() ([]byte, []int) { + return fileDescriptor_452a0625a4f6276b, []int{1} +} + +func (m *SmartLimiterStatus) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SmartLimiterStatus.Unmarshal(m, b) +} + +func (m *SmartLimiterStatus) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SmartLimiterStatus.Marshal(b, m, deterministic) +} + +func (m *SmartLimiterStatus) XXX_Merge(src proto.Message) { + xxx_messageInfo_SmartLimiterStatus.Merge(m, src) +} + +func (m *SmartLimiterStatus) XXX_Size() int { + return xxx_messageInfo_SmartLimiterStatus.Size(m) +} + +func (m *SmartLimiterStatus) XXX_DiscardUnknown() { + xxx_messageInfo_SmartLimiterStatus.DiscardUnknown(m) +} + +var xxx_messageInfo_SmartLimiterStatus proto.InternalMessageInfo + +func (m *SmartLimiterStatus) GetRatelimitStatus() map[string]*SmartLimitDescriptors { + if m != nil { + return m.RatelimitStatus + } + return nil +} + +func (m *SmartLimiterStatus) GetMetricStatus() map[string]string { + if m != nil { + return m.MetricStatus + } + return nil +} + +type SmartLimitDescriptor struct { + Condition string `protobuf:"bytes,1,opt,name=condition,proto3" json:"condition,omitempty"` + Action *SmartLimitDescriptor_Action `protobuf:"bytes,2,opt,name=action,proto3" json:"action,omitempty"` + Match []*SmartLimitDescriptor_HeaderMatcher `protobuf:"bytes,3,rep,name=match,proto3" json:"match,omitempty"` + Target *SmartLimitDescriptor_Target `protobuf:"bytes,4,opt,name=target,proto3" json:"target,omitempty"` + CustomKey string `protobuf:"bytes,5,opt,name=custom_key,json=customKey,proto3" json:"custom_key,omitempty"` + CustomValue string `protobuf:"bytes,6,opt,name=custom_value,json=customValue,proto3" json:"custom_value,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SmartLimitDescriptor) Reset() { *m = SmartLimitDescriptor{} } +func (m *SmartLimitDescriptor) String() string { return proto.CompactTextString(m) } +func (*SmartLimitDescriptor) ProtoMessage() {} +func (*SmartLimitDescriptor) Descriptor() ([]byte, []int) { + return fileDescriptor_452a0625a4f6276b, []int{2} +} + +func (m *SmartLimitDescriptor) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SmartLimitDescriptor.Unmarshal(m, b) +} + +func (m *SmartLimitDescriptor) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SmartLimitDescriptor.Marshal(b, m, deterministic) +} + +func (m *SmartLimitDescriptor) XXX_Merge(src proto.Message) { + xxx_messageInfo_SmartLimitDescriptor.Merge(m, src) +} + +func (m *SmartLimitDescriptor) XXX_Size() int { + return xxx_messageInfo_SmartLimitDescriptor.Size(m) +} + +func (m *SmartLimitDescriptor) XXX_DiscardUnknown() { + xxx_messageInfo_SmartLimitDescriptor.DiscardUnknown(m) +} + +var xxx_messageInfo_SmartLimitDescriptor proto.InternalMessageInfo + +func (m *SmartLimitDescriptor) GetCondition() string { + if m != nil { + return m.Condition + } + return "" +} + +func (m *SmartLimitDescriptor) GetAction() *SmartLimitDescriptor_Action { + if m != nil { + return m.Action + } + return nil +} + +func (m *SmartLimitDescriptor) GetMatch() []*SmartLimitDescriptor_HeaderMatcher { + if m != nil { + return m.Match + } + return nil +} + +func (m *SmartLimitDescriptor) GetTarget() *SmartLimitDescriptor_Target { + if m != nil { + return m.Target + } + return nil +} + +func (m *SmartLimitDescriptor) GetCustomKey() string { + if m != nil { + return m.CustomKey + } + return "" +} + +func (m *SmartLimitDescriptor) GetCustomValue() string { + if m != nil { + return m.CustomValue + } + return "" +} + +type SmartLimitDescriptor_HeaderMatcher struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // If specified, this regex string is a regular expression rule which implies the entire request + // header value must match the regex. The rule will not match if only a subsequence of the + // request header value matches the regex. + RegexMatch string `protobuf:"bytes,2,opt,name=regex_match,json=regexMatch,proto3" json:"regex_match,omitempty"` + // If specified, header match will be performed based on the value of the header. + ExactMatch string `protobuf:"bytes,3,opt,name=exact_match,json=exactMatch,proto3" json:"exact_match,omitempty"` + // * The prefix *abcd* matches the value *abcdxyz*, but not for *abcxyz*. + PrefixMatch string `protobuf:"bytes,4,opt,name=prefix_match,json=prefixMatch,proto3" json:"prefix_match,omitempty"` + // * The suffix *abcd* matches the value *xyzabcd*, but not for *xyzbcd*. + SuffixMatch string `protobuf:"bytes,5,opt,name=suffix_match,json=suffixMatch,proto3" json:"suffix_match,omitempty"` + // If specified as true, header match will be performed based on whether the header is in the + // request. If specified as false, header match will be performed based on whether the header is absent. + PresentMatch bool `protobuf:"varint,6,opt,name=present_match,json=presentMatch,proto3" json:"present_match,omitempty"` + // If specified, the match result will be inverted before checking. Defaults to false. + // * The regex ``\d{3}`` does not match the value *1234*, so it will match when inverted. + InvertMatch bool `protobuf:"varint,7,opt,name=invert_match,json=invertMatch,proto3" json:"invert_match,omitempty"` + // if specified, the exact match the value "" + IsExactMatchEmpty bool `protobuf:"varint,8,opt,name=is_exact_match_empty,json=isExactMatchEmpty,proto3" json:"is_exact_match_empty,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SmartLimitDescriptor_HeaderMatcher) Reset() { *m = SmartLimitDescriptor_HeaderMatcher{} } +func (m *SmartLimitDescriptor_HeaderMatcher) String() string { return proto.CompactTextString(m) } +func (*SmartLimitDescriptor_HeaderMatcher) ProtoMessage() {} +func (*SmartLimitDescriptor_HeaderMatcher) Descriptor() ([]byte, []int) { + return fileDescriptor_452a0625a4f6276b, []int{2, 0} +} + +func (m *SmartLimitDescriptor_HeaderMatcher) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SmartLimitDescriptor_HeaderMatcher.Unmarshal(m, b) +} + +func (m *SmartLimitDescriptor_HeaderMatcher) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SmartLimitDescriptor_HeaderMatcher.Marshal(b, m, deterministic) +} + +func (m *SmartLimitDescriptor_HeaderMatcher) XXX_Merge(src proto.Message) { + xxx_messageInfo_SmartLimitDescriptor_HeaderMatcher.Merge(m, src) +} + +func (m *SmartLimitDescriptor_HeaderMatcher) XXX_Size() int { + return xxx_messageInfo_SmartLimitDescriptor_HeaderMatcher.Size(m) +} + +func (m *SmartLimitDescriptor_HeaderMatcher) XXX_DiscardUnknown() { + xxx_messageInfo_SmartLimitDescriptor_HeaderMatcher.DiscardUnknown(m) +} + +var xxx_messageInfo_SmartLimitDescriptor_HeaderMatcher proto.InternalMessageInfo + +func (m *SmartLimitDescriptor_HeaderMatcher) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *SmartLimitDescriptor_HeaderMatcher) GetRegexMatch() string { + if m != nil { + return m.RegexMatch + } + return "" +} + +func (m *SmartLimitDescriptor_HeaderMatcher) GetExactMatch() string { + if m != nil { + return m.ExactMatch + } + return "" +} + +func (m *SmartLimitDescriptor_HeaderMatcher) GetPrefixMatch() string { + if m != nil { + return m.PrefixMatch + } + return "" +} + +func (m *SmartLimitDescriptor_HeaderMatcher) GetSuffixMatch() string { + if m != nil { + return m.SuffixMatch + } + return "" +} + +func (m *SmartLimitDescriptor_HeaderMatcher) GetPresentMatch() bool { + if m != nil { + return m.PresentMatch + } + return false +} + +func (m *SmartLimitDescriptor_HeaderMatcher) GetInvertMatch() bool { + if m != nil { + return m.InvertMatch + } + return false +} + +func (m *SmartLimitDescriptor_HeaderMatcher) GetIsExactMatchEmpty() bool { + if m != nil { + return m.IsExactMatchEmpty + } + return false +} + +type SmartLimitDescriptor_Action struct { + Quota string `protobuf:"bytes,1,opt,name=quota,proto3" json:"quota,omitempty"` + FillInterval *Duration `protobuf:"bytes,2,opt,name=fill_interval,json=fillInterval,proto3" json:"fill_interval,omitempty"` + Strategy string `protobuf:"bytes,3,opt,name=strategy,proto3" json:"strategy,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SmartLimitDescriptor_Action) Reset() { *m = SmartLimitDescriptor_Action{} } +func (m *SmartLimitDescriptor_Action) String() string { return proto.CompactTextString(m) } +func (*SmartLimitDescriptor_Action) ProtoMessage() {} +func (*SmartLimitDescriptor_Action) Descriptor() ([]byte, []int) { + return fileDescriptor_452a0625a4f6276b, []int{2, 1} +} + +func (m *SmartLimitDescriptor_Action) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SmartLimitDescriptor_Action.Unmarshal(m, b) +} + +func (m *SmartLimitDescriptor_Action) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SmartLimitDescriptor_Action.Marshal(b, m, deterministic) +} + +func (m *SmartLimitDescriptor_Action) XXX_Merge(src proto.Message) { + xxx_messageInfo_SmartLimitDescriptor_Action.Merge(m, src) +} + +func (m *SmartLimitDescriptor_Action) XXX_Size() int { + return xxx_messageInfo_SmartLimitDescriptor_Action.Size(m) +} + +func (m *SmartLimitDescriptor_Action) XXX_DiscardUnknown() { + xxx_messageInfo_SmartLimitDescriptor_Action.DiscardUnknown(m) +} + +var xxx_messageInfo_SmartLimitDescriptor_Action proto.InternalMessageInfo + +func (m *SmartLimitDescriptor_Action) GetQuota() string { + if m != nil { + return m.Quota + } + return "" +} + +func (m *SmartLimitDescriptor_Action) GetFillInterval() *Duration { + if m != nil { + return m.FillInterval + } + return nil +} + +func (m *SmartLimitDescriptor_Action) GetStrategy() string { + if m != nil { + return m.Strategy + } + return "" +} + +type SmartLimitDescriptor_Target struct { + Direction string `protobuf:"bytes,1,opt,name=direction,proto3" json:"direction,omitempty"` + Port int32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` + Route []string `protobuf:"bytes,3,rep,name=route,proto3" json:"route,omitempty"` + Host []string `protobuf:"bytes,4,rep,name=host,proto3" json:"host,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SmartLimitDescriptor_Target) Reset() { *m = SmartLimitDescriptor_Target{} } +func (m *SmartLimitDescriptor_Target) String() string { return proto.CompactTextString(m) } +func (*SmartLimitDescriptor_Target) ProtoMessage() {} +func (*SmartLimitDescriptor_Target) Descriptor() ([]byte, []int) { + return fileDescriptor_452a0625a4f6276b, []int{2, 2} +} + +func (m *SmartLimitDescriptor_Target) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SmartLimitDescriptor_Target.Unmarshal(m, b) +} + +func (m *SmartLimitDescriptor_Target) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SmartLimitDescriptor_Target.Marshal(b, m, deterministic) +} + +func (m *SmartLimitDescriptor_Target) XXX_Merge(src proto.Message) { + xxx_messageInfo_SmartLimitDescriptor_Target.Merge(m, src) +} + +func (m *SmartLimitDescriptor_Target) XXX_Size() int { + return xxx_messageInfo_SmartLimitDescriptor_Target.Size(m) +} + +func (m *SmartLimitDescriptor_Target) XXX_DiscardUnknown() { + xxx_messageInfo_SmartLimitDescriptor_Target.DiscardUnknown(m) +} + +var xxx_messageInfo_SmartLimitDescriptor_Target proto.InternalMessageInfo + +func (m *SmartLimitDescriptor_Target) GetDirection() string { + if m != nil { + return m.Direction + } + return "" +} + +func (m *SmartLimitDescriptor_Target) GetPort() int32 { + if m != nil { + return m.Port + } + return 0 +} + +func (m *SmartLimitDescriptor_Target) GetRoute() []string { + if m != nil { + return m.Route + } + return nil +} + +func (m *SmartLimitDescriptor_Target) GetHost() []string { + if m != nil { + return m.Host + } + return nil +} + +type SmartLimitDescriptors struct { + // Description of current rate-limit + Descriptor_ []*SmartLimitDescriptor `protobuf:"bytes,1,rep,name=descriptor,proto3" json:"descriptor,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SmartLimitDescriptors) Reset() { *m = SmartLimitDescriptors{} } +func (m *SmartLimitDescriptors) String() string { return proto.CompactTextString(m) } +func (*SmartLimitDescriptors) ProtoMessage() {} +func (*SmartLimitDescriptors) Descriptor() ([]byte, []int) { + return fileDescriptor_452a0625a4f6276b, []int{3} +} + +func (m *SmartLimitDescriptors) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SmartLimitDescriptors.Unmarshal(m, b) +} + +func (m *SmartLimitDescriptors) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SmartLimitDescriptors.Marshal(b, m, deterministic) +} + +func (m *SmartLimitDescriptors) XXX_Merge(src proto.Message) { + xxx_messageInfo_SmartLimitDescriptors.Merge(m, src) +} + +func (m *SmartLimitDescriptors) XXX_Size() int { + return xxx_messageInfo_SmartLimitDescriptors.Size(m) +} + +func (m *SmartLimitDescriptors) XXX_DiscardUnknown() { + xxx_messageInfo_SmartLimitDescriptors.DiscardUnknown(m) +} + +var xxx_messageInfo_SmartLimitDescriptors proto.InternalMessageInfo + +func (m *SmartLimitDescriptors) GetDescriptor_() []*SmartLimitDescriptor { + if m != nil { + return m.Descriptor_ + } + return nil +} + +type Duration struct { + // Signed seconds of the span of time. Must be from -315,576,000,000 + // to +315,576,000,000 inclusive. Note: these bounds are computed from: + // 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years + Seconds int64 `protobuf:"varint,1,opt,name=seconds,proto3" json:"seconds,omitempty"` + // Signed fractions of a second at nanosecond resolution of the span + // of time. Durations less than one second are represented with a 0 + // `seconds` field and a positive or negative `nanos` field. For durations + // of one second or more, a non-zero value for the `nanos` field must be + // of the same sign as the `seconds` field. Must be from -999,999,999 + // to +999,999,999 inclusive. + Nanos int32 `protobuf:"varint,2,opt,name=nanos,proto3" json:"nanos,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Duration) Reset() { *m = Duration{} } +func (m *Duration) String() string { return proto.CompactTextString(m) } +func (*Duration) ProtoMessage() {} +func (*Duration) Descriptor() ([]byte, []int) { + return fileDescriptor_452a0625a4f6276b, []int{4} +} + +func (m *Duration) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Duration.Unmarshal(m, b) +} + +func (m *Duration) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Duration.Marshal(b, m, deterministic) +} + +func (m *Duration) XXX_Merge(src proto.Message) { + xxx_messageInfo_Duration.Merge(m, src) +} + +func (m *Duration) XXX_Size() int { + return xxx_messageInfo_Duration.Size(m) +} + +func (m *Duration) XXX_DiscardUnknown() { + xxx_messageInfo_Duration.DiscardUnknown(m) +} + +var xxx_messageInfo_Duration proto.InternalMessageInfo + +func (m *Duration) GetSeconds() int64 { + if m != nil { + return m.Seconds + } + return 0 +} + +func (m *Duration) GetNanos() int32 { + if m != nil { + return m.Nanos + } + return 0 +} + +func init() { + proto.RegisterType((*SmartLimiterSpec)(nil), "slime.microservice.limiter.v1alpha2.SmartLimiterSpec") + proto.RegisterMapType((map[string]*SmartLimitDescriptors)(nil), "slime.microservice.limiter.v1alpha2.SmartLimiterSpec.SetsEntry") + proto.RegisterType((*SmartLimiterStatus)(nil), "slime.microservice.limiter.v1alpha2.SmartLimiterStatus") + proto.RegisterMapType((map[string]string)(nil), "slime.microservice.limiter.v1alpha2.SmartLimiterStatus.MetricStatusEntry") + proto.RegisterMapType((map[string]*SmartLimitDescriptors)(nil), "slime.microservice.limiter.v1alpha2.SmartLimiterStatus.RatelimitStatusEntry") + proto.RegisterType((*SmartLimitDescriptor)(nil), "slime.microservice.limiter.v1alpha2.SmartLimitDescriptor") + proto.RegisterType((*SmartLimitDescriptor_HeaderMatcher)(nil), "slime.microservice.limiter.v1alpha2.SmartLimitDescriptor.HeaderMatcher") + proto.RegisterType((*SmartLimitDescriptor_Action)(nil), "slime.microservice.limiter.v1alpha2.SmartLimitDescriptor.Action") + proto.RegisterType((*SmartLimitDescriptor_Target)(nil), "slime.microservice.limiter.v1alpha2.SmartLimitDescriptor.Target") + proto.RegisterType((*SmartLimitDescriptors)(nil), "slime.microservice.limiter.v1alpha2.SmartLimitDescriptors") + proto.RegisterType((*Duration)(nil), "slime.microservice.limiter.v1alpha2.Duration") +} + +func init() { proto.RegisterFile("smart_limiter.proto", fileDescriptor_452a0625a4f6276b) } + +var fileDescriptor_452a0625a4f6276b = []byte{ + // 707 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x55, 0xdd, 0x4e, 0x13, 0x4d, + 0x18, 0x4e, 0x7f, 0x69, 0xdf, 0x96, 0x7c, 0x30, 0x5f, 0x4d, 0x9a, 0x8d, 0x46, 0x28, 0x27, 0x9c, + 0xb0, 0x0d, 0x70, 0xa2, 0x9c, 0xa0, 0x06, 0xa2, 0x44, 0x48, 0xcc, 0xd6, 0x18, 0x35, 0x31, 0x9b, + 0x71, 0xfb, 0x02, 0x13, 0x76, 0x77, 0xd6, 0x99, 0xd9, 0x86, 0x9e, 0x78, 0x09, 0x5e, 0x85, 0x89, + 0x37, 0xe1, 0x35, 0x79, 0x0d, 0x66, 0x7e, 0x76, 0x69, 0xb1, 0x07, 0x50, 0x13, 0x4f, 0x9a, 0x99, + 0x67, 0x9e, 0x79, 0xde, 0xe7, 0xfd, 0xd9, 0x29, 0xfc, 0x2f, 0x13, 0x2a, 0x54, 0x18, 0xb3, 0x84, + 0x29, 0x14, 0x7e, 0x26, 0xb8, 0xe2, 0x64, 0x4b, 0xc6, 0x2c, 0x41, 0x3f, 0x61, 0x91, 0xe0, 0x12, + 0xc5, 0x84, 0x45, 0xe8, 0x17, 0x8c, 0xc9, 0x2e, 0x8d, 0xb3, 0x4b, 0xba, 0x37, 0xf8, 0x55, 0x81, + 0xb5, 0x91, 0xbe, 0x7c, 0x6a, 0x4f, 0x46, 0x19, 0x46, 0x64, 0x04, 0x75, 0x89, 0x4a, 0xf6, 0x2b, + 0x1b, 0xb5, 0xed, 0xce, 0xde, 0xa1, 0x7f, 0x07, 0x21, 0xff, 0xb6, 0x88, 0x3f, 0x42, 0x25, 0x8f, + 0x53, 0x25, 0xa6, 0x81, 0x11, 0x23, 0x6b, 0x50, 0x13, 0xb1, 0xec, 0x57, 0x37, 0x2a, 0xdb, 0xed, + 0x40, 0x2f, 0x3d, 0x09, 0xed, 0x92, 0xa4, 0x8f, 0xaf, 0x70, 0xda, 0xaf, 0xd8, 0xe3, 0x2b, 0x9c, + 0x92, 0x37, 0xd0, 0x98, 0xd0, 0x38, 0x47, 0x73, 0xa5, 0xb3, 0x77, 0x70, 0x4f, 0x1b, 0x47, 0x28, + 0x23, 0xc1, 0x32, 0xc5, 0x85, 0x0c, 0xac, 0xd0, 0x41, 0xf5, 0x49, 0x65, 0xf0, 0xb3, 0x06, 0x64, + 0xce, 0xab, 0xa2, 0x2a, 0x97, 0x64, 0x02, 0xff, 0x09, 0xaa, 0xd0, 0xe8, 0x59, 0xc8, 0x65, 0x7f, + 0x7a, 0xff, 0xec, 0xcd, 0x75, 0x3f, 0x98, 0x97, 0xb3, 0xa5, 0xb8, 0x1d, 0x84, 0x24, 0xd0, 0x4d, + 0x50, 0x09, 0x16, 0xb9, 0xa0, 0x55, 0x13, 0xf4, 0x64, 0xd9, 0xa0, 0x67, 0x33, 0x5a, 0x36, 0xe2, + 0x9c, 0xbc, 0xf7, 0x15, 0x7a, 0x8b, 0x7c, 0xfd, 0xab, 0xea, 0x7b, 0x87, 0xb0, 0xfe, 0x87, 0xc5, + 0x05, 0xc1, 0x7b, 0xb3, 0xc1, 0xdb, 0xb3, 0xed, 0xfb, 0xb1, 0x02, 0xbd, 0x45, 0x51, 0xc8, 0x43, + 0x68, 0x47, 0x3c, 0x1d, 0x33, 0xc5, 0x78, 0xea, 0xa4, 0x6e, 0x00, 0xf2, 0x1e, 0x9a, 0x34, 0x32, + 0x47, 0x36, 0x9d, 0x67, 0x4b, 0xa7, 0xe3, 0x3f, 0x37, 0x3a, 0x81, 0xd3, 0x23, 0x9f, 0xa0, 0x91, + 0x50, 0x15, 0x5d, 0xf6, 0x6b, 0xa6, 0x73, 0x2f, 0x97, 0x17, 0x7e, 0x85, 0x74, 0x8c, 0xe2, 0x4c, + 0x8b, 0xa1, 0x08, 0xac, 0xaa, 0x36, 0xae, 0xa8, 0xb8, 0x40, 0xd5, 0xaf, 0xff, 0xad, 0xf1, 0xb7, + 0x46, 0x27, 0x70, 0x7a, 0xe4, 0x11, 0x40, 0x94, 0x4b, 0xc5, 0x93, 0x50, 0x17, 0xbf, 0xe1, 0x2a, + 0x66, 0x90, 0xd7, 0x38, 0x25, 0x9b, 0xd0, 0x75, 0xc7, 0xb6, 0x13, 0x4d, 0x43, 0xe8, 0x58, 0xec, + 0x9d, 0x86, 0xbc, 0xef, 0x55, 0x58, 0x9d, 0x33, 0x4d, 0x08, 0xd4, 0x53, 0x9a, 0xa0, 0xab, 0xbf, + 0x59, 0x93, 0xc7, 0xd0, 0x11, 0x78, 0x81, 0xd7, 0xa1, 0x2d, 0x93, 0xed, 0x28, 0x18, 0xc8, 0x5c, + 0xd3, 0x04, 0xbc, 0xa6, 0x91, 0x0a, 0x8b, 0x3a, 0x1a, 0x82, 0x81, 0x2c, 0x61, 0x13, 0xba, 0x99, + 0xc0, 0x73, 0x56, 0x48, 0xd4, 0xad, 0x15, 0x8b, 0x95, 0x14, 0x99, 0x9f, 0xdf, 0x50, 0x6c, 0x3a, + 0x1d, 0x8b, 0x59, 0xca, 0x16, 0xac, 0x66, 0x02, 0x25, 0xa6, 0x45, 0x20, 0x9d, 0x51, 0x2b, 0xe8, + 0x3a, 0xb0, 0xd4, 0x61, 0xe9, 0x04, 0x45, 0xc1, 0x59, 0x31, 0x9c, 0x8e, 0xc5, 0x2c, 0x65, 0x08, + 0x3d, 0x26, 0xc3, 0x19, 0xc7, 0x21, 0x26, 0x99, 0x9a, 0xf6, 0x5b, 0x86, 0xba, 0xce, 0xe4, 0x71, + 0xe9, 0xfc, 0x58, 0x1f, 0x78, 0xdf, 0x2a, 0xd0, 0xb4, 0x43, 0xa3, 0xe7, 0xfa, 0x4b, 0xce, 0x15, + 0x75, 0x05, 0xb2, 0x1b, 0x12, 0xc0, 0xea, 0x39, 0x8b, 0xe3, 0x90, 0xa5, 0x0a, 0xc5, 0x84, 0xc6, + 0x6e, 0x46, 0x77, 0xee, 0xd4, 0xea, 0xa3, 0x5c, 0x50, 0x33, 0x90, 0x5d, 0xad, 0x71, 0xe2, 0x24, + 0x88, 0x07, 0x2d, 0xa9, 0xf4, 0x63, 0x73, 0x31, 0x75, 0x15, 0x2d, 0xf7, 0xde, 0x18, 0x9a, 0x76, + 0x16, 0xf4, 0x47, 0x33, 0x66, 0x02, 0xa3, 0xd9, 0x8f, 0xa6, 0x04, 0x74, 0x37, 0x33, 0x2e, 0x94, + 0xb1, 0xd3, 0x08, 0xcc, 0x5a, 0x67, 0x20, 0x78, 0xae, 0xd0, 0x8c, 0x7b, 0x3b, 0xb0, 0x1b, 0xcd, + 0xbc, 0xe4, 0x52, 0xcf, 0xa8, 0x06, 0xcd, 0x7a, 0x20, 0xe0, 0xc1, 0xc2, 0xe7, 0x80, 0x7c, 0x00, + 0x18, 0x97, 0x5b, 0xf7, 0xca, 0x3e, 0x5d, 0x7a, 0xac, 0x83, 0x19, 0xb1, 0xc1, 0x01, 0xb4, 0x8a, + 0x7a, 0x90, 0x3e, 0xac, 0x48, 0xd4, 0x2f, 0x80, 0x34, 0x99, 0xd5, 0x82, 0x62, 0xab, 0x73, 0x48, + 0x69, 0xca, 0xa5, 0x4b, 0xcc, 0x6e, 0x5e, 0xec, 0x7f, 0xdc, 0xb5, 0x1e, 0x18, 0x1f, 0x9a, 0x85, + 0xfd, 0xdd, 0x49, 0xf8, 0x38, 0x8f, 0x51, 0x0e, 0x9d, 0x9b, 0x21, 0xcd, 0xd8, 0xb0, 0x70, 0xf4, + 0xb9, 0x69, 0xfe, 0x6a, 0xf7, 0x7f, 0x07, 0x00, 0x00, 0xff, 0xff, 0xf9, 0x52, 0x72, 0xe3, 0x81, + 0x07, 0x00, 0x00, +} diff --git a/staging/src/slime.io/slime/modules/limiter/api/v1alpha2/smart_limiter.proto b/staging/src/slime.io/slime/modules/limiter/api/v1alpha2/smart_limiter.proto new file mode 100644 index 00000000..f7339b4c --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/api/v1alpha2/smart_limiter.proto @@ -0,0 +1,139 @@ +/* +* @Author: yangdihang +* @Date: 2020/5/21 +*/ + +syntax = "proto3"; +package slime.microservice.limiter.v1alpha2; + +option go_package = "slime.io/slime/slime-modules/limiter/api/v1alpha2"; + +//apiVersion: microservice.slime.io/v1alpha2 +//kind: SmartLimiter +//metadata: +// name: b +// namespace: powerful +//spec: +// rls: "" +// sets: +// _base: +// descriptor: +// - action: +// fill_interval: +// seconds: 60 +// quota: "10" +// strategy: "single" +// conditon: "true" +// target: +// port: 9080 + + +//apiVersion: microservice.slime.io/v1alpha2 +//kind: SmartLimiter +//metadata: +// name: b +// namespace: powerful +//spec: +// rls: "" +// sets: +// _base: +// descriptor: +// - action: +// fill_interval: +// seconds: 60 +// quota: "10" +// strategy: "single" +// conditon: "true" +// target: +// direction: outbound +// route: a.test.com:80/r1 + + +message SmartLimiterSpec { + // subset rate-limit,the key is subset name. + map sets = 1; + // rls service + string rls = 2; // rls 服务地址 +} + +message SmartLimiterStatus { + map ratelimitStatus = 1; + map metricStatus = 2; +} + +message SmartLimitDescriptor { + message HeaderMatcher { + string name = 1; + // If specified, this regex string is a regular expression rule which implies the entire request + // header value must match the regex. The rule will not match if only a subsequence of the + // request header value matches the regex. + string regex_match = 2; + + // If specified, header match will be performed based on the value of the header. + string exact_match = 3; + + // * The prefix *abcd* matches the value *abcdxyz*, but not for *abcxyz*. + string prefix_match = 4; + + // * The suffix *abcd* matches the value *xyzabcd*, but not for *xyzbcd*. + string suffix_match = 5; + + // If specified as true, header match will be performed based on whether the header is in the + // request. If specified as false, header match will be performed based on whether the header is absent. + bool present_match = 6; + + // If specified, the match result will be inverted before checking. Defaults to false. + // * The regex ``\d{3}`` does not match the value *1234*, so it will match when inverted. + bool invert_match = 7; + + // if specified, the exact match the value "" + bool is_exact_match_empty = 8; + } + + message Action { + string quota = 1; // 配额 + Duration fill_interval = 2; // 时间 + string strategy= 3; // 策略 + } + + message Target { + string direction = 1; // 进出 + int32 port = 2; + repeated string route = 3; + repeated string host = 4; + } + + string condition = 1; + + Action action = 2; + + repeated HeaderMatcher match = 3; + + Target target = 4; + + string custom_key = 5; + + string custom_value = 6; +} + +message SmartLimitDescriptors { + // Description of current rate-limit + repeated SmartLimitDescriptor descriptor = 1; +} + +message Duration { + + // Signed seconds of the span of time. Must be from -315,576,000,000 + // to +315,576,000,000 inclusive. Note: these bounds are computed from: + // 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years + int64 seconds = 1; + + // Signed fractions of a second at nanosecond resolution of the span + // of time. Durations less than one second are represented with a 0 + // `seconds` field and a positive or negative `nanos` field. For durations + // of one second or more, a non-zero value for the `nanos` field must be + // of the same sign as the `seconds` field. Must be from -999,999,999 + // to +999,999,999 inclusive. + int32 nanos = 2; +} + diff --git a/staging/src/slime.io/slime/modules/limiter/api/v1alpha2/smartlimiter_types.go b/staging/src/slime.io/slime/modules/limiter/api/v1alpha2/smartlimiter_types.go new file mode 100644 index 00000000..54b37ef5 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/api/v1alpha2/smartlimiter_types.go @@ -0,0 +1,45 @@ +/* + + +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 v1alpha2 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +kubebuilder:object:root=true + +// SmartLimiter is the Schema for the smartlimiters API +type SmartLimiter struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec SmartLimiterSpec `json:"spec,omitempty"` + Status SmartLimiterStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// SmartLimiterList contains a list of SmartLimiter +type SmartLimiterList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []SmartLimiter `json:"items"` +} + +func init() { + SchemeBuilder.Register(&SmartLimiter{}, &SmartLimiterList{}) +} diff --git a/staging/src/slime.io/slime/modules/limiter/api/v1alpha2/zz_generated.deepcopy.go b/staging/src/slime.io/slime/modules/limiter/api/v1alpha2/zz_generated.deepcopy.go new file mode 100644 index 00000000..cfd8f0bb --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/api/v1alpha2/zz_generated.deepcopy.go @@ -0,0 +1,339 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + timex "time" + + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Duration) DeepCopyInto(out *Duration) { + *out = *in + out.XXX_NoUnkeyedLiteral = in.XXX_NoUnkeyedLiteral + if in.XXX_unrecognized != nil { + in, out := &in.XXX_unrecognized, &out.XXX_unrecognized + *out = make([]byte, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Duration. +func (in *Duration) DeepCopy() *Duration { + if in == nil { + return nil + } + out := new(Duration) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Limiter) DeepCopyInto(out *Limiter) { + *out = *in + if in.Refresh != nil { + in, out := &in.Refresh, &out.Refresh + *out = new(timex.Duration) + **out = **in + } + out.XXX_NoUnkeyedLiteral = in.XXX_NoUnkeyedLiteral + if in.XXX_unrecognized != nil { + in, out := &in.XXX_unrecognized, &out.XXX_unrecognized + *out = make([]byte, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Limiter. +func (in *Limiter) DeepCopy() *Limiter { + if in == nil { + return nil + } + out := new(Limiter) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SmartLimitDescriptor) DeepCopyInto(out *SmartLimitDescriptor) { + *out = *in + if in.Action != nil { + in, out := &in.Action, &out.Action + *out = new(SmartLimitDescriptor_Action) + (*in).DeepCopyInto(*out) + } + if in.Match != nil { + in, out := &in.Match, &out.Match + *out = make([]*SmartLimitDescriptor_HeaderMatcher, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(SmartLimitDescriptor_HeaderMatcher) + (*in).DeepCopyInto(*out) + } + } + } + if in.Target != nil { + in, out := &in.Target, &out.Target + *out = new(SmartLimitDescriptor_Target) + (*in).DeepCopyInto(*out) + } + out.XXX_NoUnkeyedLiteral = in.XXX_NoUnkeyedLiteral + if in.XXX_unrecognized != nil { + in, out := &in.XXX_unrecognized, &out.XXX_unrecognized + *out = make([]byte, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SmartLimitDescriptor. +func (in *SmartLimitDescriptor) DeepCopy() *SmartLimitDescriptor { + if in == nil { + return nil + } + out := new(SmartLimitDescriptor) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SmartLimitDescriptor_Action) DeepCopyInto(out *SmartLimitDescriptor_Action) { + *out = *in + if in.FillInterval != nil { + in, out := &in.FillInterval, &out.FillInterval + *out = new(Duration) + (*in).DeepCopyInto(*out) + } + out.XXX_NoUnkeyedLiteral = in.XXX_NoUnkeyedLiteral + if in.XXX_unrecognized != nil { + in, out := &in.XXX_unrecognized, &out.XXX_unrecognized + *out = make([]byte, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SmartLimitDescriptor_Action. +func (in *SmartLimitDescriptor_Action) DeepCopy() *SmartLimitDescriptor_Action { + if in == nil { + return nil + } + out := new(SmartLimitDescriptor_Action) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SmartLimitDescriptor_HeaderMatcher) DeepCopyInto(out *SmartLimitDescriptor_HeaderMatcher) { + *out = *in + out.XXX_NoUnkeyedLiteral = in.XXX_NoUnkeyedLiteral + if in.XXX_unrecognized != nil { + in, out := &in.XXX_unrecognized, &out.XXX_unrecognized + *out = make([]byte, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SmartLimitDescriptor_HeaderMatcher. +func (in *SmartLimitDescriptor_HeaderMatcher) DeepCopy() *SmartLimitDescriptor_HeaderMatcher { + if in == nil { + return nil + } + out := new(SmartLimitDescriptor_HeaderMatcher) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SmartLimitDescriptor_Target) DeepCopyInto(out *SmartLimitDescriptor_Target) { + *out = *in + out.XXX_NoUnkeyedLiteral = in.XXX_NoUnkeyedLiteral + if in.XXX_unrecognized != nil { + in, out := &in.XXX_unrecognized, &out.XXX_unrecognized + *out = make([]byte, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SmartLimitDescriptor_Target. +func (in *SmartLimitDescriptor_Target) DeepCopy() *SmartLimitDescriptor_Target { + if in == nil { + return nil + } + out := new(SmartLimitDescriptor_Target) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SmartLimitDescriptors) DeepCopyInto(out *SmartLimitDescriptors) { + *out = *in + if in.Descriptor_ != nil { + in, out := &in.Descriptor_, &out.Descriptor_ + *out = make([]*SmartLimitDescriptor, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(SmartLimitDescriptor) + (*in).DeepCopyInto(*out) + } + } + } + out.XXX_NoUnkeyedLiteral = in.XXX_NoUnkeyedLiteral + if in.XXX_unrecognized != nil { + in, out := &in.XXX_unrecognized, &out.XXX_unrecognized + *out = make([]byte, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SmartLimitDescriptors. +func (in *SmartLimitDescriptors) DeepCopy() *SmartLimitDescriptors { + if in == nil { + return nil + } + out := new(SmartLimitDescriptors) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SmartLimiter) DeepCopyInto(out *SmartLimiter) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SmartLimiter. +func (in *SmartLimiter) DeepCopy() *SmartLimiter { + if in == nil { + return nil + } + out := new(SmartLimiter) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *SmartLimiter) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SmartLimiterList) DeepCopyInto(out *SmartLimiterList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]SmartLimiter, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SmartLimiterList. +func (in *SmartLimiterList) DeepCopy() *SmartLimiterList { + if in == nil { + return nil + } + out := new(SmartLimiterList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *SmartLimiterList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SmartLimiterSpec) DeepCopyInto(out *SmartLimiterSpec) { + *out = *in + if in.Sets != nil { + in, out := &in.Sets, &out.Sets + *out = make(map[string]*SmartLimitDescriptors, len(*in)) + for key, val := range *in { + var outVal *SmartLimitDescriptors + if val == nil { + (*out)[key] = nil + } else { + in, out := &val, &outVal + *out = new(SmartLimitDescriptors) + (*in).DeepCopyInto(*out) + } + (*out)[key] = outVal + } + } + out.XXX_NoUnkeyedLiteral = in.XXX_NoUnkeyedLiteral + if in.XXX_unrecognized != nil { + in, out := &in.XXX_unrecognized, &out.XXX_unrecognized + *out = make([]byte, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SmartLimiterSpec. +func (in *SmartLimiterSpec) DeepCopy() *SmartLimiterSpec { + if in == nil { + return nil + } + out := new(SmartLimiterSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SmartLimiterStatus) DeepCopyInto(out *SmartLimiterStatus) { + *out = *in + if in.RatelimitStatus != nil { + in, out := &in.RatelimitStatus, &out.RatelimitStatus + *out = make(map[string]*SmartLimitDescriptors, len(*in)) + for key, val := range *in { + var outVal *SmartLimitDescriptors + if val == nil { + (*out)[key] = nil + } else { + in, out := &val, &outVal + *out = new(SmartLimitDescriptors) + (*in).DeepCopyInto(*out) + } + (*out)[key] = outVal + } + } + if in.MetricStatus != nil { + in, out := &in.MetricStatus, &out.MetricStatus + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + out.XXX_NoUnkeyedLiteral = in.XXX_NoUnkeyedLiteral + if in.XXX_unrecognized != nil { + in, out := &in.XXX_unrecognized, &out.XXX_unrecognized + *out = make([]byte, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SmartLimiterStatus. +func (in *SmartLimiterStatus) DeepCopy() *SmartLimiterStatus { + if in == nil { + return nil + } + out := new(SmartLimiterStatus) + in.DeepCopyInto(out) + return out +} diff --git a/staging/src/slime.io/slime/modules/limiter/cfg.json b/staging/src/slime.io/slime/modules/limiter/cfg.json new file mode 100644 index 00000000..91418f95 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/cfg.json @@ -0,0 +1,28 @@ +{ + "enable":true, + "limiter":{ + "backend":1 + }, + "metric":{ + "k8s":{ + "handlers":[ + "pod" + ] + }, + "prometheus":{ + "address":"http://127.0.0.1:9090", + "handlers":{ + "cpu.max":{ + "query":"max(container_cpu_usage_seconds_total{namespace=\"$namespace\",pod=~\"$pod_name\",image=\"\"})\n" + }, + "cpu.sum":{ + "query":"sum(container_cpu_usage_seconds_total{namespace=\"$namespace\",pod=~\"$pod_name\",image=\"\"})\n" + }, + "rt99":{ + "query":"histogram_quantile(0.99, sum(rate(istio_request_duration_milliseconds_bucket{kubernetes_pod_name=~\"$pod_name\"}[2m]))by(le))\n" + } + } + } + }, + "name":"limiter" +} diff --git a/staging/src/slime.io/slime/modules/limiter/charts/values.yaml b/staging/src/slime.io/slime/modules/limiter/charts/values.yaml new file mode 100644 index 00000000..e69de29b diff --git a/staging/src/slime.io/slime/modules/limiter/config/certmanager/certificate.yaml b/staging/src/slime.io/slime/modules/limiter/config/certmanager/certificate.yaml new file mode 100644 index 00000000..58db114f --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/config/certmanager/certificate.yaml @@ -0,0 +1,26 @@ +# The following manifests contain a self-signed issuer CR and a certificate CR. +# More document can be found at https://docs.cert-manager.io +# WARNING: Targets CertManager 0.11 check https://docs.cert-manager.io/en/latest/tasks/upgrading/index.html for +# breaking changes +apiVersion: cert-manager.io/v1alpha2 +kind: Issuer +metadata: + name: selfsigned-issuer + namespace: system +spec: + selfSigned: {} +--- +apiVersion: cert-manager.io/v1alpha2 +kind: Certificate +metadata: + name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml + namespace: system +spec: + # $(SERVICE_NAME) and $(SERVICE_NAMESPACE) will be substituted by kustomize + dnsNames: + - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc + - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc.cluster.local + issuerRef: + kind: Issuer + name: selfsigned-issuer + secretName: webhook-server-cert # this secret will not be prefixed, since it's not managed by kustomize diff --git a/staging/src/slime.io/slime/modules/limiter/config/certmanager/kustomization.yaml b/staging/src/slime.io/slime/modules/limiter/config/certmanager/kustomization.yaml new file mode 100644 index 00000000..bebea5a5 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/config/certmanager/kustomization.yaml @@ -0,0 +1,5 @@ +resources: +- certificate.yaml + +configurations: +- kustomizeconfig.yaml diff --git a/staging/src/slime.io/slime/modules/limiter/config/certmanager/kustomizeconfig.yaml b/staging/src/slime.io/slime/modules/limiter/config/certmanager/kustomizeconfig.yaml new file mode 100644 index 00000000..90d7c313 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/config/certmanager/kustomizeconfig.yaml @@ -0,0 +1,16 @@ +# This configuration is for teaching kustomize how to update name ref and var substitution +nameReference: +- kind: Issuer + group: cert-manager.io + fieldSpecs: + - kind: Certificate + group: cert-manager.io + path: spec/issuerRef/name + +varReference: +- kind: Certificate + group: cert-manager.io + path: spec/commonName +- kind: Certificate + group: cert-manager.io + path: spec/dnsNames diff --git a/staging/src/slime.io/slime/modules/limiter/config/crd/kustomization.yaml b/staging/src/slime.io/slime/modules/limiter/config/crd/kustomization.yaml new file mode 100644 index 00000000..c1b8b4ea --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/config/crd/kustomization.yaml @@ -0,0 +1,24 @@ +# This kustomization.yaml is not intended to be run by itself, +# since it depends on service name and namespace that are out of this kustomize package. +# It should be run by config/default +resources: +- bases/microservice.slime.io_smartlimiters.yaml +- bases/networking.istio.io_destinationrules.yaml +# +kubebuilder:scaffold:crdkustomizeresource + +patchesStrategicMerge: +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. +# patches here are for enabling the conversion webhook for each CRD +#- patches/webhook_in_smartlimiters.yaml +#- patches/webhook_in_destinationrules.yaml +# +kubebuilder:scaffold:crdkustomizewebhookpatch + +# [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix. +# patches here are for enabling the CA injection for each CRD +#- patches/cainjection_in_smartlimiters.yaml +#- patches/cainjection_in_destinationrules.yaml +# +kubebuilder:scaffold:crdkustomizecainjectionpatch + +# the following config is for teaching kustomize how to do kustomization for CRDs. +configurations: +- kustomizeconfig.yaml diff --git a/staging/src/slime.io/slime/modules/limiter/config/crd/kustomizeconfig.yaml b/staging/src/slime.io/slime/modules/limiter/config/crd/kustomizeconfig.yaml new file mode 100644 index 00000000..6f83d9a9 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/config/crd/kustomizeconfig.yaml @@ -0,0 +1,17 @@ +# This file is for teaching kustomize how to substitute name and namespace reference in CRD +nameReference: +- kind: Service + version: v1 + fieldSpecs: + - kind: CustomResourceDefinition + group: apiextensions.k8s.io + path: spec/conversion/webhookClientConfig/service/name + +namespace: +- kind: CustomResourceDefinition + group: apiextensions.k8s.io + path: spec/conversion/webhookClientConfig/service/namespace + create: false + +varReference: +- path: metadata/annotations diff --git a/staging/src/slime.io/slime/modules/limiter/config/crd/patches/cainjection_in_destinationrules.yaml b/staging/src/slime.io/slime/modules/limiter/config/crd/patches/cainjection_in_destinationrules.yaml new file mode 100644 index 00000000..1c273f08 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/config/crd/patches/cainjection_in_destinationrules.yaml @@ -0,0 +1,8 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: destinationrules.networking.istio.io diff --git a/staging/src/slime.io/slime/modules/limiter/config/crd/patches/cainjection_in_smartlimiters.yaml b/staging/src/slime.io/slime/modules/limiter/config/crd/patches/cainjection_in_smartlimiters.yaml new file mode 100644 index 00000000..d273213e --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/config/crd/patches/cainjection_in_smartlimiters.yaml @@ -0,0 +1,8 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: smartlimiters.microservice.slime.io diff --git a/staging/src/slime.io/slime/modules/limiter/config/crd/patches/webhook_in_destinationrules.yaml b/staging/src/slime.io/slime/modules/limiter/config/crd/patches/webhook_in_destinationrules.yaml new file mode 100644 index 00000000..a15fd858 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/config/crd/patches/webhook_in_destinationrules.yaml @@ -0,0 +1,17 @@ +# The following patch enables conversion webhook for CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: destinationrules.networking.istio.io +spec: + conversion: + strategy: Webhook + webhookClientConfig: + # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, + # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) + caBundle: Cg== + service: + namespace: system + name: webhook-service + path: /convert diff --git a/staging/src/slime.io/slime/modules/limiter/config/crd/patches/webhook_in_smartlimiters.yaml b/staging/src/slime.io/slime/modules/limiter/config/crd/patches/webhook_in_smartlimiters.yaml new file mode 100644 index 00000000..8b8e397d --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/config/crd/patches/webhook_in_smartlimiters.yaml @@ -0,0 +1,17 @@ +# The following patch enables conversion webhook for CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: smartlimiters.microservice.slime.io +spec: + conversion: + strategy: Webhook + webhookClientConfig: + # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, + # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) + caBundle: Cg== + service: + namespace: system + name: webhook-service + path: /convert diff --git a/staging/src/slime.io/slime/modules/limiter/config/default/kustomization.yaml b/staging/src/slime.io/slime/modules/limiter/config/default/kustomization.yaml new file mode 100644 index 00000000..b0a34332 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/config/default/kustomization.yaml @@ -0,0 +1,70 @@ +# Adds namespace to all resources. +namespace: limiter-system + +# Value of this field is prepended to the +# names of all resources, e.g. a deployment named +# "wordpress" becomes "alices-wordpress". +# Note that it should also match with the prefix (text before '-') of the namespace +# field above. +namePrefix: limiter- + +# Labels to add to all resources and selectors. +#commonLabels: +# someName: someValue + +bases: +- ../crd +- ../rbac +- ../manager +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in +# crd/kustomization.yaml +#- ../webhook +# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. +#- ../certmanager +# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. +#- ../prometheus + +patchesStrategicMerge: + # Protect the /metrics endpoint by putting it behind auth. + # If you want your controller-manager to expose the /metrics + # endpoint w/o any authn/z, please comment the following line. +- manager_auth_proxy_patch.yaml + +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in +# crd/kustomization.yaml +#- manager_webhook_patch.yaml + +# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. +# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks. +# 'CERTMANAGER' needs to be enabled to use ca injection +#- webhookcainjection_patch.yaml + +# the following config is for teaching kustomize how to do var substitution +vars: +# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. +#- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR +# objref: +# kind: Certificate +# group: cert-manager.io +# version: v1alpha2 +# name: serving-cert # this name should match the one in certificate.yaml +# fieldref: +# fieldpath: metadata.namespace +#- name: CERTIFICATE_NAME +# objref: +# kind: Certificate +# group: cert-manager.io +# version: v1alpha2 +# name: serving-cert # this name should match the one in certificate.yaml +#- name: SERVICE_NAMESPACE # namespace of the service +# objref: +# kind: Service +# version: v1 +# name: webhook-service +# fieldref: +# fieldpath: metadata.namespace +#- name: SERVICE_NAME +# objref: +# kind: Service +# version: v1 +# name: webhook-service diff --git a/staging/src/slime.io/slime/modules/limiter/config/default/manager_auth_proxy_patch.yaml b/staging/src/slime.io/slime/modules/limiter/config/default/manager_auth_proxy_patch.yaml new file mode 100644 index 00000000..77e743d1 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/config/default/manager_auth_proxy_patch.yaml @@ -0,0 +1,25 @@ +# This patch inject a sidecar container which is a HTTP proxy for the +# controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system +spec: + template: + spec: + containers: + - name: kube-rbac-proxy + image: gcr.io/kubebuilder/kube-rbac-proxy:v0.5.0 + args: + - "--secure-listen-address=0.0.0.0:8443" + - "--upstream=http://127.0.0.1:8080/" + - "--logtostderr=true" + - "--v=10" + ports: + - containerPort: 8443 + name: https + - name: manager + args: + - "--metrics-addr=127.0.0.1:8080" + - "--enable-leader-election" diff --git a/staging/src/slime.io/slime/modules/limiter/config/default/manager_webhook_patch.yaml b/staging/src/slime.io/slime/modules/limiter/config/default/manager_webhook_patch.yaml new file mode 100644 index 00000000..738de350 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/config/default/manager_webhook_patch.yaml @@ -0,0 +1,23 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system +spec: + template: + spec: + containers: + - name: manager + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert + readOnly: true + volumes: + - name: cert + secret: + defaultMode: 420 + secretName: webhook-server-cert diff --git a/staging/src/slime.io/slime/modules/limiter/config/default/webhookcainjection_patch.yaml b/staging/src/slime.io/slime/modules/limiter/config/default/webhookcainjection_patch.yaml new file mode 100644 index 00000000..7e79bf99 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/config/default/webhookcainjection_patch.yaml @@ -0,0 +1,15 @@ +# This patch add annotation to admission webhook config and +# the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize. +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: MutatingWebhookConfiguration +metadata: + name: mutating-webhook-configuration + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) +--- +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: ValidatingWebhookConfiguration +metadata: + name: validating-webhook-configuration + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) diff --git a/staging/src/slime.io/slime/modules/limiter/config/manager/kustomization.yaml b/staging/src/slime.io/slime/modules/limiter/config/manager/kustomization.yaml new file mode 100644 index 00000000..5c5f0b84 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/config/manager/kustomization.yaml @@ -0,0 +1,2 @@ +resources: +- manager.yaml diff --git a/staging/src/slime.io/slime/modules/limiter/config/manager/manager.yaml b/staging/src/slime.io/slime/modules/limiter/config/manager/manager.yaml new file mode 100644 index 00000000..b6c85a52 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/config/manager/manager.yaml @@ -0,0 +1,39 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + control-plane: controller-manager + name: system +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system + labels: + control-plane: controller-manager +spec: + selector: + matchLabels: + control-plane: controller-manager + replicas: 1 + template: + metadata: + labels: + control-plane: controller-manager + spec: + containers: + - command: + - /manager + args: + - --enable-leader-election + image: controller:latest + name: manager + resources: + limits: + cpu: 100m + memory: 30Mi + requests: + cpu: 100m + memory: 20Mi + terminationGracePeriodSeconds: 10 diff --git a/staging/src/slime.io/slime/modules/limiter/config/prometheus/kustomization.yaml b/staging/src/slime.io/slime/modules/limiter/config/prometheus/kustomization.yaml new file mode 100644 index 00000000..ed137168 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/config/prometheus/kustomization.yaml @@ -0,0 +1,2 @@ +resources: +- monitor.yaml diff --git a/staging/src/slime.io/slime/modules/limiter/config/prometheus/monitor.yaml b/staging/src/slime.io/slime/modules/limiter/config/prometheus/monitor.yaml new file mode 100644 index 00000000..9b8047b7 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/config/prometheus/monitor.yaml @@ -0,0 +1,16 @@ + +# Prometheus Monitor Service (Metrics) +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + labels: + control-plane: controller-manager + name: controller-manager-metrics-monitor + namespace: system +spec: + endpoints: + - path: /metrics + port: https + selector: + matchLabels: + control-plane: controller-manager diff --git a/staging/src/slime.io/slime/modules/limiter/config/rbac/auth_proxy_client_clusterrole.yaml b/staging/src/slime.io/slime/modules/limiter/config/rbac/auth_proxy_client_clusterrole.yaml new file mode 100644 index 00000000..7d62534c --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/config/rbac/auth_proxy_client_clusterrole.yaml @@ -0,0 +1,7 @@ +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + name: metrics-reader +rules: +- nonResourceURLs: ["/metrics"] + verbs: ["get"] diff --git a/staging/src/slime.io/slime/modules/limiter/config/rbac/auth_proxy_role.yaml b/staging/src/slime.io/slime/modules/limiter/config/rbac/auth_proxy_role.yaml new file mode 100644 index 00000000..618f5e41 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/config/rbac/auth_proxy_role.yaml @@ -0,0 +1,13 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: proxy-role +rules: +- apiGroups: ["authentication.k8s.io"] + resources: + - tokenreviews + verbs: ["create"] +- apiGroups: ["authorization.k8s.io"] + resources: + - subjectaccessreviews + verbs: ["create"] diff --git a/staging/src/slime.io/slime/modules/limiter/config/rbac/auth_proxy_role_binding.yaml b/staging/src/slime.io/slime/modules/limiter/config/rbac/auth_proxy_role_binding.yaml new file mode 100644 index 00000000..48ed1e4b --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/config/rbac/auth_proxy_role_binding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: proxy-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: proxy-role +subjects: +- kind: ServiceAccount + name: default + namespace: system diff --git a/staging/src/slime.io/slime/modules/limiter/config/rbac/auth_proxy_service.yaml b/staging/src/slime.io/slime/modules/limiter/config/rbac/auth_proxy_service.yaml new file mode 100644 index 00000000..6cf656be --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/config/rbac/auth_proxy_service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + control-plane: controller-manager + name: controller-manager-metrics-service + namespace: system +spec: + ports: + - name: https + port: 8443 + targetPort: https + selector: + control-plane: controller-manager diff --git a/staging/src/slime.io/slime/modules/limiter/config/rbac/destinationrule_editor_role.yaml b/staging/src/slime.io/slime/modules/limiter/config/rbac/destinationrule_editor_role.yaml new file mode 100644 index 00000000..baf5c068 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/config/rbac/destinationrule_editor_role.yaml @@ -0,0 +1,24 @@ +# permissions for end users to edit destinationrules. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: destinationrule-editor-role +rules: +- apiGroups: + - networking.istio.io + resources: + - destinationrules + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - networking.istio.io + resources: + - destinationrules/status + verbs: + - get diff --git a/staging/src/slime.io/slime/modules/limiter/config/rbac/destinationrule_viewer_role.yaml b/staging/src/slime.io/slime/modules/limiter/config/rbac/destinationrule_viewer_role.yaml new file mode 100644 index 00000000..29e24066 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/config/rbac/destinationrule_viewer_role.yaml @@ -0,0 +1,20 @@ +# permissions for end users to view destinationrules. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: destinationrule-viewer-role +rules: +- apiGroups: + - networking.istio.io + resources: + - destinationrules + verbs: + - get + - list + - watch +- apiGroups: + - networking.istio.io + resources: + - destinationrules/status + verbs: + - get diff --git a/staging/src/slime.io/slime/modules/limiter/config/rbac/kustomization.yaml b/staging/src/slime.io/slime/modules/limiter/config/rbac/kustomization.yaml new file mode 100644 index 00000000..66c28338 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/config/rbac/kustomization.yaml @@ -0,0 +1,12 @@ +resources: +- role.yaml +- role_binding.yaml +- leader_election_role.yaml +- leader_election_role_binding.yaml +# Comment the following 4 lines if you want to disable +# the auth proxy (https://github.com/brancz/kube-rbac-proxy) +# which protects your /metrics endpoint. +- auth_proxy_service.yaml +- auth_proxy_role.yaml +- auth_proxy_role_binding.yaml +- auth_proxy_client_clusterrole.yaml diff --git a/staging/src/slime.io/slime/modules/limiter/config/rbac/leader_election_role.yaml b/staging/src/slime.io/slime/modules/limiter/config/rbac/leader_election_role.yaml new file mode 100644 index 00000000..eaa79158 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/config/rbac/leader_election_role.yaml @@ -0,0 +1,32 @@ +# permissions to do leader election. +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: leader-election-role +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - configmaps/status + verbs: + - get + - update + - patch +- apiGroups: + - "" + resources: + - events + verbs: + - create diff --git a/staging/src/slime.io/slime/modules/limiter/config/rbac/leader_election_role_binding.yaml b/staging/src/slime.io/slime/modules/limiter/config/rbac/leader_election_role_binding.yaml new file mode 100644 index 00000000..eed16906 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/config/rbac/leader_election_role_binding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: leader-election-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: leader-election-role +subjects: +- kind: ServiceAccount + name: default + namespace: system diff --git a/staging/src/slime.io/slime/modules/limiter/config/rbac/role_binding.yaml b/staging/src/slime.io/slime/modules/limiter/config/rbac/role_binding.yaml new file mode 100644 index 00000000..8f265870 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/config/rbac/role_binding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: manager-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: system diff --git a/staging/src/slime.io/slime/modules/limiter/config/rbac/smartlimiter_editor_role.yaml b/staging/src/slime.io/slime/modules/limiter/config/rbac/smartlimiter_editor_role.yaml new file mode 100644 index 00000000..defe11e7 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/config/rbac/smartlimiter_editor_role.yaml @@ -0,0 +1,24 @@ +# permissions for end users to edit smartlimiters. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: smartlimiter-editor-role +rules: +- apiGroups: + - microservice.slime.io + resources: + - smartlimiters + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - microservice.slime.io + resources: + - smartlimiters/status + verbs: + - get diff --git a/staging/src/slime.io/slime/modules/limiter/config/rbac/smartlimiter_viewer_role.yaml b/staging/src/slime.io/slime/modules/limiter/config/rbac/smartlimiter_viewer_role.yaml new file mode 100644 index 00000000..9664e6e1 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/config/rbac/smartlimiter_viewer_role.yaml @@ -0,0 +1,20 @@ +# permissions for end users to view smartlimiters. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: smartlimiter-viewer-role +rules: +- apiGroups: + - microservice.slime.io + resources: + - smartlimiters + verbs: + - get + - list + - watch +- apiGroups: + - microservice.slime.io + resources: + - smartlimiters/status + verbs: + - get diff --git a/staging/src/slime.io/slime/modules/limiter/config/samples/microservice.slime.io_v1alpha1_smartlimiter.yaml b/staging/src/slime.io/slime/modules/limiter/config/samples/microservice.slime.io_v1alpha1_smartlimiter.yaml new file mode 100644 index 00000000..8c5f965c --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/config/samples/microservice.slime.io_v1alpha1_smartlimiter.yaml @@ -0,0 +1,7 @@ +apiVersion: microservice.slime.io/v1alpha1 +kind: SmartLimiter +metadata: + name: smartlimiter-sample +spec: + # Add fields here + foo: bar diff --git a/staging/src/slime.io/slime/modules/limiter/config/samples/networking.istio.io_v1alpha3_destinationrule.yaml b/staging/src/slime.io/slime/modules/limiter/config/samples/networking.istio.io_v1alpha3_destinationrule.yaml new file mode 100644 index 00000000..634efc6b --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/config/samples/networking.istio.io_v1alpha3_destinationrule.yaml @@ -0,0 +1,7 @@ +apiVersion: networking.istio.io/v1alpha3 +kind: DestinationRule +metadata: + name: destinationrule-sample +spec: + # Add fields here + foo: bar diff --git a/staging/src/slime.io/slime/modules/limiter/config/webhook/kustomization.yaml b/staging/src/slime.io/slime/modules/limiter/config/webhook/kustomization.yaml new file mode 100644 index 00000000..9cf26134 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/config/webhook/kustomization.yaml @@ -0,0 +1,6 @@ +resources: +- manifests.yaml +- service.yaml + +configurations: +- kustomizeconfig.yaml diff --git a/staging/src/slime.io/slime/modules/limiter/config/webhook/kustomizeconfig.yaml b/staging/src/slime.io/slime/modules/limiter/config/webhook/kustomizeconfig.yaml new file mode 100644 index 00000000..25e21e3c --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/config/webhook/kustomizeconfig.yaml @@ -0,0 +1,25 @@ +# the following config is for teaching kustomize where to look at when substituting vars. +# It requires kustomize v2.1.0 or newer to work properly. +nameReference: +- kind: Service + version: v1 + fieldSpecs: + - kind: MutatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/name + - kind: ValidatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/name + +namespace: +- kind: MutatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/namespace + create: true +- kind: ValidatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/namespace + create: true + +varReference: +- path: metadata/annotations diff --git a/staging/src/slime.io/slime/modules/limiter/config/webhook/service.yaml b/staging/src/slime.io/slime/modules/limiter/config/webhook/service.yaml new file mode 100644 index 00000000..31e0f829 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/config/webhook/service.yaml @@ -0,0 +1,12 @@ + +apiVersion: v1 +kind: Service +metadata: + name: webhook-service + namespace: system +spec: + ports: + - port: 443 + targetPort: 9443 + selector: + control-plane: controller-manager diff --git a/staging/src/slime.io/slime/modules/limiter/controllers/controllers.go b/staging/src/slime.io/slime/modules/limiter/controllers/controllers.go new file mode 100644 index 00000000..f5697c21 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/controllers/controllers.go @@ -0,0 +1,8 @@ +package controllers + +import ( + frameworkmodel "slime.io/slime/framework/model" + "slime.io/slime/modules/limiter/model" +) + +var log = model.ModuleLog.WithField(frameworkmodel.LogFieldKeyPkg, "controllers") diff --git a/staging/src/slime.io/slime/modules/limiter/controllers/envoy_filters.go b/staging/src/slime.io/slime/modules/limiter/controllers/envoy_filters.go new file mode 100644 index 00000000..0d8598da --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/controllers/envoy_filters.go @@ -0,0 +1,163 @@ +package controllers + +import ( + "context" + "fmt" + + networking "istio.io/api/networking/v1alpha3" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "slime.io/slime/framework/controllers" + "slime.io/slime/framework/util" + microservicev1alpha2 "slime.io/slime/modules/limiter/api/v1alpha2" + "slime.io/slime/modules/limiter/model" +) + +func (r *SmartLimiterReconciler) GenerateEnvoyConfigs(spec microservicev1alpha2.SmartLimiterSpec, + material map[string]string, loc types.NamespacedName) ( + map[string]*networking.EnvoyFilter, map[string]*microservicev1alpha2.SmartLimitDescriptors, []*model.Descriptor, error, +) { + materialInterface := util.MapToMapInterface(material) + setsEnvoyFilter := make(map[string]*networking.EnvoyFilter) + setsSmartLimitDescriptor := make(map[string]*microservicev1alpha2.SmartLimitDescriptors) + // global descriptors + globalDescriptors := make([]*model.Descriptor, 0) + host := util.UnityHost(loc.Name, loc.Namespace) + rls := spec.Rls + + // get destinationrule subset of the host + var sets []*networking.Subset + if controllers.HostSubsetMapping.Get(host) != nil { + sets = controllers.HostSubsetMapping.Get(host).([]*networking.Subset) + } else { + sets = make([]*networking.Subset, 0, 1) + } + sets = append(sets, &networking.Subset{Name: util.Wellkonw_BaseSet}) + + svc := &v1.Service{} + if err := r.Client.Get(context.TODO(), loc, svc); err != nil { + if errors.IsNotFound(err) { + log.Errorf("svc %s:%s is not found", loc.Name, loc.Namespace) + } else { + log.Errorf("get svc %s:%s err: %+v", loc.Name, loc.Namespace, err.Error()) + } + return setsEnvoyFilter, setsSmartLimitDescriptor, globalDescriptors, err + } + svcSelector := svc.Spec.Selector + + for _, set := range sets { + if setDescriptor, ok := spec.Sets[set.Name]; !ok { + // sets is specified in the descriptor, but not found in the Destinationrule set + // the set must be deleted in the DestinationRule set + setsEnvoyFilter[set.Name] = nil + } else { + validDescriptor := µservicev1alpha2.SmartLimitDescriptors{} + for _, des := range setDescriptor.Descriptor_ { + // update the EnvoyFilter when condition value is true after calculate + if shouldUpdate, err := util.CalculateTemplateBool(des.Condition, materialInterface); err != nil { + log.Errorf("calaulate %s condition err, %+v", des.Condition, err.Error()) + continue + } else if !shouldUpdate { + log.Infof("the value of condition %s is false", des.Condition) + } else { + // update + if des.Action != nil { + if rateLimitValue, err := util.CalculateTemplate(des.Action.Quota, materialInterface); err != nil { + log.Errorf("calculate quota %s err, %+v", des.Action.Quota, err.Error()) + } else { + // log.Infof("after calculate, the quota %s is %d",des.Action.Quota,rateLimitValue) + validDescriptor.Descriptor_ = append(validDescriptor.Descriptor_, µservicev1alpha2.SmartLimitDescriptor{ + Action: µservicev1alpha2.SmartLimitDescriptor_Action{ + Quota: fmt.Sprintf("%d", rateLimitValue), + FillInterval: des.Action.FillInterval, + Strategy: des.Action.Strategy, + }, + Match: des.Match, + Target: des.Target, + }) + } + } + } + } + + if len(validDescriptor.Descriptor_) == 0 { + log.Infof("not matchd descriptor in %s", set.Name) + setsEnvoyFilter[set.Name] = nil + } else { + // prepare to generate ef according the descriptor in validDescriptor + selector := util.CopyMap(svcSelector) + for k, v := range set.Labels { + selector[k] = v + } + ef := descriptorsToEnvoyFilter(validDescriptor.Descriptor_, selector, loc, rls) + setsEnvoyFilter[set.Name] = ef + setsSmartLimitDescriptor[set.Name] = validDescriptor + + desc := descriptorsToGlobalRateLimit(validDescriptor.Descriptor_, loc) + globalDescriptors = append(globalDescriptors, desc...) + } + } + } + return setsEnvoyFilter, setsSmartLimitDescriptor, globalDescriptors, nil +} + +func descriptorsToEnvoyFilter(descriptors []*microservicev1alpha2.SmartLimitDescriptor, labels map[string]string, loc types.NamespacedName, rls string) *networking.EnvoyFilter { + ef := &networking.EnvoyFilter{ + WorkloadSelector: &networking.WorkloadSelector{ + Labels: labels, + }, + } + ef.ConfigPatches = make([]*networking.EnvoyFilter_EnvoyConfigObjectPatch, 0) + globalDescriptors := make([]*microservicev1alpha2.SmartLimitDescriptor, 0) + localDescriptors := make([]*microservicev1alpha2.SmartLimitDescriptor, 0) + + // split descriptors due to different envoy plugins + for _, descriptor := range descriptors { + if descriptor.Action != nil { + if descriptor.Action.Strategy == model.GlobalSmartLimiter { + globalDescriptors = append(globalDescriptors, descriptor) + } else { + localDescriptors = append(localDescriptors, descriptor) + } + } + } + + // http router + httpRouterPatches, err := generateHttpRouterPatch(descriptors, loc) + if err != nil { + log.Errorf("generateHttpRouterPatch err: %+v", err.Error()) + return nil + } else if len(httpRouterPatches) > 0 { + ef.ConfigPatches = append(ef.ConfigPatches, httpRouterPatches...) + } + + // config plugin envoy.filters.http.ratelimit + if len(globalDescriptors) > 0 { + server := getRateLimiterServerCluster(rls) + httpFilterEnvoyRateLimitPatch := generateEnvoyHttpFilterGlobalRateLimitPatch(server) + if httpFilterEnvoyRateLimitPatch != nil { + ef.ConfigPatches = append(ef.ConfigPatches, httpFilterEnvoyRateLimitPatch) + } + } + + // enable and config plugin envoy.filters.http.local_ratelimit + if len(localDescriptors) > 0 { + httpFilterLocalRateLimitPatch := generateHttpFilterLocalRateLimitPatch() + ef.ConfigPatches = append(ef.ConfigPatches, httpFilterLocalRateLimitPatch) + + perFilterPatch := generateLocalRateLimitPerFilterPatch(localDescriptors, loc) + ef.ConfigPatches = append(ef.ConfigPatches, perFilterPatch...) + } + return ef +} + +func descriptorsToGlobalRateLimit(descriptors []*microservicev1alpha2.SmartLimitDescriptor, loc types.NamespacedName) []*model.Descriptor { + globalDescriptors := make([]*microservicev1alpha2.SmartLimitDescriptor, 0) + for _, descriptor := range descriptors { + if descriptor.Action.Strategy == model.GlobalSmartLimiter { + globalDescriptors = append(globalDescriptors, descriptor) + } + } + return generateGlobalRateLimitDescriptor(globalDescriptors, loc) +} diff --git a/staging/src/slime.io/slime/modules/limiter/controllers/envoy_global_limit.go b/staging/src/slime.io/slime/modules/limiter/controllers/envoy_global_limit.go new file mode 100644 index 00000000..68283902 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/controllers/envoy_global_limit.go @@ -0,0 +1,163 @@ +package controllers + +import ( + "fmt" + "strconv" + + envoy_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + envoy_config_ratelimit_v3 "github.com/envoyproxy/go-control-plane/envoy/config/ratelimit/v3" + structpb "github.com/gogo/protobuf/types" + networking "istio.io/api/networking/v1alpha3" + "k8s.io/apimachinery/pkg/types" + "slime.io/slime/framework/util" + microservicev1alpha2 "slime.io/slime/modules/limiter/api/v1alpha2" + "slime.io/slime/modules/limiter/model" +) + +func generateEnvoyHttpFilterGlobalRateLimitPatch(server string) *networking.EnvoyFilter_EnvoyConfigObjectPatch { + rateLimitServiceConfig := generateRateLimitService(server) + rs, err := util.MessageToStruct(rateLimitServiceConfig) + if err != nil { + log.Errorf("MessageToStruct err: %+v", err.Error()) + return nil + } + patch := &networking.EnvoyFilter_EnvoyConfigObjectPatch{ + ApplyTo: networking.EnvoyFilter_HTTP_FILTER, + Match: generateEnvoyHttpFilterMatch(), + Patch: generateEnvoyHttpFilterRateLimitServicePatch(rs), + } + return patch +} + +func generateRateLimitService(clusterName string) *envoy_config_ratelimit_v3.RateLimitServiceConfig { + rateLimitServiceConfig := &envoy_config_ratelimit_v3.RateLimitServiceConfig{ + GrpcService: &envoy_core_v3.GrpcService{ + TargetSpecifier: &envoy_core_v3.GrpcService_EnvoyGrpc_{EnvoyGrpc: &envoy_core_v3.GrpcService_EnvoyGrpc{ + ClusterName: clusterName, + }}, + }, + TransportApiVersion: envoy_core_v3.ApiVersion_V3, + } + return rateLimitServiceConfig +} + +func generateEnvoyHttpFilterMatch() *networking.EnvoyFilter_EnvoyConfigObjectMatch { + return &networking.EnvoyFilter_EnvoyConfigObjectMatch{ + Context: networking.EnvoyFilter_SIDECAR_INBOUND, + ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ + Listener: &networking.EnvoyFilter_ListenerMatch{ + FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ + Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{ + Name: util.Envoy_HttpConnectionManager, + SubFilter: &networking.EnvoyFilter_ListenerMatch_SubFilterMatch{ + Name: util.Envoy_Route, + }, + }, + }, + }, + }, + } +} + +func generateEnvoyHttpFilterRateLimitServicePatch(rs *structpb.Struct) *networking.EnvoyFilter_Patch { + return &networking.EnvoyFilter_Patch{ + Operation: networking.EnvoyFilter_Patch_INSERT_BEFORE, + Value: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + util.Struct_HttpFilter_Name: { + Kind: &structpb.Value_StringValue{StringValue: model.EnvoyFiltersHttpRateLimit}, + }, + util.Struct_HttpFilter_TypedConfig: { + Kind: &structpb.Value_StructValue{ + StructValue: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + util.Struct_Any_AtType: { + Kind: &structpb.Value_StringValue{StringValue: util.TypeUrl_UdpaTypedStruct}, + }, + util.Struct_Any_TypedUrl: { + Kind: &structpb.Value_StringValue{StringValue: model.TypeUrlEnvoyRateLimit}, + }, + util.Struct_Any_Value: { + Kind: &structpb.Value_StructValue{ + StructValue: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + model.StructDomain: { + Kind: &structpb.Value_StringValue{StringValue: model.Domain}, + }, + model.StructRateLimitService: { + Kind: &structpb.Value_StructValue{StructValue: rs}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func generateGlobalRateLimitDescriptor(descriptors []*microservicev1alpha2.SmartLimitDescriptor, loc types.NamespacedName) []*model.Descriptor { + desc := make([]*model.Descriptor, 0) + for _, descriptor := range descriptors { + quota, unit, err := calculateQuotaPerUnit(descriptor) + if err != nil { + log.Errorf("calculateQuotaPerUnit err: %+v", err) + return desc + } + item := &model.Descriptor{ + Value: generateDescriptorValue(descriptor, loc), + RateLimit: &model.RateLimit{ + RequestsPerUnit: uint32(quota), + Unit: unit, + }, + } + if len(descriptor.Match) == 0 { + item.Key = model.GenericKey + } else { + item.Key = model.HeaderValueMatch + } + desc = append(desc, item) + } + return desc +} + +// https://github.com/envoyproxy/ratelimit only support per second, minute, hour, and day limits +func calculateQuotaPerUnit(descriptor *microservicev1alpha2.SmartLimitDescriptor) (quota int, unit string, err error) { + quota, err = strconv.Atoi(descriptor.Action.Quota) + if err != nil { + return quota, unit, err + } + seconds := descriptor.Action.FillInterval.Seconds + switch seconds { + case 60 * 60 * 24: + unit = "DAY" + case 60 * 60: + unit = "HOUR" + case 60: + unit = "MINUTE" + case 1: + unit = "SECOND" + default: + return quota, unit, fmt.Errorf("invalid time in global rate limit") + } + return quota, unit, nil +} + +func getRateLimiterServerCluster(server string) string { + if server == "" { + return model.RateLimitService + } else { + return server + } +} + +func getConfigMapNamespaceName() types.NamespacedName { + return types.NamespacedName{ + Namespace: model.ConfigMapNamespace, + Name: model.ConfigMapName, + } +} diff --git a/staging/src/slime.io/slime/modules/limiter/controllers/envoy_local_limit.go b/staging/src/slime.io/slime/modules/limiter/controllers/envoy_local_limit.go new file mode 100644 index 00000000..66fea4af --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/controllers/envoy_local_limit.go @@ -0,0 +1,452 @@ +/* +* @Author: yangdihang +* @Date: 2020/11/19 + */ + +package controllers + +import ( + "fmt" + "hash/adler32" + "strconv" + "strings" + + envoy_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + envoy_config_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" + envoy_ratelimit_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/common/ratelimit/v3" + envoy_extensions_filters_http_local_ratelimit_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/local_ratelimit/v3" + envoy_match_v3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" + envoy_type_v3 "github.com/envoyproxy/go-control-plane/envoy/type/v3" + structpb "github.com/gogo/protobuf/types" + "github.com/golang/protobuf/ptypes/duration" + "github.com/golang/protobuf/ptypes/wrappers" + + networking "istio.io/api/networking/v1alpha3" + "k8s.io/apimachinery/pkg/types" + "slime.io/slime/framework/util" + microservicev1alpha2 "slime.io/slime/modules/limiter/api/v1alpha2" + "slime.io/slime/modules/limiter/model" +) + +// store the vhostName/routeName and action +type routeConfig struct { + action *envoy_config_route_v3.RateLimit_Action + routeName string + vhostName string + direction string +} + +// vhostName/routeName, +func genVhostRouteName(rc *routeConfig) string { + return fmt.Sprintf("%s/%s", rc.vhostName, rc.routeName) +} + +func generateRouteConfigs(target *microservicev1alpha2.SmartLimitDescriptor_Target) []*routeConfig { + rcs := make([]*routeConfig, 0) + // build outbound route + if target != nil && len(target.Route) > 0 { + for _, route := range target.Route { + parts := strings.Split(route, "/") + if len(parts) != 2 { + continue + } + vhostName, routeName := parts[0], parts[1] + rc := &routeConfig{ + routeName: routeName, + vhostName: vhostName, + direction: target.Direction, + } + rcs = append(rcs, rc) + } + return rcs + } + // build inbound by default + rcs = generateDefaultInboundRouteConfigs(target) + return rcs +} + +// if port is zero, allow any +func generateDefaultInboundRouteConfigs(target *microservicev1alpha2.SmartLimitDescriptor_Target) []*routeConfig { + rcs := make([]*routeConfig, 0) + if target == nil || target.Port == 0 { + rcs = append(rcs, &routeConfig{ + routeName: model.InboundDefaultRoute, + vhostName: model.AllowAllPort, + direction: model.Inbound, + }) + return rcs + } + rcs = append(rcs, &routeConfig{ + routeName: model.InboundDefaultRoute, + vhostName: fmt.Sprintf("%s|%s|%d", model.Inbound, "http", target.Port), + direction: model.Inbound, + }) + return rcs +} + +func generateHttpRouterPatch(descriptors []*microservicev1alpha2.SmartLimitDescriptor, loc types.NamespacedName) ([]*networking.EnvoyFilter_EnvoyConfigObjectPatch, error) { + patches := make([]*networking.EnvoyFilter_EnvoyConfigObjectPatch, 0) + route2RouteConfig := make(map[string][]*routeConfig) + + for _, descriptor := range descriptors { + rcs := generateRouteConfigs(descriptor.Target) + action := generateRouteRateLimitAction(descriptor, loc) + if action == nil { + continue + } + for _, rc := range rcs { + rc.action = action + vHostRouteName := genVhostRouteName(rc) + if _, ok := route2RouteConfig[vHostRouteName]; !ok { + route2RouteConfig[vHostRouteName] = []*routeConfig{rc} + } else { + route2RouteConfig[vHostRouteName] = append(route2RouteConfig[vHostRouteName], rc) + } + } + } + + for _, rcs := range route2RouteConfig { + rateLimits := make([]*envoy_config_route_v3.RateLimit, 0) + for _, rc := range rcs { + rateLimits = append(rateLimits, &envoy_config_route_v3.RateLimit{Actions: []*envoy_config_route_v3.RateLimit_Action{rc.action}}) + } + route := &envoy_config_route_v3.Route{ + Action: &envoy_config_route_v3.Route_Route{ + Route: &envoy_config_route_v3.RouteAction{ + RateLimits: rateLimits, + }, + }, + } + routeStruct, err := util.MessageToStruct(route) + if err != nil { + return nil, err + } + patch := &networking.EnvoyFilter_EnvoyConfigObjectPatch{ + ApplyTo: networking.EnvoyFilter_HTTP_ROUTE, + Match: generateEnvoyVhostMatch(rcs[0]), + Patch: &networking.EnvoyFilter_Patch{ + Operation: networking.EnvoyFilter_Patch_MERGE, + Value: routeStruct, + }, + } + patches = append(patches, patch) + } + return patches, nil +} + +// only enable local rate limit +func generateHttpFilterLocalRateLimitPatch() *networking.EnvoyFilter_EnvoyConfigObjectPatch { + localRateLimit := &envoy_extensions_filters_http_local_ratelimit_v3.LocalRateLimit{ + StatPrefix: util.Struct_EnvoyLocalRateLimit_Limiter, + } + local, err := util.MessageToStruct(localRateLimit) + if err != nil { + log.Errorf("can not be here, convert message to struct err,%+v", err.Error()) + return nil + } + + patch := &networking.EnvoyFilter_EnvoyConfigObjectPatch{ + ApplyTo: networking.EnvoyFilter_HTTP_FILTER, + // Match: generateEnvoyHttpFilterMatch(), + Patch: &networking.EnvoyFilter_Patch{ + Operation: networking.EnvoyFilter_Patch_INSERT_BEFORE, + Value: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + util.Struct_HttpFilter_Name: { + Kind: &structpb.Value_StringValue{StringValue: util.Envoy_LocalRateLimit}, + }, + util.Struct_HttpFilter_TypedConfig: { + Kind: &structpb.Value_StructValue{ + StructValue: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + util.Struct_Any_AtType: { + Kind: &structpb.Value_StringValue{StringValue: util.TypeUrl_UdpaTypedStruct}, + }, + util.Struct_Any_TypedUrl: { + Kind: &structpb.Value_StringValue{StringValue: util.TypeUrl_EnvoyLocalRatelimit}, + }, + util.Struct_Any_Value: { + Kind: &structpb.Value_StructValue{StructValue: local}, + }, + }, + }, + }, + }, + }, + }, + }, + } + return patch +} + +func generateLocalRateLimitPerFilterPatch(descriptors []*microservicev1alpha2.SmartLimitDescriptor, loc types.NamespacedName) []*networking.EnvoyFilter_EnvoyConfigObjectPatch { + patches := make([]*networking.EnvoyFilter_EnvoyConfigObjectPatch, 0) + route2Descriptors := make(map[string][]*microservicev1alpha2.SmartLimitDescriptor) + route2RouteConfig := make(map[string][]*routeConfig) + + for _, descriptor := range descriptors { + rcs := generateRouteConfigs(descriptor.Target) + for _, rc := range rcs { + vHostRouteName := genVhostRouteName(rc) + if _, ok := route2Descriptors[vHostRouteName]; !ok { + route2Descriptors[vHostRouteName] = []*microservicev1alpha2.SmartLimitDescriptor{descriptor} + } else { + route2Descriptors[vHostRouteName] = append(route2Descriptors[vHostRouteName], descriptor) + } + + if _, ok := route2RouteConfig[vHostRouteName]; !ok { + route2RouteConfig[vHostRouteName] = []*routeConfig{rc} + } else { + route2RouteConfig[vHostRouteName] = append(route2RouteConfig[vHostRouteName], rc) + } + } + } + + for vr, desc := range route2Descriptors { + localRateLimitDescriptors := generateLocalRateLimitDescriptors(desc, loc) + localRateLimit := &envoy_extensions_filters_http_local_ratelimit_v3.LocalRateLimit{ + TokenBucket: generateCustomTokenBucket(100000, 100000, 1), + Descriptors: localRateLimitDescriptors, + StatPrefix: util.Struct_EnvoyLocalRateLimit_Limiter, + FilterEnabled: generateEnvoyLocalRateLimitEnabled(), + FilterEnforced: generateEnvoyLocalRateLimitEnforced(), + } + local, err := util.MessageToStruct(localRateLimit) + if err != nil { + return nil + } + if len(desc) < 1 { + return nil + } + patch := &networking.EnvoyFilter_EnvoyConfigObjectPatch{ + ApplyTo: networking.EnvoyFilter_HTTP_ROUTE, + Match: generateEnvoyVhostMatch(route2RouteConfig[vr][0]), + Patch: generatePerFilterPatch(local), + } + patches = append(patches, patch) + } + return patches +} + +/* +// if key/value is not empty, envoyplugin is needed, we will not generate http route patch +// 有match时,只有当header中的值与match相匹配才会进行对路由进行action限流,需要注意的是RegexMatch(name 的值是否匹配正则)与 +// PresentMatch(name是否存在)互斥 +// 这里之前打算在pb声明为oneof,但是用kubebuilder生成api的过程中无法识别相关interface{} +*/ +func generateRouteRateLimitAction(descriptor *microservicev1alpha2.SmartLimitDescriptor, loc types.NamespacedName) *envoy_config_route_v3.RateLimit_Action { + action := &envoy_config_route_v3.RateLimit_Action{} + if descriptor.CustomKey != "" && descriptor.CustomValue != "" { + log.Infof("customKey/customValue is not empty, users should apply a envoyplugin with same customKey/customValue") + return nil + } else if len(descriptor.Match) == 0 { + action.ActionSpecifier = &envoy_config_route_v3.RateLimit_Action_GenericKey_{ + GenericKey: &envoy_config_route_v3.RateLimit_Action_GenericKey{ + DescriptorValue: generateDescriptorValue(descriptor, loc), + }, + } + } else { + headers := make([]*envoy_config_route_v3.HeaderMatcher, 0) + for _, match := range descriptor.Match { + header := &envoy_config_route_v3.HeaderMatcher{} + header.Name = match.Name + header.InvertMatch = generateInvertMatch(match) + switch { + case match.RegexMatch != "": + header.HeaderMatchSpecifier = generateSafeRegexMatch(match) + case match.ExactMatch != "": + header.HeaderMatchSpecifier = generateExactMatch(match) + case match.PrefixMatch != "": + header.HeaderMatchSpecifier = generatePrefixMatch(match) + case match.SuffixMatch != "": + header.HeaderMatchSpecifier = generateSuffixMatch(match) + default: + if match.IsExactMatchEmpty { + header.HeaderMatchSpecifier = generateExactMatch(match) + } else { + header.HeaderMatchSpecifier = generatePresentMatch(match) + } + } + headers = append(headers, header) + } + action.ActionSpecifier = &envoy_config_route_v3.RateLimit_Action_HeaderValueMatch_{ + HeaderValueMatch: &envoy_config_route_v3.RateLimit_Action_HeaderValueMatch{ + DescriptorValue: generateDescriptorValue(descriptor, loc), + Headers: headers, + }, + } + } + return action +} + +func generateLocalRateLimitDescriptors(descriptors []*microservicev1alpha2.SmartLimitDescriptor, loc types.NamespacedName) []*envoy_ratelimit_v3.LocalRateLimitDescriptor { + localRateLimitDescriptors := make([]*envoy_ratelimit_v3.LocalRateLimitDescriptor, 0) + for _, item := range descriptors { + entries := generateLocalRateLimitDescriptorEntries(item, loc) + tokenBucket := generateTokenBucket(item) + localRateLimitDescriptors = append(localRateLimitDescriptors, &envoy_ratelimit_v3.LocalRateLimitDescriptor{ + Entries: entries, + TokenBucket: tokenBucket, + }) + } + return localRateLimitDescriptors +} + +func generateLocalRateLimitDescriptorEntries(item *microservicev1alpha2.SmartLimitDescriptor, loc types.NamespacedName) []*envoy_ratelimit_v3.RateLimitDescriptor_Entry { + entry := &envoy_ratelimit_v3.RateLimitDescriptor_Entry{} + if item.CustomKey != "" && item.CustomValue != "" { + entry.Key = item.CustomKey + entry.Value = item.CustomValue + } else if len(item.Match) == 0 { + entry.Key = model.GenericKey + entry.Value = generateDescriptorValue(item, loc) + } else { + entry.Key = model.HeaderValueMatch + entry.Value = generateDescriptorValue(item, loc) + } + return []*envoy_ratelimit_v3.RateLimitDescriptor_Entry{entry} +} + +func generateTokenBucket(item *microservicev1alpha2.SmartLimitDescriptor) *envoy_type_v3.TokenBucket { + i, _ := strconv.Atoi(item.Action.Quota) + return &envoy_type_v3.TokenBucket{ + MaxTokens: uint32(i), + FillInterval: &duration.Duration{ + Seconds: item.Action.FillInterval.Seconds, + Nanos: item.Action.FillInterval.Nanos, + }, + TokensPerFill: &wrappers.UInt32Value{Value: uint32(i)}, + } +} + +func generateDescriptorValue(item *microservicev1alpha2.SmartLimitDescriptor, loc types.NamespacedName) string { + id := adler32.Checksum([]byte(item.String() + loc.String())) + return fmt.Sprintf("Service[%s.%s]-User[none]-Id[%d]", loc.Name, loc.Namespace, id) +} + +func generateSafeRegexMatch(match *microservicev1alpha2.SmartLimitDescriptor_HeaderMatcher) *envoy_config_route_v3.HeaderMatcher_SafeRegexMatch { + return &envoy_config_route_v3.HeaderMatcher_SafeRegexMatch{ + SafeRegexMatch: &envoy_match_v3.RegexMatcher{ + EngineType: &envoy_match_v3.RegexMatcher_GoogleRe2{ + GoogleRe2: &envoy_match_v3.RegexMatcher_GoogleRE2{}, + }, + Regex: match.RegexMatch, + }, + } +} + +func generatePrefixMatch(match *microservicev1alpha2.SmartLimitDescriptor_HeaderMatcher) *envoy_config_route_v3.HeaderMatcher_PrefixMatch { + return &envoy_config_route_v3.HeaderMatcher_PrefixMatch{PrefixMatch: match.PrefixMatch} +} + +func generateSuffixMatch(match *microservicev1alpha2.SmartLimitDescriptor_HeaderMatcher) *envoy_config_route_v3.HeaderMatcher_SuffixMatch { + return &envoy_config_route_v3.HeaderMatcher_SuffixMatch{SuffixMatch: match.SuffixMatch} +} + +func generateExactMatch(match *microservicev1alpha2.SmartLimitDescriptor_HeaderMatcher) *envoy_config_route_v3.HeaderMatcher_ExactMatch { + return &envoy_config_route_v3.HeaderMatcher_ExactMatch{ExactMatch: match.ExactMatch} +} + +func generateInvertMatch(match *microservicev1alpha2.SmartLimitDescriptor_HeaderMatcher) bool { + return match.InvertMatch +} + +func generatePresentMatch(match *microservicev1alpha2.SmartLimitDescriptor_HeaderMatcher) *envoy_config_route_v3.HeaderMatcher_PresentMatch { + return &envoy_config_route_v3.HeaderMatcher_PresentMatch{PresentMatch: match.PresentMatch} +} + +// TODO +func generateCustomTokenBucket(maxTokens, tokensPerFill, second int) *envoy_type_v3.TokenBucket { + return &envoy_type_v3.TokenBucket{ + MaxTokens: uint32(maxTokens), + FillInterval: &duration.Duration{ + Seconds: int64(second), + }, + TokensPerFill: &wrappers.UInt32Value{Value: uint32(tokensPerFill)}, + } +} + +// % of requests that will check the local rate limit decision, but not enforce, +// for a given route_key specified in the local rate limit configuration. Defaults to 0. +func generateEnvoyLocalRateLimitEnabled() *envoy_core_v3.RuntimeFractionalPercent { + return &envoy_core_v3.RuntimeFractionalPercent{ + RuntimeKey: util.Struct_EnvoyLocalRateLimit_Enabled, + DefaultValue: &envoy_type_v3.FractionalPercent{ + Numerator: 100, + Denominator: envoy_type_v3.FractionalPercent_HUNDRED, + }, + } +} + +// % of requests that will enforce the local rate limit decision for a given route_key specified in the local rate limit configuration. +// Defaults to 0. This can be used to test what would happen before fully enforcing the outcome. +func generateEnvoyLocalRateLimitEnforced() *envoy_core_v3.RuntimeFractionalPercent { + return &envoy_core_v3.RuntimeFractionalPercent{ + RuntimeKey: util.Struct_EnvoyLocalRateLimit_Enforced, + DefaultValue: &envoy_type_v3.FractionalPercent{ + Numerator: 100, + Denominator: envoy_type_v3.FractionalPercent_HUNDRED, + }, + } +} + +func generateEnvoyVhostMatch(rc *routeConfig) *networking.EnvoyFilter_EnvoyConfigObjectMatch { + match := &networking.EnvoyFilter_EnvoyConfigObjectMatch{ + Context: networking.EnvoyFilter_SIDECAR_INBOUND, + ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_RouteConfiguration{ + RouteConfiguration: &networking.EnvoyFilter_RouteConfigurationMatch{ + Vhost: &networking.EnvoyFilter_RouteConfigurationMatch_VirtualHostMatch{ + Route: &networking.EnvoyFilter_RouteConfigurationMatch_RouteMatch{ + Name: rc.routeName, + }, + }, + }, + }, + } + if rc.direction == model.Outbound { + match.Context = networking.EnvoyFilter_SIDECAR_OUTBOUND + } + config, ok := match.ObjectTypes.(*networking.EnvoyFilter_EnvoyConfigObjectMatch_RouteConfiguration) + if !ok { + log.Errorf("covert to EnvoyFilter_EnvoyConfigObjectMatch_RouteConfiguration err, can not be here") + return match + } + config.RouteConfiguration.Vhost.Name = rc.vhostName + + return match +} + +func generatePerFilterPatch(local *structpb.Struct) *networking.EnvoyFilter_Patch { + return &networking.EnvoyFilter_Patch{ + Operation: networking.EnvoyFilter_Patch_MERGE, + Value: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + model.TypePerFilterConfig: { + Kind: &structpb.Value_StructValue{ + StructValue: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + util.Envoy_LocalRateLimit: { + Kind: &structpb.Value_StructValue{ + StructValue: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + util.Struct_Any_AtType: { + Kind: &structpb.Value_StringValue{StringValue: util.TypeUrl_UdpaTypedStruct}, + }, + util.Struct_Any_TypedUrl: { + Kind: &structpb.Value_StringValue{StringValue: util.TypeUrl_EnvoyLocalRatelimit}, + }, + util.Struct_Any_Value: { + Kind: &structpb.Value_StructValue{StructValue: local}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } +} diff --git a/staging/src/slime.io/slime/modules/limiter/controllers/multicluster/controller.go b/staging/src/slime.io/slime/modules/limiter/controllers/multicluster/controller.go new file mode 100644 index 00000000..b8f6cdb6 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/controllers/multicluster/controller.go @@ -0,0 +1,204 @@ +/* +* @Author: yangdihang +* @Date: 2020/7/24 + */ + +package multicluster + +import ( + "fmt" + "time" + + log "github.com/sirupsen/logrus" + corev1 "k8s.io/api/core/v1" + meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/util/workqueue" + "slime.io/slime/framework/bootstrap" +) + +const ( + RootNamespace = "istio-mc" + maxRetries = 3 +) + +type Controller struct { + k8sClient *kubernetes.Clientset + informer cache.SharedIndexInformer + queue workqueue.RateLimitingInterface + cs map[string]string + subscriber []func(*kubernetes.Clientset) + unSubscriber []func(string) + stop <-chan struct{} +} + +func New(env *bootstrap.Environment, subscriber []func(*kubernetes.Clientset), unSubscriber []func(string)) *Controller { + c := &Controller{ + k8sClient: env.K8SClient, + stop: env.Stop, + subscriber: subscriber, + unSubscriber: unSubscriber, + cs: make(map[string]string), + } + + secretsInformer := cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(opts meta_v1.ListOptions) (runtime.Object, error) { + opts.LabelSelector = env.Config.Global.Multicluster + "=true" + obj, err := c.k8sClient.CoreV1().Secrets(RootNamespace).List(opts) + return obj, err + }, + WatchFunc: func(opts meta_v1.ListOptions) (watch.Interface, error) { + opts.LabelSelector = env.Config.Global.Multicluster + "=true" + return c.k8sClient.CoreV1().Secrets(RootNamespace).Watch(opts) + }, + }, + &corev1.Secret{}, 0, cache.Indexers{}, + ) + c.informer = secretsInformer + + queue := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()) + c.queue = queue + + log.Info("Setting up event handlers") + secretsInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + key, err := cache.MetaNamespaceKeyFunc(obj) + log.Infof("Processing add: %s", key) + if err == nil { + queue.Add(key) + } + }, + DeleteFunc: func(obj interface{}) { + key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) + log.Infof("Processing delete: %s", key) + if err == nil { + queue.Add(key) + } + }, + }) + return c +} + +func (c *Controller) Run() { + defer c.queue.ShutDown() + + log.Info("Starting Secrets controller") + + go c.informer.Run(c.stop) + + // Wait for the caches to be synced before starting workers + log.Info("Waiting for informer caches to sync") + if !cache.WaitForCacheSync(c.stop, c.informer.HasSynced) { + // utilruntime.HandleError(fmt.Errorf("timed out waiting for caches to sync")) + return + } + wait.Until(c.runWorker, 5*time.Second, c.stop) +} + +func (c *Controller) runWorker() { + for c.processNextItem() { + } +} + +func (c *Controller) processNextItem() bool { + secretName, quit := c.queue.Get() + + if quit { + return false + } + defer c.queue.Done(secretName) + + err := c.processItem(secretName.(string)) + if err == nil { + // No error, reset the ratelimit counters + c.queue.Forget(secretName) + } else if c.queue.NumRequeues(secretName) < maxRetries { + log.Errorf("processing %s err, (will retry): %+v", secretName, err) + c.queue.AddRateLimited(secretName) + } else { + log.Errorf("processing %s err, (giving up): %+v", secretName, err) + c.queue.Forget(secretName) + } + + return true +} + +func (c *Controller) processItem(secretName string) error { + obj, exists, err := c.informer.GetIndexer().GetByKey(secretName) + if err != nil { + return fmt.Errorf("error fetching object %s error: %v", secretName, err) + } + + if exists { + c.addMemberCluster(secretName, obj.(*corev1.Secret)) + } else { + c.deleteMemberCluster(secretName) + } + + return nil +} + +func (c *Controller) addMemberCluster(secretName string, s *corev1.Secret) { + for clusterID, kubeConfig := range s.Data { + // clusterID must be unique even across multiple secrets + if _, ok := c.cs[clusterID]; !ok { + if len(kubeConfig) == 0 { + log.Infof("Data '%s' in the secret %s in namespace %s is empty, and disregarded ", + clusterID, secretName, s.Namespace) + continue + } + + clientConfig, err := clientcmd.Load(kubeConfig) + if err != nil { + log.Infof("Data '%s' in the secret %s in namespace %s is not a kubeconfig: %+v", + clusterID, secretName, s.Namespace, err) + continue + } + + if err := clientcmd.Validate(*clientConfig); err != nil { + log.Errorf("Data '%s' in the secret %s in namespace %s is not a valid kubeconfig: %+v", + clusterID, secretName, s.Namespace, err) + continue + } + + log.Infof("Adding new cluster member: %s", clusterID) + c.cs[clusterID] = secretName + client := clientcmd.NewDefaultClientConfig(*clientConfig, &clientcmd.ConfigOverrides{}) + restConfig, err := client.ClientConfig() + if err != nil { + continue + } + k8sClient, err := kubernetes.NewForConfig(restConfig) + if err != nil { + log.Errorf("error during create of kubernetes client interface for cluster: %s %+v", clusterID, err) + continue + } + for _, f := range c.subscriber { + f(k8sClient) + } + } else { + log.Infof("Cluster %s in the secret %s in namespace %s already exists", + clusterID, c.cs[clusterID], s.Namespace) + } + } + log.Infof("Number of remote clusters: %d", len(c.cs)) +} + +func (c *Controller) deleteMemberCluster(secretName string) { + for clusterID, cluster := range c.cs { + if cluster == secretName { + log.Infof("Deleting cluster member :%s", clusterID) + for _, f := range c.unSubscriber { + f(clusterID) + } + delete(c.cs, clusterID) + } + } + log.Infof("Number of remote clusters: %d", len(c.cs)) +} diff --git a/staging/src/slime.io/slime/modules/limiter/controllers/process.go b/staging/src/slime.io/slime/modules/limiter/controllers/process.go new file mode 100644 index 00000000..3758ffdf --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/controllers/process.go @@ -0,0 +1,380 @@ +/* +* @Author: yangdihang +* @Date: 2020/11/19 + */ + +package controllers + +import ( + "context" + "fmt" + "reflect" + "strconv" + "strings" + + "gopkg.in/yaml.v2" + networking "istio.io/api/networking/v1alpha3" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/json" + + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "slime.io/slime/framework/apis/networking/v1alpha3" + slime_model "slime.io/slime/framework/model" + "slime.io/slime/framework/model/metric" + "slime.io/slime/framework/util" + microservicev1alpha2 "slime.io/slime/modules/limiter/api/v1alpha2" + "slime.io/slime/modules/limiter/model" +) + +// WatchMetric consume metric from chan, which is produced by producer +func (r *SmartLimiterReconciler) WatchMetric() { + log := log.WithField("reporter", "SmartLimiterReconciler").WithField("function", "WatchMetric") + log.Infof("start watching metric") + + for { + select { + case metric, ok := <-r.watcherMetricChan: + if !ok { + log.Warningf("watcher mertic channel closed, break process loop") + return + } + log.Debugf("get metric from watcherMetricChan, %+v", metric) + r.ConsumeMetric(metric) + case metric, ok := <-r.tickerMetricChan: + if !ok { + log.Warningf("ticker metric channel closed, break process loop") + return + } + log.Debugf("get metric from tickerMetricChan, %+v", metric) + r.ConsumeMetric(metric) + } + } +} + +func (r *SmartLimiterReconciler) ConsumeMetric(metricMap metric.Metric) { + for metaInfo, results := range metricMap { + Info := make(map[string]string) + meta := &StaticMeta{} + if err := json.Unmarshal([]byte(metaInfo), meta); err != nil { + log.Errorf("unmarshal static meta info err, %+v", err.Error()) + continue + } + // exact info from meta + namespace, name := meta.Namespace, meta.Name + loc := types.NamespacedName{Namespace: namespace, Name: name} + nPod, isGroup := meta.NPod, meta.IsGroup + + for _, result := range results { + metricName := result.Name + metricValue := "" + + if !isGroup[metricName] { + if len(result.Value) != 1 { + continue + } + for _, value := range result.Value { + metricValue = value + } + Info[metricName] = metricValue + } else { + Info = result.Value + } + } + + // record the number of pods , specified by subset + for subset, number := range nPod { + if number == 0 { + continue + } + Info[subset] = strconv.Itoa(number) + } + log.Debugf("exact metric info, %v", Info) + + // use info to refresh SmartLimiter's status + if _, err := r.Refresh(reconcile.Request{NamespacedName: loc}, Info); err != nil { + log.Errorf("refresh error:%v", err) + } + } +} + +func (r *SmartLimiterReconciler) Refresh(request reconcile.Request, args map[string]string) (reconcile.Result, error) { + _, ok := r.metricInfo.Get(request.Namespace + "/" + request.Name) + if !ok { + r.metricInfo.Set(request.Namespace+"/"+request.Name, &slime_model.Endpoints{ + Location: request.NamespacedName, + Info: args, + }) + } else { + if i, ok := r.metricInfo.Get(request.Namespace + "/" + request.Name); ok { + if ep, ok := i.(*slime_model.Endpoints); ok { + ep.Lock.Lock() + for key, value := range args { + ep.Info[key] = value + } + ep.Lock.Unlock() + } + } + } + + instance := µservicev1alpha2.SmartLimiter{} + err := r.Client.Get(context.TODO(), request.NamespacedName, instance) + if err != nil { + if errors.IsNotFound(err) { + // Request object not found, could have been deleted after reconcile request. + // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers. + // Return and don't requeue + return reconcile.Result{}, nil + } else { + // Error reading the object - requeue the request. + return reconcile.Result{}, err + } + } + + if result, err := r.refresh(instance); err == nil { + return result, nil + } else { + return reconcile.Result{}, err + } +} + +// refresh envoy filters and configmap +func (r *SmartLimiterReconciler) refresh(instance *microservicev1alpha2.SmartLimiter) (reconcile.Result, error) { + var err error + loc := types.NamespacedName{ + Namespace: instance.Namespace, + Name: instance.Name, + } + material := r.getMaterial(loc) + if instance.Spec.Sets == nil { + return reconcile.Result{}, util.Error{M: "invalid rateLimit spec with none sets"} + } + spec := instance.Spec + + var efs map[string]*networking.EnvoyFilter + var descriptor map[string]*microservicev1alpha2.SmartLimitDescriptors + var gdesc []*model.Descriptor + + efs, descriptor, gdesc, err = r.GenerateEnvoyConfigs(spec, material, loc) + if err != nil { + return reconcile.Result{}, err + } + for k, ef := range efs { + var efcr *v1alpha3.EnvoyFilter + if k == util.Wellkonw_BaseSet { + efcr = &v1alpha3.EnvoyFilter{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s.%s.ratelimit", instance.Name, instance.Namespace), + Namespace: instance.Namespace, + }, + } + } else { + efcr = &v1alpha3.EnvoyFilter{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s.%s.%s.ratelimit", instance.Name, instance.Namespace, k), + Namespace: instance.Namespace, + }, + } + } + if ef != nil { + if mi, err := util.ProtoToMap(ef); err == nil { + efcr.Spec = mi + } else { + log.Errorf("proto map err :%+v", err) + } + } + _, err = refreshEnvoyFilter(instance, r, efcr) + if err != nil { + log.Errorf("generated/deleted EnvoyFilter %s failed:%+v", efcr.Name, err) + } + } + if r.env.Config != nil && r.env.Config.Limiter != nil && !r.env.Config.Limiter.GetDisableGlobalRateLimit() { + refreshConfigMap(gdesc, r, loc) + } else { + log.Info("global rate limiter is closed") + } + instance.Status = microservicev1alpha2.SmartLimiterStatus{ + RatelimitStatus: descriptor, + MetricStatus: material, + } + if err = r.Client.Status().Update(context.TODO(), instance); err != nil { + return reconcile.Result{}, err + } + return reconcile.Result{}, nil +} + +// TODO different with old version +// the function will not trigger if subset is changed +// if subset is deleted, how to delete the exist envoyfilters, add anohter function to delete the efs ? +func (r *SmartLimiterReconciler) subscribe(host string, subset interface{}) { + if name, ns, ok := util.IsK8SService(host); ok { + loc := types.NamespacedName{Name: name, Namespace: ns} + instance := µservicev1alpha2.SmartLimiter{} + err := r.Client.Get(context.TODO(), loc, instance) + if err != nil { + if !errors.IsNotFound(err) { + log.Errorf("failed to get smartlimiter, host:%s, %+v", host, err) + } + } else { + _, _ = r.refresh(instance) + } + } +} + +func (r *SmartLimiterReconciler) getMaterial(loc types.NamespacedName) map[string]string { + if i, ok := r.metricInfo.Get(loc.Namespace + "/" + loc.Name); ok { + if ep, ok := i.(*slime_model.Endpoints); ok { + return util.CopyMap(ep.Info) + } + } + return nil +} + +func refreshEnvoyFilter(instance *microservicev1alpha2.SmartLimiter, r *SmartLimiterReconciler, obj *v1alpha3.EnvoyFilter) (reconcile.Result, error) { + if err := controllerutil.SetControllerReference(instance, obj, r.scheme); err != nil { + return reconcile.Result{}, err + } + loc := types.NamespacedName{Name: obj.Name, Namespace: instance.Namespace} + istioRev := slime_model.IstioRevFromLabel(instance.Labels) + slime_model.PatchIstioRevLabel(&obj.Labels, istioRev) + + found := &v1alpha3.EnvoyFilter{} + if err := r.Client.Get(context.TODO(), loc, found); err != nil { + if errors.IsNotFound(err) { + found = nil + err = nil + } else { + return reconcile.Result{}, fmt.Errorf("get envoyfilter err: %+v", err.Error()) + } + } + + // Envoy is found or not + if found == nil { + // found is nil and obj's spec is not nil , create envoyFilter + if obj.Spec != nil { + if err := r.Client.Create(context.TODO(), obj); err != nil { + return reconcile.Result{}, fmt.Errorf("creating a new EnvoyFilter err, %+v", err.Error()) + } + log.Infof("creating a new EnvoyFilter,%+v", loc) + return reconcile.Result{}, nil + } else { + log.Infof("envoyfilter %+v is not found, and obj spec is nil, skip ", loc) + } + } else { + + if slime_model.IstioRevFromLabel(found.Labels) != istioRev { + log.Errorf("existing envoyfilter %v istioRev %s but our %s, skip ...", + loc, slime_model.IstioRevFromLabel(found.Labels), istioRev) + return reconcile.Result{}, nil + } + + foundSpec, err := json.Marshal(found.Spec) + if err != nil { + log.Errorf("marshal found.spec err: %+v", err) + } + objSpec, err := json.Marshal(obj.Spec) + if err != nil { + log.Errorf("marshal obj.spec err: %+v", err) + } + // spec is not nil , update + if obj.Spec != nil { + if !reflect.DeepEqual(string(foundSpec), string(objSpec)) { + obj.ResourceVersion = found.ResourceVersion + err := r.Client.Update(context.TODO(), obj) + if err != nil { + log.Errorf("update envoyfilter err: %+v", err.Error()) + return reconcile.Result{}, err + } + log.Infof("Update a new EnvoyFilter succeed,%v", loc) + // Pod created successfully - don't requeue + return reconcile.Result{}, nil + } + } else { + // spec is nil , delete + err := r.Client.Delete(context.TODO(), obj) + if errors.IsNotFound(err) { + err = nil + } + return reconcile.Result{}, err + } + } + return reconcile.Result{}, nil +} + +// if configmap rate-limit-config not exist, ratelimit server will not running +func refreshConfigMap(desc []*model.Descriptor, r *SmartLimiterReconciler, serviceLoc types.NamespacedName) { + loc := getConfigMapNamespaceName() + + found := &v1.ConfigMap{} + err := r.Client.Get(context.TODO(), loc, found) + if err != nil { + if errors.IsNotFound(err) { + log.Errorf("configmap %s:%s is not found, can not refresh configmap", loc.Namespace, loc.Name) + return + } else { + log.Errorf("get configmap %s:%s err: %+v, cant not refresh configmap", loc.Namespace, loc.Name, err.Error()) + return + } + } + + config, ok := found.Data[model.ConfigMapConfig] + if !ok { + log.Errorf("config.yaml not found in configmap %s:%s", loc.Namespace, loc.Name) + return + } + rc := &model.RateLimitConfig{} + if err = yaml.Unmarshal([]byte(config), &rc); err != nil { + log.Infof("unmarshal ratelimitConfig %s err: %+v", config, err.Error()) + return + } + + newCm := make([]*model.Descriptor, 0) + serviceInfo := fmt.Sprintf("%s.%s", serviceLoc.Name, serviceLoc.Namespace) + for _, item := range rc.Descriptors { + if !strings.Contains(item.Value, serviceInfo) { + newCm = append(newCm, item) + } + } + newCm = append(newCm, desc...) + + configmap := constructConfigMap(newCm) + if !reflect.DeepEqual(found.Data, configmap.Data) { + log.Infof("update configmap %s:%s", loc.Namespace, loc.Name) + configmap.ResourceVersion = found.ResourceVersion + err = r.Client.Update(context.TODO(), configmap) + if err != nil { + log.Infof("update configmap %s:%s err: %+v", loc.Namespace, loc.Name, err.Error()) + return + } + } +} + +func constructConfigMap(desc []*model.Descriptor) *v1.ConfigMap { + rateLimitConfig := &model.RateLimitConfig{ + Domain: model.Domain, + Descriptors: desc, + } + b, _ := yaml.Marshal(rateLimitConfig) + loc := getConfigMapNamespaceName() + configmap := &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: loc.Name, + Namespace: loc.Namespace, + Labels: generateConfigMapLabels(), + }, + Data: map[string]string{ + model.ConfigMapConfig: string(b), + }, + } + return configmap +} + +// TODO query from global config +func generateConfigMapLabels() map[string]string { + labels := make(map[string]string) + labels["app"] = "rate-limit" + return labels +} diff --git a/staging/src/slime.io/slime/modules/limiter/controllers/producer.go b/staging/src/slime.io/slime/modules/limiter/controllers/producer.go new file mode 100644 index 00000000..a873f48e --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/controllers/producer.go @@ -0,0 +1,295 @@ +package controllers + +import ( + stderrors "errors" + "fmt" + "strings" + + prometheusApi "github.com/prometheus/client_golang/api" + prometheusV1 "github.com/prometheus/client_golang/api/prometheus/v1" + "istio.io/api/networking/v1alpha3" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/json" + "k8s.io/client-go/kubernetes" + "slime.io/slime/framework/apis/config/v1alpha1" + "slime.io/slime/framework/bootstrap" + "slime.io/slime/framework/controllers" + "slime.io/slime/framework/model/metric" + "slime.io/slime/framework/model/trigger" + "slime.io/slime/framework/util" +) + +// StaticMeta is static info and do not to query from prometheus +type StaticMeta struct { + Name string `json:"name"` + Namespace string `json:"namespace"` + NPod map[string]int `json:"nPod"` + IsGroup map[string]bool `json:"isGroup"` +} + +func (s StaticMeta) String() string { + b, err := json.Marshal(s) + if err != nil { + log.Errorf("marshal meta err :%v", err.Error()) + return "" + } + return string(b) +} + +// the following functions is registered to framework + +// handleWatcherEvent is triggered by endpoint event +func (r *SmartLimiterReconciler) handleWatcherEvent(event trigger.WatcherEvent) metric.QueryMap { + log.Infof("%v trigger handleWatcherEvent", event) + return r.handleEvent(event.NN) +} + +// handleTickerEvent is triggered by ticker +func (r *SmartLimiterReconciler) handleTickerEvent(event trigger.TickerEvent) metric.QueryMap { + log.Infof("ticker trigger handleTickerEvent") + queryMap := make(map[string][]metric.Handler, 0) + + // traverse interest map + for k := range r.interest.Items() { + item := strings.Split(k, "/") + namespace, name := item[0], item[1] + qm := r.handleEvent(types.NamespacedName{Namespace: namespace, Name: name}) + + for meta, handlers := range qm { + queryMap[meta] = handlers + } + } + return queryMap +} + +func (r *SmartLimiterReconciler) handleEvent(loc types.NamespacedName) metric.QueryMap { + // handle loc which is in interest map + if _, ok := r.interest.Get(loc.Namespace + "/" + loc.Name); !ok { + log.Infof("%v is not in interest map", loc) + return nil + } + if r.env.Config != nil && r.env.Config.Limiter != nil && !r.env.Config.Limiter.GetDisableAdaptive() { + return r.handlePrometheusEvent(loc) + } else { + return r.handleLocalEvent(loc) + } + return nil +} + +func (r *SmartLimiterReconciler) handleLocalEvent(loc types.NamespacedName) metric.QueryMap { + pods, err := queryServicePods(r.env.K8SClient, loc) + if err != nil { + log.Infof("get err in queryServicePods, %+v", err.Error()) + return nil + } + subsetsPods, err := querySubsetPods(pods, loc) + if err != nil { + log.Infof("%+v", err.Error()) + return nil + } + queryMap := make(map[string][]metric.Handler, 0) + meta := generateMeta(subsetsPods, loc) + metaInfo := meta.String() + if metaInfo == "" { + return nil + } + queryMap[metaInfo] = []metric.Handler{} + return queryMap +} + +// handlePrometheusEvent means construct query map as following +// example: handler is a map +// cpu.max => max(container_cpu_usage_seconds_total{namespace="$namespace",pod=~"$pod_name",image=""}) +func (r *SmartLimiterReconciler) handlePrometheusEvent(loc types.NamespacedName) metric.QueryMap { + if r.env.Config == nil || r.env.Config.Metric == nil || r.env.Config.Metric.Prometheus == nil || r.env.Config.Metric.Prometheus.Handlers == nil { + log.Debugf("query handler is empty, skip query") + return nil + } + handlers := r.env.Config.Metric.Prometheus.Handlers + pods, err := queryServicePods(r.env.K8SClient, loc) + if err != nil { + log.Infof("get err in queryServicePods, %+v", err.Error()) + return nil + } + + subsetsPods, err := querySubsetPods(pods, loc) + if err != nil { + log.Infof("%+v", err.Error()) + return nil + } + return generateQueryString(subsetsPods, loc, handlers) +} + +// QueryServicePods query pods related to service, return pods +func queryServicePods(c *kubernetes.Clientset, loc types.NamespacedName) ([]v1.Pod, error) { + var err error + var service *v1.Service + pods := make([]v1.Pod, 0) + + service, err = c.CoreV1().Services(loc.Namespace).Get(loc.Name, metav1.GetOptions{}) + if err != nil { + return pods, fmt.Errorf("get service %+v faild, %s", loc, err.Error()) + } + podList, err := c.CoreV1().Pods(loc.Namespace).List(metav1.ListOptions{ + LabelSelector: labels.SelectorFromSet(service.Spec.Selector).String(), + }) + if err != nil { + return pods, fmt.Errorf("query pod list faild, %+v", err.Error()) + } + + for _, item := range podList.Items { + if item.DeletionTimestamp != nil { + // pod is deleted + continue + } + pods = append(pods, item) + } + return pods, nil +} + +// QuerySubsetPods query pods related to subset +func querySubsetPods(pods []v1.Pod, loc types.NamespacedName) (map[string][]string, error) { + subsetsPods := make(map[string][]string) + host := util.UnityHost(loc.Name, loc.Namespace) + + // if subset is existed, assign pods to subset + if controllers.HostSubsetMapping.Get(host) != nil { + subsets, ok := controllers.HostSubsetMapping.Get(host).([]*v1alpha3.Subset) + if ok { + for _, pod := range pods { + for _, sb := range subsets { + if util.IsContain(pod.Labels, sb.Labels) { + append2Subsets(sb.GetName(), subsetsPods, pod) + } + } + } + } + } + for _, pod := range pods { + append2Base(subsetsPods, pod) + } + return subsetsPods, nil +} + +func append2Subsets(subsetName string, subsetsPods map[string][]string, pod v1.Pod) { + if subsetsPods[subsetName] != nil { + subsetsPods[subsetName] = append(subsetsPods[subsetName], pod.Name) + } else { + subsetsPods[subsetName] = []string{pod.Name} + } +} + +func append2Base(subsetsPods map[string][]string, pod v1.Pod) { + if subsetsPods[util.Wellkonw_BaseSet] != nil { + subsetsPods[util.Wellkonw_BaseSet] = append(subsetsPods[util.Wellkonw_BaseSet], pod.Name) + } else { + subsetsPods[util.Wellkonw_BaseSet] = []string{pod.Name} + } +} + +// GenerateQueryString +func generateQueryString(subsetsPods map[string][]string, loc types.NamespacedName, handlers map[string]*v1alpha1.Prometheus_Source_Handler) map[string][]metric.Handler { + queryMap := make(map[string][]metric.Handler, 0) + queryHandlers := make([]metric.Handler, 0) + isGroup := make(map[string]bool) + + meta := generateMeta(subsetsPods, loc) + + // example + // item => cpu.max: max(container_cpu_usage_seconds_total{namespace="$namespace",pod=~"$pod_name",image=""}) + for customMetricName, handler := range handlers { + if handler.Query == "" { + continue + } + queryHandlers, isGroup = replaceQueryString(customMetricName, handler.Query, handler.Type, loc, subsetsPods) + + for name, group := range isGroup { + meta.IsGroup[name] = group + } + } + metaInfo := meta.String() + if metaInfo == "" { + return queryMap + } + queryMap[metaInfo] = append(queryMap[metaInfo], queryHandlers...) + return queryMap +} + +// some metric is not query from prometheus, so add it to staticMeta +func generateMeta(subsetsPods map[string][]string, loc types.NamespacedName) StaticMeta { + // NPOD record like + // _base.pod: 6 + // v1.pod: 2 + // v2.pod: 4 + nPod := make(map[string]int) + for k, v := range subsetsPods { + if len(v) > 0 { + nPod[k+".pod"] = len(v) + } + } + meta := StaticMeta{ + Name: loc.Name, + Namespace: loc.Namespace, + NPod: nPod, + IsGroup: map[string]bool{}, + } + return meta +} + +func replaceQueryString(metricName string, query string, typ v1alpha1.Prometheus_Source_Type, loc types.NamespacedName, subsetsPods map[string][]string) ([]metric.Handler, map[string]bool) { + handlers := make([]metric.Handler, 0) + isGroup := make(map[string]bool) + query = strings.ReplaceAll(query, "$namespace", loc.Namespace) + + switch typ { + case v1alpha1.Prometheus_Source_Value: + if strings.Contains(query, "$pod_name") { + // each subset will generate a query + for subsetName, subsetPods := range subsetsPods { + subQuery := strings.ReplaceAll(query, "$pod_name", strings.Join(subsetPods, "|")) + + h := metric.Handler{ + Name: subsetName + "." + metricName, + Query: subQuery, + } + // handlers hold all query and real metricName + handlers = append(handlers, h) + isGroup[h.Name] = false + } + } else { + h := metric.Handler{ + Name: metricName, + Query: query, + } + handlers = append(handlers, h) + isGroup[h.Name] = false + } + case v1alpha1.Prometheus_Source_Group: + h := metric.Handler{ + Name: metricName, + Query: query, + } + handlers = append(handlers, h) + isGroup[h.Name] = true + } + return handlers, isGroup +} + +func newPrometheusSourceConfig(env bootstrap.Environment) (metric.PrometheusSourceConfig, error) { + if env.Config == nil || env.Config.Metric == nil || env.Config.Metric.Prometheus == nil { + return metric.PrometheusSourceConfig{}, stderrors.New("failure create prometheus client, empty prometheus config") + } + promClient, err := prometheusApi.NewClient(prometheusApi.Config{ + Address: env.Config.Metric.Prometheus.Address, + RoundTripper: nil, + }) + if err != nil { + return metric.PrometheusSourceConfig{}, err + } + return metric.PrometheusSourceConfig{ + Api: prometheusV1.NewAPI(promClient), + }, nil +} diff --git a/staging/src/slime.io/slime/modules/limiter/controllers/smartlimiter_controller.go b/staging/src/slime.io/slime/modules/limiter/controllers/smartlimiter_controller.go new file mode 100644 index 00000000..1813d501 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/controllers/smartlimiter_controller.go @@ -0,0 +1,203 @@ +/* + + +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 controllers + +import ( + "context" + "os" + "reflect" + "sync" + "time" + + cmap "github.com/orcaman/concurrent-map" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" + "slime.io/slime/framework/bootstrap" + slime_model "slime.io/slime/framework/model" + "slime.io/slime/framework/model/metric" + "slime.io/slime/framework/model/trigger" + microservicev1alpha2 "slime.io/slime/modules/limiter/api/v1alpha2" + "slime.io/slime/modules/limiter/model" +) + +// SmartLimiterReconciler reconciles a SmartLimiter object +type SmartLimiterReconciler struct { + client.Client + Scheme *runtime.Scheme + + cfg *microservicev1alpha2.Limiter + env bootstrap.Environment + scheme *runtime.Scheme + + interest cmap.ConcurrentMap + // reuse, or use anther filed to store interested nn + // key is the interested namespace/name + // value is the metricInfo + metricInfo cmap.ConcurrentMap + MetricSource source.Source + + metricInfoLock sync.RWMutex + + lastUpdatePolicy microservicev1alpha2.SmartLimiterSpec + lastUpdatePolicyLock *sync.RWMutex + + watcherMetricChan <-chan metric.Metric + tickerMetricChan <-chan metric.Metric + // Interest cmap.ConcurrentMap +} + +// +kubebuilder:rbac:groups=microservice.slime.io,resources=smartlimiters,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=microservice.slime.io,resources=smartlimiters/status,verbs=get;update;patch + +func (r *SmartLimiterReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { + _ = context.Background() + log.Debugf("begin reconcile,get smartlimiter %+v", req) + instance := µservicev1alpha2.SmartLimiter{} + if err := r.Client.Get(context.TODO(), req.NamespacedName, instance); err != nil { + if errors.IsNotFound(err) { + instance = nil + err = nil + log.Infof("smartlimiter %v not found", req.NamespacedName) + } else { + log.Errorf("get smartlimiter %v err, %s", req.NamespacedName, err) + return reconcile.Result{}, err + } + } + // deleted + if instance == nil { + log.Infof("metricInfo.Pop, name %s, namespace,%s", req.Name, req.Namespace) + r.metricInfo.Pop(req.Namespace + "/" + req.Name) + r.interest.Pop(req.Namespace + "/" + req.Name) + r.lastUpdatePolicyLock.Lock() + r.lastUpdatePolicy = microservicev1alpha2.SmartLimiterSpec{} + r.lastUpdatePolicyLock.Unlock() + // if contain global smart limiter, should delete info in configmap + if r.env.Config != nil && r.env.Config.Limiter != nil && !r.env.Config.Limiter.GetDisableGlobalRateLimit() { + refreshConfigMap([]*model.Descriptor{}, r, req.NamespacedName) + } else { + log.Info("global rate limiter is closed") + } + return reconcile.Result{}, nil + } else { + // add or update + if !r.env.RevInScope(slime_model.IstioRevFromLabel(instance.Labels)) { + log.Debugf("existing smartlimiter %v istiorev %s but our %s, skip ...", + req.NamespacedName, slime_model.IstioRevFromLabel(instance.Labels), r.env.IstioRev()) + return ctrl.Result{}, nil + } + r.lastUpdatePolicyLock.RLock() + if reflect.DeepEqual(instance.Spec, r.lastUpdatePolicy) { + r.lastUpdatePolicyLock.RUnlock() + return reconcile.Result{}, nil + } else { + r.lastUpdatePolicyLock.RUnlock() + r.lastUpdatePolicyLock.Lock() + r.lastUpdatePolicy = instance.Spec + r.lastUpdatePolicyLock.Unlock() + r.interest.Set(req.Namespace+"/"+req.Name, struct{}{}) + } + } + return ctrl.Result{}, nil +} + +func (r *SmartLimiterReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(µservicev1alpha2.SmartLimiter{}). + Complete(r) +} + +func NewReconciler(mgr ctrl.Manager, env bootstrap.Environment) *SmartLimiterReconciler { + r := &SmartLimiterReconciler{ + Client: mgr.GetClient(), + scheme: mgr.GetScheme(), + metricInfo: cmap.New(), + interest: cmap.New(), + env: env, + lastUpdatePolicyLock: &sync.RWMutex{}, + } + + pc, err := newProducerConfig(env) + if err != nil { + log.Errorf("new producer config err, %v", err) + os.Exit(1) + } + r.watcherMetricChan = pc.WatcherProducerConfig.MetricChan + r.tickerMetricChan = pc.TickerProducerConfig.MetricChan + pc.WatcherProducerConfig.NeedUpdateMetricHandler = r.handleWatcherEvent + pc.TickerProducerConfig.NeedUpdateMetricHandler = r.handleTickerEvent + metric.NewProducer(pc) + log.Infof("producers starts") + + go r.WatchMetric() + return r +} + +func newProducerConfig(env bootstrap.Environment) (*metric.ProducerConfig, error) { + pc := &metric.ProducerConfig{ + EnableWatcherProducer: false, + WatcherProducerConfig: metric.WatcherProducerConfig{ + Name: "smartLimiter-watcher", + MetricChan: make(chan metric.Metric), + WatcherTriggerConfig: trigger.WatcherTriggerConfig{ + Kinds: []schema.GroupVersionKind{ + { + Group: "", + Version: "v1", + Kind: "Endpoints", + }, + }, + EventChan: make(chan trigger.WatcherEvent), + DynamicClient: env.DynamicClient, + }, + }, + EnableTickerProducer: true, + TickerProducerConfig: metric.TickerProducerConfig{ + Name: "smartLimiter-ticker", + MetricChan: make(chan metric.Metric), + TickerTriggerConfig: trigger.TickerTriggerConfig{ + Durations: []time.Duration{ + 30 * time.Second, + }, + EventChan: make(chan trigger.TickerEvent), + }, + }, + StopChan: env.Stop, + } + + if env.Config != nil && env.Config.Limiter != nil && !env.Config.Limiter.GetDisableAdaptive() { + log.Info("enable adaptive ratelimiter") + prometheusSourceConfig, err := newPrometheusSourceConfig(env) + if err != nil { + return nil, err + } + log.Infof("create new prometheus client success") + pc.PrometheusSourceConfig = prometheusSourceConfig + pc.EnablePrometheusSource = true + pc.EnableWatcherProducer = true + } else { + log.Info("disable adaptive ratelimiter and promql is closed") + pc.EnableMockSource = true + } + + return pc, nil +} diff --git a/staging/src/slime.io/slime/modules/limiter/controllers/suite_test.go b/staging/src/slime.io/slime/modules/limiter/controllers/suite_test.go new file mode 100644 index 00000000..85850512 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/controllers/suite_test.go @@ -0,0 +1,78 @@ +/* + + +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 controllers + +import ( + "path/filepath" + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + "sigs.k8s.io/controller-runtime/pkg/envtest/printer" + microservicev1alpha2 "slime.io/slime/modules/limiter/api/v1alpha2" + // +kubebuilder:scaffold:imports +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +var ( + cfg *rest.Config + k8sClient client.Client + testEnv *envtest.Environment +) + +func TestAPIs(t *testing.T) { + RegisterFailHandler(Fail) + + RunSpecsWithDefaultAndCustomReporters(t, + "Controller Suite", + []Reporter{printer.NewlineReporter{}}) +} + +var _ = BeforeSuite(func(done Done) { + By("bootstrapping test environment") + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")}, + } + + var err error + cfg, err = testEnv.Start() + Expect(err).ToNot(HaveOccurred()) + Expect(cfg).ToNot(BeNil()) + + err = microservicev1alpha2.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + // +kubebuilder:scaffold:scheme + + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(err).ToNot(HaveOccurred()) + Expect(k8sClient).ToNot(BeNil()) + + close(done) +}, 60) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + err := testEnv.Stop() + Expect(err).ToNot(HaveOccurred()) +}) diff --git a/staging/src/slime.io/slime/modules/limiter/document/smartlimiter.md b/staging/src/slime.io/slime/modules/limiter/document/smartlimiter.md new file mode 100644 index 00000000..239c9ec1 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/document/smartlimiter.md @@ -0,0 +1,572 @@ +- [Adaptive rate limiting](#adaptive-rate-limiting) + - [Installation and Usage](#installation-and-usage) + - [Installing Prometheus](#installing-prometheus) + - [Installing Rls & Redis](#installing-rls--redis) + - [Install Limiter](#install-limiter) + - [SmartLimiter](#smartlimiter) + - [Single Ratelimit](#single-ratelimit) + - [Global Average Ratelimit](#global-average-ratelimit) + - [Global Shared Ratelimit](#global-shared-ratelimit) + - [Example](#example) + - [global average ratelimit](#global-average-ratelimit-1) + - [global shared ratelimit](#global-shared-ratelimit-1) + - [Troubleshooting](#troubleshooting) +# Adaptive rate limiting + +## Installation and Usage + +Please read the Installing [Prometheus](#installing-prometheus) and Installing [RLS & Redis](#installing-rls--redis) subsections before installing the service + +### Installing Prometheus + +Prometheus is a widely used monitoring system and limiter relies on prometheus to collect metrics. + +A simple Prometheus installation checklist is provided for this purpose, use the following command to install it. + +``` +kubectl apply -f "https://raw.githubusercontent.com/slime-io/limiter/master/install/prometheus.yaml" +``` + +### Installing RLS & Redis + +RLS service is Rate Limit Service [RLS](https://github.com/envoyproxy/ratelimit), we use it to support global shared rate limiting, if you confirm that the service does not need to support global rate limiting you can choose not to install RLS, skip this section. + +A brief introduction to the RLS service is that it is a GO-developed gRPC service that leverages Redis to support fully restricted streaming. After you have configured the SmartLimiter, the resource list will first be transformed into EnvoyFilter, and Istio will send rate limiting restriction rules to the corresponding envoy based on the EnvoyFilter content. + +A simple RLS & Redis installation checklist is provided for this purpose, using the following command + +~~~ +kubectl apply -f "https://raw.githubusercontent.com/slime-io/slime/master/install/rls.yaml" +~~~ + +### Install Limiter + +Please first follow the [slime-boot installation](https://raw.githubusercontent.com/slime-io/slime/master/doc/en/slime-boot.md) guidelines to install slime-boot which is a bootloader for slime. After installation, users can install different slime modules by submitting SlimeBoot resources. + +The user can manually submit the following yaml file to install the limiter module, which SlimeBoot can specify the limiter image version and the specific metrics to be queried. + +A simple SlimeBoot installation checklist is provided for this purpose, using the following command. + +``` +kubectl apply -f "https://raw.githubusercontent.com/slime-io/slime-io/limiter/master/install/limiter.yaml" +``` + +The details of the list are as follows + +```yaml +apiVersion: config.netease.com/v1alpha1 +kind: SlimeBoot +metadata: + name: smartlimiter + namespace: mesh-operator +spec: + image: + pullPolicy: Always + repository: docker.io/slimeio/slime-limiter + tag: v0.2.0_linux_amd64 + module: + - name: limiter # custom value + kind: limiter # should be "limiter" + enable: true + general: # replace previous "limiter" field + backend: 1 + metric: + prometheus: + address: http://prometheus.istio-system:9090 + handlers: + cpu.sum: + query: | + sum(container_cpu_usage_seconds_total{namespace="$namespace",pod=~"$pod_name",image=""}) + cpu.max: + query: | + max(container_cpu_usage_seconds_total{namespace="$namespace",pod=~"$pod_name",image=""}) + rt99: + query: | + histogram_quantile(0.99, sum(rate(istio_request_duration_milliseconds_bucket{kubernetes_pod_name=~"$pod_name"}[2m]))by(le)) + k8s: + handlers: + - pod # inline +``` + +In the above list, we have configured prometheus as the monitoring source by default. prometheus.handlers defines the monitoring metrics that we want to get from prometheus , which can be used as thresholds for some adaptive algorithms to achieve adaptive rate limiting. + + The user can also define the metrics that the limiter module needs to get according to their needs, the following are some of the commonly used statements that can be used to get monitoring metrics. + +``` +cpu: +总和: +sum(rate(container_cpu_usage_seconds_total{namespace="$namespace",pod=~"$pod_name",image=""} [5m])) by(container_name) +最大值: +max(container_cpu_usage_seconds_total{namespace="$namespace",pod=~"$pod_name",image=""}) +limit: +container_spec_cpu_quota{pod=~"$pod_name"} + +内存: +总和: +sum(container_memory_usage_bytes{namespace="$namespace",pod=~"$pod_name",image=""} [5m]) +最大值: +max(container_memory_usage_bytes{namespace="$namespace",pod=~"$pod_name",image=""}) +limit: +sum(container_spec_memory_limit_bytes{pod=~"$pod_name"}) + +请求时延: +90值: +histogram_quantile(0.90, sum(rate(istio_request_duration_milliseconds_bucket{kubernetes_pod_name=~"$pod_name"}[2m]))by(le)) +95值: +histogram_quantile(0.95, sum(rate(istio_request_duration_milliseconds_bucket{kubernetes_pod_name=~"$pod_name"}[2m]))by(le)) +99值: +histogram_quantile(0.99, sum(rate(istio_request_duration_milliseconds_bucket{kubernetes_pod_name=~"$pod_name"}[2m]))by(le)) +``` + +## SmartLimiter + +Definition in [proto](https://raw.githubusercontent.com/slime-io/slime/master/staging/src/slime.io/slime/modules/limiter/master/api/v1alpha2/smart_limiter.proto) + +Note that each service can only create one SmartLimiter resource, whose name and namespace corresponds to the service's name and namespace + +### Single Ratelimit + +The single rate limiting feature sets a fixed rate limiting value for each pod of the service, which relies on the rate limiting capability provided by the envoy plugin envoy.filters.http.local_ratelimit, [Local Ratelimit Plugin](https://www.envoyproxy.io/ docs/envoy/latest/configuration/http/http_filters/local_rate_limit_filter). + +A simple example is that we limit reviews service's request, based on the value of the condition field to determine whether to rate limit, here we directly set true, so that it will perform the rate limit permanently, also the user can set a dynamic value, the limiter will calculate the results, execute dynamic rate limit. fill_interval specifies the flow limit interval to 60s, quota specifies the number of limits 10, strategy identifies the limit is a single limit , target field specifies port 9080. + +```yaml +apiVersion: microservice.slime.io/v1alpha2 +kind: SmartLimiter +metadata: + name: reviews + namespace: default +spec: + sets: + _base: # _base match the service + descriptor: + - action: + fill_interval: + seconds: 60 + quota: '10' + strategy: 'single' + condition: 'true' + #condition: '{{._base.cpu.sum}}>100' + target: + port: 9080 +``` + +### Global Average Ratelimit + +The global average ratelimit feature is based on the total number of ratelimits set by the user and then distributed equally to each pod, relying on the ratelimit capability provided by the envoy plugin envoy.filters.http.local_ratelimit [Local Ratelimit Plugin](https://www. envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/local_rate_limit_filter). + +For a simple example, let's limit the request to reviews service's . + +Here we set true directly to make it permanent, the user can set a dynamic value and the limiter will calculate the result and limit the flow dynamically. fill_interval specifies a limit interval of 60s and quota specifies a limit number of 100/{{. _base.pod}}, The value of {{{._base.pod}} is calculated by the limiter module based on the metric, if the service has 2 pods, then the value of quota is 100/2=50, the strategy field specify to average. + +```yaml +apiVersion: microservice.slime.io/v1alpha2 +kind: SmartLimiter +metadata: + name: reviews + namespace: default +spec: + sets: + _base: + descriptor: + - action: + fill_interval: + seconds: 60 + quota: '100/{{._base.pod}}' + strategy: 'average' + condition: 'true' + target: + port: 9080 +``` + +### Global Shared Ratelimit + +The global shared ratelimit feature maintains a global counter for all pods of the service, relying on the rate limiting capability provided by the envoy plugin nvoy.filters.http.ratelimit [Ratelimit Plugin](https://www.envoyproxy.io/docs/envoy/ latest/configuration/http/http_filters/rate_limit_filter) and the global count capability [RLS](https://github.com/envoyproxy/ratelimit) provided by the RLS service. + +When a global shared rate limiting SmartLimiter is submitted, the limiter module generates EnvoyFilter and a ConfigMap named slime-rate-limit-config based on its contents. EnvoyFilter is watched by Istio and sended to envoy. ConfigMap is mounted to the RLS service, which generates a global shared counter based on the ConfigMap content. + +For a simple example, we execute rate limiting on reviews service, and the meaning of the fields can be found in the above document. The main difference is that the strategy is specified to global and RLS address is speccified, if field rls not specified then the default is outbound|18081||rate-limit.istio-system.svc.cluster.local, which corresponds to the default installed RLS. + + note: due to the requirements of the RLS feature seconds only supports 1, 60, 3600, 86400, i.e. 1 second, 1 minute, 1 hour, 1 day + +```yaml +apiVersion: microservice.slime.io/v1alpha2 +kind: SmartLimiter +metadata: + name: reviews + namespace: default +spec: + sets: + _base: + #rls: 'outbound|18081||rate-limit.istio-system.svc.cluster.local' # default + descriptor: + - action: + fill_interval: + seconds: 60 + quota: '100' + strategy: 'global' + condition: 'true' + target: + port: 9080 +``` + +## Example + +Enable rate limiting for bookinfo's productpage service. + +Note that each service can only create one SmartLimiter resource, whose name and namespace corresponds to the service's name and namespace + +Install istio (1.8+) + +### global average ratelimit + +First, submit a global average SmartLimiter resources to the cluster, which means to limit the productpage service to 10/min + +~~~yaml +apiVersion: microservice.slime.io/v1alpha2 +kind: SmartLimiter +metadata: + name: productpage + namespace: default +spec: + sets: + _base: + descriptor: + - action: + fill_interval: + seconds: 60 + quota: '10/{{._base.pod}}' + strategy: 'average' + condition: 'true' + target: + port: 9080 +~~~ + +Then we qeury the submitted SmartLimiter resource (below, part of the content is removed), in the queried SmartLimiter, there will be more metricStatus and ratelimitStatus contents, which represent the metric obtained from prometheus at this moment, and the currently effective The ratelimit rule is currently in effect. You can see that since the number of pod of the productpage is only 1, the value of all quotas is 10/1 = 10. + +~~~yaml +apiVersion: microservice.slime.io/v1alpha2 +kind: SmartLimiter +metadata: + name: productpage + namespace: default +spec: + sets: + _base: + descriptor: + - action: + fill_interval: + seconds: 60 + quota: 10/{{._base.pod}} + strategy: average + condition: "true" + target: + port: 9080 +status: + metricStatus: + _base.cpu.max: "531.8215157" + _base.pod: "1" + ratelimitStatus: + _base: + descriptor: + - action: + fill_interval: + seconds: 60 + quota: "10" + strategy: average + target: + port: 9080 +~~~ + +Finally, we accessed the productpage service by way of curl, and on the 11th visit '429 Too Many Requests' appeared, which means that the service was restricted, which confirms the validity of the service. + +~~~ +... +... +... +node@ratings-v1-85b8d86597-smv5m:/opt/microservices$ curl -I http://productpage:9080/productpage +HTTP/1.1 200 OK +content-type: text/html; charset=utf-8 +content-length: 3769 +server: envoy +date: Fri, 26 Nov 2021 07:16:21 GMT +x-envoy-upstream-service-time: 9 + +node@ratings-v1-85b8d86597-smv5m:/opt/microservices$ curl -I http://productpage:9080/productpage +HTTP/1.1 429 Too Many Requests +content-length: 18 +content-type: text/plain +date: Fri, 26 Nov 2021 07:16:21 GMT +server: envoy +x-envoy-upstream-service-time: 10 +~~~ + +The following explains the lower level details against the generated EnvoyFilter manifest. + +EnvoyFilter manifest contains 3 patch parts, which are generating genericKey for traffic, enabling envoy.filters.http.local_ratelimit plugin function, and set bucket. + +~~~yaml +apiVersion: networking.istio.io/v1alpha3 +kind: EnvoyFilter +metadata: + creationTimestamp: "2021-11-26T07:09:58Z" + generation: 1 + name: productpage.default.ratelimit + namespace: default + ownerReferences: + - apiVersion: microservice.slime.io/v1alpha2 + blockOwnerDeletion: true + controller: true + kind: SmartLimiter + name: productpage +spec: + configPatches: + - applyTo: HTTP_ROUTE + match: + routeConfiguration: + vhost: + name: inbound|http|9080 + route: + name: default + patch: + operation: MERGE + value: + route: + rate_limits: + - actions: + - generic_key: + descriptor_value: Service[productpage.default]-User[none]-Id[3651085651] + - applyTo: HTTP_FILTER + match: + context: SIDECAR_INBOUND + listener: + filterChain: + filter: + name: envoy.http_connection_manager + subFilter: + name: envoy.router + patch: + operation: INSERT_BEFORE + value: + name: envoy.filters.http.local_ratelimit + typed_config: + '@type': type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit + stat_prefix: http_local_rate_limiter + - applyTo: HTTP_ROUTE + match: + routeConfiguration: + vhost: + name: inbound|http|9080 + route: + name: default + patch: + operation: MERGE + value: + typed_per_filter_config: + envoy.filters.http.local_ratelimit: + '@type': type.googleapis.com/udpa.type.v1.TypedStruct + type_url: type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit + value: + descriptors: + - entries: + - key: generic_key + value: Service[productpage.default]-User[none]-Id[3651085651] + token_bucket: + fill_interval: + seconds: "60" + max_tokens: 10 + tokens_per_fill: + value: 10 + filter_enabled: + default_value: + numerator: 100 + runtime_key: local_rate_limit_enabled + filter_enforced: + default_value: + numerator: 100 + runtime_key: local_rate_limit_enforced + stat_prefix: http_local_rate_limiter + token_bucket: + fill_interval: + seconds: "1" + max_tokens: 100000 + tokens_per_fill: + value: 100000 + workloadSelector: + labels: + app: productpage +~~~ + +### global shared ratelimit + +First, submit a global shared SmartLimiter resource to the cluster, and restrict the productpage service to a total of 10 flows per minute. + +~~~yaml +apiVersion: microservice.slime.io/v1alpha2 +kind: SmartLimiter +metadata: + name: productpage + namespace: default +spec: + sets: + _base: + descriptor: + - action: + fill_interval: + seconds: 60 + quota: "10" + strategy: "global" + condition: "true" + target: + port: 9080 +~~~ + +there will be more metricStatus and ratelimitStatus contents, which represent the metric obtained from prometheus at this moment, and the currently effective The ratelimit rule is currently in effect. + +~~~yaml +kind: SmartLimiter +metadata: + name: productpage + namespace: default +spec: + sets: + _base: + descriptor: + - action: + fill_interval: + seconds: 60 + quota: "10" + strategy: global + condition: "true" + target: + port: 9080 +status: + metricStatus: + _base.cpu.max: "563.3837582" + _base.pod: "1" + ratelimitStatus: + _base: + descriptor: + - action: + fill_interval: + seconds: 60 + quota: "10" + strategy: global + target: + port: 9080 +~~~ + +Finally, we accessed the productpage service by way of curl, and on the 11th visit '429 Too Many Requests' appeared, which means that the service was restricted, which confirms the validity of the service. + +~~~ +... +... +... 省略 + +node@ratings-v1-85b8d86597-smv5m:/opt/microservices$ curl -I http://productpage:9080/productpage +HTTP/1.1 200 OK +content-type: text/html; charset=utf-8 +content-length: 3769 +server: envoy +date: Fri, 26 Nov 2021 07:33:11 GMT +x-envoy-upstream-service-time: 10 + +node@ratings-v1-85b8d86597-smv5m:/opt/microservices$ curl -I http://productpage:9080/productpage +HTTP/1.1 429 Too Many Requests +x-envoy-ratelimited: true +date: Fri, 26 Nov 2021 07:33:11 GMT +server: envoy +x-envoy-upstream-service-time: 1 +transfer-encoding: chunked +~~~ + +The following explains the lower level details against the generated EnvoyFilter manifest. + +EnvoyFilter contains two patches, one is to generate a genericKey for the traffic, the other is to set the RLS service address outbound|18081||rate-limit.istio-system.svc.cluster.local + +~~~ +apiVersion: networking.istio.io/v1alpha3 +kind: EnvoyFilter +metadata: + creationTimestamp: "2021-11-26T07:31:31Z" + name: productpage.default.ratelimit + namespace: default + ownerReferences: + - apiVersion: microservice.slime.io/v1alpha2 + blockOwnerDeletion: true + controller: true + kind: SmartLimiter + name: productpage +spec: + configPatches: + - applyTo: HTTP_ROUTE + match: + routeConfiguration: + vhost: + name: inbound|http|9080 + route: + name: default + patch: + operation: MERGE + value: + route: + rate_limits: + - actions: + - generic_key: + descriptor_value: Service[productpage.default]-User[none]-Id[2970232041] + - applyTo: HTTP_FILTER + match: + context: SIDECAR_INBOUND + listener: + filterChain: + filter: + name: envoy.http_connection_manager + subFilter: + name: envoy.router + patch: + operation: INSERT_BEFORE + value: + name: envoy.filters.http.ratelimit + typed_config: + '@type': type.googleapis.com/udpa.type.v1.TypedStruct + type_url: type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit + value: + domain: slime + rate_limit_service: + grpc_service: + envoy_grpc: + cluster_name: outbound|18081||rate-limit.istio-system.svc.cluster.local + transport_api_version: V3 + workloadSelector: + labels: + app: productpage +~~~ + +The ConfigMap content contains a config.yaml that will be mounted to the /data/ratelimit/config directory of the RLS and will limit the traffic with Service[productpage.default]-User[none]-Id[2970232041] to Limit the traffic 10/min. + +~~~ +apiVersion: v1 +data: + config.yaml: | + domain: slime + descriptors: + - key: generic_key + value: Service[productpage.default]-User[none]-Id[2970232041] + rate_limit: + requests_per_unit: 10 + unit: MINUTE +kind: ConfigMap +metadata: + name: slime-rate-limit-config + namespace: istio-system +~~~ + +## Troubleshooting + +If the rate limiting does not take effect, you can troubleshoot along the following lines + +1. whether the Limiter log is abnormal +2. whether EnvoyFilter or ConfigMap is generated normally +3. use config dump command to see if the envoy rate limit configuration is really in effect +4. whether there is any relevant ConfigMap in the /data/ratelimit/config directory of RLS service (global shared ratelimit) + diff --git a/staging/src/slime.io/slime/modules/limiter/document/smartlimiter_zh.md b/staging/src/slime.io/slime/modules/limiter/document/smartlimiter_zh.md new file mode 100644 index 00000000..7ce340e4 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/document/smartlimiter_zh.md @@ -0,0 +1,576 @@ +- [自适应限流](#自适应限流) + - [安装和使用](#安装和使用) + - [安装 Prometheus](#安装-prometheus) + - [安装 Rls & Redis](#安装-RLS-&-Redis) + - [安装 Limiter](#安装-limiter) + - [SmartLimiter](#smartlimiter) + - [单机限流](#单机限流) + - [全局均分限流](#全局均分限流) + - [全局共享限流](#全局共享限流) + - [实践](#实践) + - [实践1:全局均分](#实践1全局均分) + - [实践2:全局共享](#实践2全局共享) + - [问题排查](#问题排查) +# 自适应限流 + +## 安装和使用 + +在安装服务前请先阅读 [安装Prometheus](#安装-prometheus) 和 [安装 Rls & Redis](#安装-rls-&-redis) + +### 安装 Prometheus + +Prometheus 是一款广泛应用的监控系统,本服务依赖prometheus采集服务指标。 + +为此我们提供了一个简单的Prometheus安装清单,使用以下命令进行安装。 + +``` +kubectl apply -f "https://raw.githubusercontent.com/slime-io/limiter/master/install/prometheus.yaml" +``` + +### 安装 RLS & Redis + +RLS服务即 Rate Limit Service [RLS](https://github.com/envoyproxy/ratelimit) , 我们利用它支持全局共享限流, 如果确认服务不需要支持全局限流可以选择不安装Rls, 跳过该小节。 + +简单介绍下RLS服务,它是一个GO开发的gRPC服务,利用Redis支持了全局限流的功能。当你配置了全局限流SmartLimiter后,该资源清单首先会被转化成EnvoyFilter,Istio会根据EnvoyFilter内容下发限流规则到相应的Envoy,之后Envoy在执行全局限流规则时,会去访问RLS服务,让其决定是否进行限流。 + +为此我们提供了一个简单的 RLS&Redis 安装清单,使用以下命令进行安装。 + +~~~ +kubectl apply -f "https://raw.githubusercontent.com/slime-io/limiter/master/install/rls.yaml" +~~~ + +### 安装 Limiter + +请先按照[slime-boot 安装](https://raw.githubusercontent.com/slime-io/slime/master/doc/zh/slime-boot.md) 指引安装`slime-boot`,该服务是slime的一个引导程序,安装后用户可以通过提交SlimeBoot资源的方式安装不同的slime模块。 + +之后,用户可以手动提交以下yaml 文件,安装 limiter模块,该SlimeBoot可以指定limiter镜像版本以及需要查询的具体指标。 + +为此我们提供了一个简单的SlimeBoot安装清单,使用以下命令进行安装。 + +``` +kubectl apply -f "https://raw.githubusercontent.com/slime-io/slime-io/limiter/master/install/limiter.yaml" +``` + +清单的具体内容如下 + +```yaml +--- +apiVersion: config.netease.com/v1alpha1 +kind: SlimeBoot +metadata: + name: smartlimiter + namespace: mesh-operator +spec: + image: + pullPolicy: Always + repository: docker.io/slimeio/slime-limiter + tag: v0.2.0_linux_amd64 + module: + - name: limiter # custom value + kind: limiter # should be "limiter" + enable: true + general: # replace previous "limiter" field + backend: 1 + metric: + prometheus: + address: http://prometheus.istio-system:9090 + handlers: + cpu.sum: + query: | + sum(container_cpu_usage_seconds_total{namespace="$namespace",pod=~"$pod_name",image=""}) + cpu.max: + query: | + max(container_cpu_usage_seconds_total{namespace="$namespace",pod=~"$pod_name",image=""}) + rt99: + query: | + histogram_quantile(0.99, sum(rate(istio_request_duration_milliseconds_bucket{kubernetes_pod_name=~"$pod_name"}[2m]))by(le)) + k8s: + handlers: + - pod # inline +``` + +在上面清单中,我们默认配置了prometheus作为监控源,prometheus.handlers定义了希望从监控中获取的监控指标,这些监控指标可以作为一些自适应算法的阈值,从而达到自适应限流。 + +用户也可以根据需要定义limiter模块需要获取的监控指标,以下是一些可以常用的监控指标获取语句: + +``` +cpu: +总和: +sum(rate(container_cpu_usage_seconds_total{namespace="$namespace",pod=~"$pod_name",image=""} [5m])) by(container_name) +最大值: +max(container_cpu_usage_seconds_total{namespace="$namespace",pod=~"$pod_name",image=""}) +limit: +container_spec_cpu_quota{pod=~"$pod_name"} + +内存: +总和: +sum(container_memory_usage_bytes{namespace="$namespace",pod=~"$pod_name",image=""} [5m]) +最大值: +max(container_memory_usage_bytes{namespace="$namespace",pod=~"$pod_name",image=""}) +limit: +sum(container_spec_memory_limit_bytes{pod=~"$pod_name"}) + +请求时延: +90值: +histogram_quantile(0.90, sum(rate(istio_request_duration_milliseconds_bucket{kubernetes_pod_name=~"$pod_name"}[2m]))by(le)) +95值: +histogram_quantile(0.95, sum(rate(istio_request_duration_milliseconds_bucket{kubernetes_pod_name=~"$pod_name"}[2m]))by(le)) +99值: +histogram_quantile(0.99, sum(rate(istio_request_duration_milliseconds_bucket{kubernetes_pod_name=~"$pod_name"}[2m]))by(le)) +``` + +## SmartLimiter + +定义见 [proto](https://raw.githubusercontent.com/slime-io/slime/master/staging/src/slime.io/slime/modules/limiter/api/v1alpha2/smart_limiter.proto) + +注意每个服务只能创建一个SmartLimiter资源,其name和namespace对应着service的name和namespace + +### 单机限流 + +单机限流功能替服务的每个pod设置固定的限流数值,其底层是依赖envoy插件envoy.filters.http.local_ratelimit 提供的限流能力,[Local Ratelimit Plugin](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/local_rate_limit_filter)。 + +简单样例如下,我们对reviews服务进行限流,根据condition字段的值判断是否执行限流,这里我们直接设置了true,让其永久执行限流,同样用户可以设置一个动态的值,limiter 会计算其结果,动态的进行限流。fill_interval 指定限流间隔为60s,quota指定限流数量100,strategy标识该限流是单机限流single,target 字段标识需要限流的端口9080。 + +```yaml +apiVersion: microservice.slime.io/v1alpha2 +kind: SmartLimiter +metadata: + name: reviews + namespace: default +spec: + sets: + _base: # 匹配所有服务,关键词 _base ,也可以是你定义的 subset ,如 v1 + descriptor: + - action: # 限流规则 + fill_interval: + seconds: 60 + quota: '100' + strategy: 'single' + condition: 'true' # 永远执行该限流 + #condition: '{{._base.cpu.sum}}>100' 如果服务的所有负载大于100,则执行该限流 + target: + port: 9080 +``` + +### 全局均分限流 + +全局均分限功能根据用户设置的总的限流数,然后平均分配到各个pod,底层同样是依赖envoy插件envoy.filters.http.local_ratelimit 提供的限流能力[Local Ratelimit Plugin](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/local_rate_limit_filter)。 + +简单样例如下,我们对reviews服务进行限流, + +根据condition字段的值判断是否执行限流,这里我们直接设置了true,让其永久执行限流,同样用户可以设置一个动态的值,limiter 会计算其结果,动态的进行限流。fill_interval 指定限流间隔为60s,quota指定限流数量100/{{._base.pod}}, {{._base.pod}}的值是由limiter模块根据metric计算得到,假如该服务有2个副本,那么quota的值为50,strategy标识该限流是均分限流,target 字段标识需要限流的端口9080。 + +```yaml +apiVersion: microservice.slime.io/v1alpha2 +kind: SmartLimiter +metadata: + name: reviews + namespace: default +spec: + sets: + _base: + descriptor: + - action: + fill_interval: + seconds: 60 + quota: '100/{{._base.pod}}' + strategy: 'average' + condition: 'true' + target: + port: 9080 +``` + +### 全局共享限流 + +全局共享限流功能替服务的所有pod维护了一个全局计数器,底层依赖的是envoy插件nvoy.filters.http.ratelimit 提供的限流能力 [Ratelimit Plugin](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/rate_limit_filter) 和RLS服务提供给的全局计数能力[RLS](https://github.com/envoyproxy/ratelimit) 。 + +当提交一个全局共享限流SmartLimiter后,limiter模块会根据其内容生成EnvoyFilter和名为slime-rate-limit-config的ConfigMap。EnvoyFilter会被Istio监听到,下发限流配置至envoy,而ConfigMap则会被挂载到RLS服务,RLS根据ConfigMap内容生成全局共享计数器。 + +简单样例如下,我们对reviews服务进行限流,字段含义可参考上面文档。主要区别在于 strategy为global,并且有rls 地址,如果不指定的话为默认为outbound|18081||rate-limit.istio-system.svc.cluster.local,这对应着默认安装的RLS。注意:由于RLS功能的要求,seconds 只支持 1、60、3600、86400,即1秒、1分钟、1小时、1天 + +```yaml +apiVersion: microservice.slime.io/v1alpha2 +kind: SmartLimiter +metadata: + name: reviews + namespace: default +spec: + sets: + _base: + #rls: 'outbound|18081||rate-limit.istio-system.svc.cluster.local' 如果不指定默认是该地址 + descriptor: + - action: + fill_interval: + seconds: 60 + quota: '100' + strategy: 'global' + condition: 'true' + target: + port: 9080 +``` + +## 实践 + +为bookinfo的productpage服务开启自适应限流功能。 + +注意每个服务只能创建一个SmartLimiter资源,其name和namespace对应着service的name和namespace + +安装 istio (1.8+) + +### 实践1:全局均分 + +首先提交一份全局均分SmartLimiter资源到集群, 对productpage服务进行每分钟均分10次限流。 + +~~~yaml +apiVersion: microservice.slime.io/v1alpha2 +kind: SmartLimiter +metadata: + name: productpage + namespace: default +spec: + sets: + _base: + descriptor: + - action: + fill_interval: + seconds: 60 + quota: '10/{{._base.pod}}' + strategy: 'average' + condition: 'true' + target: + port: 9080 +~~~ + +然后我们查看提交的SmartLimiter资源(如下,部分内容被移除),在查询到的SmartLimiter中,将会多出metricStatus 和 ratelimitStatus 内容,它们分别代表着这一刻从prometheus中获取的metric,以及当前生效的限流规则。可以看到由于productpage的副本数只有1,所有quota的值为10/1 = 10。 + +~~~yaml +apiVersion: microservice.slime.io/v1alpha2 +kind: SmartLimiter +metadata: + name: productpage + namespace: default +spec: + sets: + _base: + descriptor: + - action: + fill_interval: + seconds: 60 + quota: 10/{{._base.pod}} + strategy: average + condition: "true" + target: + port: 9080 +status: + metricStatus: + _base.cpu.max: "531.8215157" + _base.pod: "1" + ratelimitStatus: + _base: + descriptor: + - action: + fill_interval: + seconds: 60 + quota: "10" + strategy: average + target: + port: 9080 +~~~ + +最后,我们通过curl的方式去访问 productpage 服务,在访问第11次的时候出现了429 Too Many Requests,也就是服务被限流了,这也就证实了服务的有效性。 + +~~~ +... +... +... 省略 + +node@ratings-v1-85b8d86597-smv5m:/opt/microservices$ curl -I http://productpage:9080/productpage +HTTP/1.1 200 OK +content-type: text/html; charset=utf-8 +content-length: 3769 +server: envoy +date: Fri, 26 Nov 2021 07:16:21 GMT +x-envoy-upstream-service-time: 9 + +node@ratings-v1-85b8d86597-smv5m:/opt/microservices$ curl -I http://productpage:9080/productpage +HTTP/1.1 429 Too Many Requests +content-length: 18 +content-type: text/plain +date: Fri, 26 Nov 2021 07:16:21 GMT +server: envoy +x-envoy-upstream-service-time: 10 +~~~ + +以下对照生成的EnvoyFilter清单,解释更底层的细节。 + +EnvoyFilter清单包含3个patch部分,分别是给流量生成了genericKey,开启envoy.filters.http.local_ratelimit 插件功能,以及bucket的设置。 + +~~~yaml +apiVersion: networking.istio.io/v1alpha3 +kind: EnvoyFilter +metadata: + creationTimestamp: "2021-11-26T07:09:58Z" + generation: 1 + name: productpage.default.ratelimit + namespace: default + ownerReferences: + - apiVersion: microservice.slime.io/v1alpha2 + blockOwnerDeletion: true + controller: true + kind: SmartLimiter + name: productpage +spec: + configPatches: + - applyTo: HTTP_ROUTE + match: + routeConfiguration: + vhost: + name: inbound|http|9080 + route: + name: default + patch: + operation: MERGE + value: + route: + rate_limits: + - actions: + - generic_key: + descriptor_value: Service[productpage.default]-User[none]-Id[3651085651] + - applyTo: HTTP_FILTER + match: + context: SIDECAR_INBOUND + listener: + filterChain: + filter: + name: envoy.http_connection_manager + subFilter: + name: envoy.router + patch: + operation: INSERT_BEFORE + value: + name: envoy.filters.http.local_ratelimit + typed_config: + '@type': type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit + stat_prefix: http_local_rate_limiter + - applyTo: HTTP_ROUTE + match: + routeConfiguration: + vhost: + name: inbound|http|9080 + route: + name: default + patch: + operation: MERGE + value: + typed_per_filter_config: + envoy.filters.http.local_ratelimit: + '@type': type.googleapis.com/udpa.type.v1.TypedStruct + type_url: type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit + value: + descriptors: + - entries: + - key: generic_key + value: Service[productpage.default]-User[none]-Id[3651085651] + token_bucket: + fill_interval: + seconds: "60" + max_tokens: 10 + tokens_per_fill: + value: 10 + filter_enabled: + default_value: + numerator: 100 + runtime_key: local_rate_limit_enabled + filter_enforced: + default_value: + numerator: 100 + runtime_key: local_rate_limit_enforced + stat_prefix: http_local_rate_limiter + token_bucket: + fill_interval: + seconds: "1" + max_tokens: 100000 + tokens_per_fill: + value: 100000 + workloadSelector: + labels: + app: productpage +~~~ + +### 实践2:全局共享 + +首先提交一份全局共享SmartLimiter资源到集群, 对productpage服务进行每分钟共计10次限流。 + +~~~yaml +apiVersion: microservice.slime.io/v1alpha2 +kind: SmartLimiter +metadata: + name: productpage + namespace: default +spec: + sets: + _base: + descriptor: + - action: + fill_interval: + seconds: 60 + quota: "10" + strategy: "global" + condition: "true" + target: + port: 9080 +~~~ + +然后我们查看提交的SmartLimiter资源(如下,部分内容被移除),在查询到的SmartLimiter中,将会多出了metricStatus 和 ratelimitStatus 内容,它们分别代表着这一刻从prometheus中获取的metric. + +~~~yaml +kind: SmartLimiter +metadata: + name: productpage + namespace: default +spec: + sets: + _base: + descriptor: + - action: + fill_interval: + seconds: 60 + quota: "10" + strategy: global + condition: "true" + target: + port: 9080 +status: + metricStatus: + _base.cpu.max: "563.3837582" + _base.pod: "1" + ratelimitStatus: + _base: + descriptor: + - action: + fill_interval: + seconds: 60 + quota: "10" + strategy: global + target: + port: 9080 +~~~ + +最后,我们通过curl的方式去访问 productpage 服务,在访问第11次的时候出现了429 Too Many Requests,也就是服务被限流了,这也就证实了服务的有效性。 + +~~~ +... +... +... 省略 + +node@ratings-v1-85b8d86597-smv5m:/opt/microservices$ curl -I http://productpage:9080/productpage +HTTP/1.1 200 OK +content-type: text/html; charset=utf-8 +content-length: 3769 +server: envoy +date: Fri, 26 Nov 2021 07:33:11 GMT +x-envoy-upstream-service-time: 10 + +node@ratings-v1-85b8d86597-smv5m:/opt/microservices$ curl -I http://productpage:9080/productpage +HTTP/1.1 429 Too Many Requests +x-envoy-ratelimited: true +date: Fri, 26 Nov 2021 07:33:11 GMT +server: envoy +x-envoy-upstream-service-time: 1 +transfer-encoding: chunked +~~~ + +以下对照生成的EnvoyFilter和ConfigMap清单,解释更底层的细节。 + +在EnvoyFilter中包含两个patch,一个是给流量生成了genericKey,另一个是设置RLS的服务地址outbound|18081||rate-limit.istio-system.svc.cluster.local + +~~~ +apiVersion: networking.istio.io/v1alpha3 +kind: EnvoyFilter +metadata: + creationTimestamp: "2021-11-26T07:31:31Z" + name: productpage.default.ratelimit + namespace: default + ownerReferences: + - apiVersion: microservice.slime.io/v1alpha2 + blockOwnerDeletion: true + controller: true + kind: SmartLimiter + name: productpage +spec: + configPatches: + - applyTo: HTTP_ROUTE + match: + routeConfiguration: + vhost: + name: inbound|http|9080 + route: + name: default + patch: + operation: MERGE + value: + route: + rate_limits: + - actions: + - generic_key: + descriptor_value: Service[productpage.default]-User[none]-Id[2970232041] + - applyTo: HTTP_FILTER + match: + context: SIDECAR_INBOUND + listener: + filterChain: + filter: + name: envoy.http_connection_manager + subFilter: + name: envoy.router + patch: + operation: INSERT_BEFORE + value: + name: envoy.filters.http.ratelimit + typed_config: + '@type': type.googleapis.com/udpa.type.v1.TypedStruct + type_url: type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit + value: + domain: slime + rate_limit_service: + grpc_service: + envoy_grpc: + cluster_name: outbound|18081||rate-limit.istio-system.svc.cluster.local + transport_api_version: V3 + workloadSelector: + labels: + app: productpage +~~~ + +ConfigMap内容中包含了一个config.yaml,其内容是将要挂载到RLS的/data/ratelimit/config目录,对带有Service[productpage.default]-User[none]-Id[2970232041]的流量进行限流10/min. + +~~~ +apiVersion: v1 +data: + config.yaml: | + domain: slime + descriptors: + - key: generic_key + value: Service[productpage.default]-User[none]-Id[2970232041] + rate_limit: + requests_per_unit: 10 + unit: MINUTE +kind: ConfigMap +metadata: + name: slime-rate-limit-config + namespace: istio-system +~~~ + +## 问题排查 + +如果出现限流未生效的情况,可以顺着以下思路进行排查。 + +1. Limiter 日志是否出现异常 +2. EnvoyFilter或者ConfigMap是否正常生成(全局限流) +3. 通过config dump 命令查看envoy限流配置是否真实生效 +4. RLS服务的 /data/ratelimit/config 目录下是否有相关的ConfigMap内容(全局限流) + + + +欢迎交流~~~ + diff --git a/staging/src/slime.io/slime/modules/limiter/go.mod b/staging/src/slime.io/slime/modules/limiter/go.mod new file mode 100644 index 00000000..e17b7ac9 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/go.mod @@ -0,0 +1,29 @@ +module slime.io/slime/modules/limiter + +go 1.13 + +require ( + github.com/envoyproxy/go-control-plane v0.9.9 + github.com/gogo/protobuf v1.3.2 + github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b + github.com/golang/protobuf v1.4.3 + github.com/onsi/ginkgo v1.11.0 + github.com/onsi/gomega v1.8.1 + github.com/orcaman/concurrent-map v0.0.0-20210106121528-16402b402231 + github.com/prometheus/client_golang v1.0.0 + github.com/sirupsen/logrus v1.4.2 + gopkg.in/yaml.v2 v2.3.0 + istio.io/api v0.0.0-20210322145030-ec7ef4cd6eaf + k8s.io/api v0.20.2 + k8s.io/apimachinery v0.20.2 + k8s.io/client-go v0.17.2 + sigs.k8s.io/controller-runtime v0.5.0 + slime.io/slime/framework v0.0.0-00010101000000-000000000000 +) + +replace ( + k8s.io/api => k8s.io/api v0.17.2 + k8s.io/apimachinery => k8s.io/apimachinery v0.17.2 + + slime.io/slime/framework => ../../../../../../framework +) diff --git a/staging/src/slime.io/slime/modules/limiter/go.sum b/staging/src/slime.io/slime/modules/limiter/go.sum new file mode 100644 index 00000000..5d9e3cd7 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/go.sum @@ -0,0 +1,575 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= +github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= +github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed h1:OZmjad4L3H8ncOIR8rnb5MREYqG8ixi5+WbeUsquF0c= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QLSV/BsnenAOcDXdX4cMv4wP0B/5QbPg= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e h1:p1yVGRW3nmb85p1Sh1ZJSDm4A4iKLS5QNbvUHMgGu/M= +github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= +github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9 h1:vQLjymTobffN2R0F8eTqw6q7iozfRO5Z0m+/4Vw+/uA= +github.com/envoyproxy/go-control-plane v0.9.9/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M= +github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= +github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/zapr v0.1.0 h1:h+WVe9j6HAA01niTJPA/kKH0i7e0rLZBCwauQFcRE54= +github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= +github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= +github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= +github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= +github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= +github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= +github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk= +github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= +github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= +github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= +github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= +github.com/go-openapi/spec v0.19.3 h1:0XRyw8kguri6Yw4SxhsQA/atC88yqrk0+G4YhI2wabc= +github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= +github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= +github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= +github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7 h1:u4bArs140e9+AfE52mFHOXVFnOSBJBRlzTHrOPLOIhE= +github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho= +github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995/go.mod h1:lJgMEyOkYFkPcDKwRXegd+iM6E7matEszMG5HhwytU8= +github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk= +github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= +github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= +github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= +github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw= +github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.8.1 h1:C5Dqfs/LeauYDX0jJXIe2SWmwCbGzx9yF8C8xy3Lh34= +github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/orcaman/concurrent-map v0.0.0-20210106121528-16402b402231 h1:fa50YL1pzKW+1SsBnJDOHppJN9stOEwS+CRWyUtyYGU= +github.com/orcaman/concurrent-map v0.0.0-20210106121528-16402b402231/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.0.1 h1:xyiBuvkD2g5n7cYzx6u2sxQvsAy4QJsZFCzGVdzOXZ0= +gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= +gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0 h1:o1bcQ6imQMIOpdrO3SWf2z5RV72WbDwdXuK0MDlc8As= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +istio.io/api v0.0.0-20210322145030-ec7ef4cd6eaf h1:wAkMg4dDRWkas2Gzkln5926OvHw3g/XbKsjICF/msQw= +istio.io/api v0.0.0-20210322145030-ec7ef4cd6eaf/go.mod h1:nsSFw1LIMmGL7r/+6fJI6FxeG/UGlLxRK8bkojIvBVs= +istio.io/gogo-genproto v0.0.0-20210113155706-4daf5697332f h1:9710FpGLvIJ1GGEbpuTh1smVBv+r8cJfR3G82ouSxIQ= +istio.io/gogo-genproto v0.0.0-20210113155706-4daf5697332f/go.mod h1:6BwTZRNbWS570wHX/uR1Wqk5e0157TofTAUMzT7N4+s= +k8s.io/api v0.17.2 h1:NF1UFXcKN7/OOv1uxdRz3qfra8AHsPav5M93hlV9+Dc= +k8s.io/api v0.17.2/go.mod h1:BS9fjjLc4CMuqfSO8vgbHPKMt5+SF0ET6u/RVDihTo4= +k8s.io/apiextensions-apiserver v0.17.2 h1:cP579D2hSZNuO/rZj9XFRzwJNYb41DbNANJb6Kolpss= +k8s.io/apiextensions-apiserver v0.17.2/go.mod h1:4KdMpjkEjjDI2pPfBA15OscyNldHWdBCfsWMDWAmSTs= +k8s.io/apimachinery v0.17.2 h1:hwDQQFbdRlpnnsR64Asdi55GyCaIP/3WQpMmbNBeWr4= +k8s.io/apimachinery v0.17.2/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= +k8s.io/apiserver v0.17.2/go.mod h1:lBmw/TtQdtxvrTk0e2cgtOxHizXI+d0mmGQURIHQZlo= +k8s.io/cli-runtime v0.17.2/go.mod h1:aa8t9ziyQdbkuizkNLAw3qe3srSyWh9zlSB7zTqRNPI= +k8s.io/client-go v0.17.2 h1:ndIfkfXEGrNhLIgkr0+qhRguSD3u6DCmonepn1O6NYc= +k8s.io/client-go v0.17.2/go.mod h1:QAzRgsa0C2xl4/eVpeVAZMvikCn8Nm81yqVx3Kk9XYI= +k8s.io/code-generator v0.17.2/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s= +k8s.io/component-base v0.17.2/go.mod h1:zMPW3g5aH7cHJpKYQ/ZsGMcgbsA/VyhEugF3QT1awLs= +k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= +k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= +k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLyy7rfbeuf1PYyBf973pgyU= +k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= +k8s.io/kubectl v0.17.2 h1:QZR8Q6lWiVRjwKslekdbN5WPMp53dS/17j5e+oi5XVU= +k8s.io/kubectl v0.17.2/go.mod h1:y4rfLV0n6aPmvbRCqZQjvOp3ezxsFgpqL+zF5jH/lxk= +k8s.io/metrics v0.17.2/go.mod h1:3TkNHET4ROd+NfzNxkjoVfQ0Ob4iZnaHmSEA4vYpwLw= +k8s.io/utils v0.0.0-20191114184206-e782cd3c129f h1:GiPwtSzdP43eI1hpPCbROQCCIgCuiMMNF8YUVLF3vJo= +k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= +modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= +modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= +modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= +modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= +sigs.k8s.io/controller-runtime v0.5.0 h1:CbqIy5fbUX+4E9bpnBFd204YAzRYlM9SWW77BbrcDQo= +sigs.k8s.io/controller-runtime v0.5.0/go.mod h1:REiJzC7Y00U+2YkMbT8wxgrsX5USpXKGhb2sCtAXiT8= +sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= +sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= +sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18= +sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI= diff --git a/staging/src/slime.io/slime/modules/limiter/install/easy_install_limiter.sh b/staging/src/slime.io/slime/modules/limiter/install/easy_install_limiter.sh new file mode 100644 index 00000000..def0c872 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/install/easy_install_limiter.sh @@ -0,0 +1,30 @@ +#!/bin/bash +if [[ "$#" -eq 0 ]]; then + echo "No specified tag or commit. Use the latest tag." + slime_tag_or_commit=$(curl -s https://api.github.com/repos/slime-io/slime/tags | grep 'name' | cut -d\" -f4 | head -1) + limiter_tag_or_commit=$(curl -s https://api.github.com/repos/slime-io/limiter/tags | grep 'name' | cut -d\" -f4 | head -1) + if [[ -z $slime_tag_or_commit ]]; then + echo "Failed to get the latest slime tag. Exited." + exit 1 + fi + if [[ -z $limiter_tag_or_commit ]]; then + echo "Failed to get the latest limiter tag. Exited." + exit 1 + fi + echo "The Latest tag: $slime_tag_or_commit." + echo "The Latest tag: $limiter_tag_or_commit." +else + slime_tag_or_commit=$1 + limiter_tag_or_commit=$2 + echo "Use specified slime tag or commit: $slime_tag_or_commit" + echo "Use specified limiter tag or commit: $limiter_tag_or_commit" +fi + +crds_url="https://raw.githubusercontent.com/slime-io/slime/$slime_tag_or_commit/install/init/crds.yaml" +deployment_slimeboot_url="https://raw.githubusercontent.com/slime-io/slime/$slime_tag_or_commit/install/init/deployment_slime-boot.yaml" +slimeboot_smartlimiter_url="https://raw.githubusercontent.com/slime-io/limiter/master/install/limiter.yaml" + +kubectl create ns mesh-operator +kubectl apply -f "${crds_url}" +kubectl apply -f "${deployment_slimeboot_url}" +kubectl apply -f "${slimeboot_smartlimiter_url}" diff --git a/staging/src/slime.io/slime/modules/limiter/install/easy_uninstall_limiter.sh b/staging/src/slime.io/slime/modules/limiter/install/easy_uninstall_limiter.sh new file mode 100644 index 00000000..4e7a6dad --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/install/easy_uninstall_limiter.sh @@ -0,0 +1,31 @@ +#!/bin/bash +if [[ "$#" -eq 0 ]]; then + echo "No specified tag or commit. Use the latest tag." + slime_tag_or_commit=$(curl -s https://api.github.com/repos/slime-io/slime/tags | grep 'name' | cut -d\" -f4 | head -1) + limiter_tag_or_commit=$(curl -s https://api.github.com/repos/slime-io/limiter/tags | grep 'name' | cut -d\" -f4 | head -1) + if [[ -z $slime_tag_or_commit ]]; then + echo "Failed to get the latest slime tag. Exited." + exit 1 + fi + if [[ -z $limiter_tag_or_commit ]]; then + echo "Failed to get the latest limiter tag. Exited." + exit 1 + fi + echo "The Latest tag: $slime_tag_or_commit." + echo "The Latest tag: $limiter_tag_or_commit." +else + slime_tag_or_commit=$1 + limiter_tag_or_commit=$2 + echo "Use specified slime tag or commit: $slime_tag_or_commit" + echo "Use specified limiter tag or commit: $limiter_tag_or_commit" +fi + +crds_url="https://raw.githubusercontent.com/slime-io/slime/$slime_tag_or_commit/install/init/crds.yaml" +deployment_slimeboot_url="https://raw.githubusercontent.com/slime-io/limiter/$slime_tag_or_commit/install/init/deployment_slime-boot.yaml" +slimeboot_smartlimiter_url="https://raw.githubusercontent.com/slime-io/limiter/master/install/limiter.yaml" + +for i in $(kubectl get ns --no-headers |awk '{print $1}');do kubectl delete smartlimiter -n $i --all;done +kubectl delete -f "${slimeboot_smartlimiter_url}" +kubectl delete -f "${deployment_slimeboot_url}" +kubectl delete -f "${crds_url}" +kubectl delete ns mesh-operator diff --git a/staging/src/slime.io/slime/modules/limiter/install/limiter.yaml b/staging/src/slime.io/slime/modules/limiter/install/limiter.yaml new file mode 100644 index 00000000..ebb6dbb4 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/install/limiter.yaml @@ -0,0 +1,33 @@ +--- +apiVersion: config.netease.com/v1alpha1 +kind: SlimeBoot +metadata: + name: smartlimiter + namespace: mesh-operator +spec: + image: + pullPolicy: Always + repository: docker.io/slimeio/slime-limiter + tag: v0.2.0_linux_amd64 + module: + - name: limiter # custom value + kind: limiter # should be "limiter" + enable: true + general: # replace previous "limiter" field + backend: 1 + metric: + prometheus: + address: http://prometheus.istio-system:9090 + handlers: + cpu.sum: + query: | + sum(container_cpu_usage_seconds_total{namespace="$namespace",pod=~"$pod_name",image=""}) + cpu.max: + query: | + max(container_cpu_usage_seconds_total{namespace="$namespace",pod=~"$pod_name",image=""}) + rt99: + query: | + histogram_quantile(0.99, sum(rate(istio_request_duration_milliseconds_bucket{kubernetes_pod_name=~"$pod_name"}[2m]))by(le)) + k8s: + handlers: + - pod # inline diff --git a/staging/src/slime.io/slime/modules/limiter/install/prometheus.yaml b/staging/src/slime.io/slime/modules/limiter/install/prometheus.yaml new file mode 100644 index 00000000..6528d455 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/install/prometheus.yaml @@ -0,0 +1,487 @@ +--- +# Source: prometheus/templates/server/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + component: "server" + app: prometheus + release: prometheus + chart: prometheus-13.6.0 + heritage: Helm + name: prometheus + namespace: istio-system + annotations: + {} +--- +# Source: prometheus/templates/server/cm.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + component: "server" + app: prometheus + release: prometheus + chart: prometheus-13.6.0 + heritage: Helm + name: prometheus + namespace: istio-system +data: + alerting_rules.yml: | + {} + alerts: | + {} + prometheus.yml: | + global: + evaluation_interval: 1m + scrape_interval: 15s + scrape_timeout: 10s + rule_files: + - /etc/config/recording_rules.yml + - /etc/config/alerting_rules.yml + - /etc/config/rules + - /etc/config/alerts + scrape_configs: + - job_name: prometheus + static_configs: + - targets: + - localhost:9090 + - bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token + job_name: kubernetes-apiservers + kubernetes_sd_configs: + - role: endpoints + relabel_configs: + - action: keep + regex: default;kubernetes;https + source_labels: + - __meta_kubernetes_namespace + - __meta_kubernetes_service_name + - __meta_kubernetes_endpoint_port_name + scheme: https + tls_config: + ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + insecure_skip_verify: true + - bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token + job_name: kubernetes-nodes + kubernetes_sd_configs: + - role: node + relabel_configs: + - action: labelmap + regex: __meta_kubernetes_node_label_(.+) + - replacement: kubernetes.default.svc:443 + target_label: __address__ + - regex: (.+) + replacement: /api/v1/nodes/$1/proxy/metrics + source_labels: + - __meta_kubernetes_node_name + target_label: __metrics_path__ + scheme: https + tls_config: + ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + insecure_skip_verify: true + - bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token + job_name: kubernetes-nodes-cadvisor + kubernetes_sd_configs: + - role: node + relabel_configs: + - action: labelmap + regex: __meta_kubernetes_node_label_(.+) + - replacement: kubernetes.default.svc:443 + target_label: __address__ + - regex: (.+) + replacement: /api/v1/nodes/$1/proxy/metrics/cadvisor + source_labels: + - __meta_kubernetes_node_name + target_label: __metrics_path__ + scheme: https + tls_config: + ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + insecure_skip_verify: true + - job_name: kubernetes-service-endpoints + kubernetes_sd_configs: + - role: endpoints + relabel_configs: + - action: keep + regex: true + source_labels: + - __meta_kubernetes_service_annotation_prometheus_io_scrape + - action: replace + regex: (https?) + source_labels: + - __meta_kubernetes_service_annotation_prometheus_io_scheme + target_label: __scheme__ + - action: replace + regex: (.+) + source_labels: + - __meta_kubernetes_service_annotation_prometheus_io_path + target_label: __metrics_path__ + - action: replace + regex: ([^:]+)(?::\d+)?;(\d+) + replacement: $1:$2 + source_labels: + - __address__ + - __meta_kubernetes_service_annotation_prometheus_io_port + target_label: __address__ + - action: labelmap + regex: __meta_kubernetes_service_label_(.+) + - action: replace + source_labels: + - __meta_kubernetes_namespace + target_label: kubernetes_namespace + - action: replace + source_labels: + - __meta_kubernetes_service_name + target_label: kubernetes_name + - action: replace + source_labels: + - __meta_kubernetes_pod_node_name + target_label: kubernetes_node + - job_name: kubernetes-service-endpoints-slow + kubernetes_sd_configs: + - role: endpoints + relabel_configs: + - action: keep + regex: true + source_labels: + - __meta_kubernetes_service_annotation_prometheus_io_scrape_slow + - action: replace + regex: (https?) + source_labels: + - __meta_kubernetes_service_annotation_prometheus_io_scheme + target_label: __scheme__ + - action: replace + regex: (.+) + source_labels: + - __meta_kubernetes_service_annotation_prometheus_io_path + target_label: __metrics_path__ + - action: replace + regex: ([^:]+)(?::\d+)?;(\d+) + replacement: $1:$2 + source_labels: + - __address__ + - __meta_kubernetes_service_annotation_prometheus_io_port + target_label: __address__ + - action: labelmap + regex: __meta_kubernetes_service_label_(.+) + - action: replace + source_labels: + - __meta_kubernetes_namespace + target_label: kubernetes_namespace + - action: replace + source_labels: + - __meta_kubernetes_service_name + target_label: kubernetes_name + - action: replace + source_labels: + - __meta_kubernetes_pod_node_name + target_label: kubernetes_node + scrape_interval: 5m + scrape_timeout: 30s + - honor_labels: true + job_name: prometheus-pushgateway + kubernetes_sd_configs: + - role: service + relabel_configs: + - action: keep + regex: pushgateway + source_labels: + - __meta_kubernetes_service_annotation_prometheus_io_probe + - job_name: kubernetes-services + kubernetes_sd_configs: + - role: service + metrics_path: /probe + params: + module: + - http_2xx + relabel_configs: + - action: keep + regex: true + source_labels: + - __meta_kubernetes_service_annotation_prometheus_io_probe + - source_labels: + - __address__ + target_label: __param_target + - replacement: blackbox + target_label: __address__ + - source_labels: + - __param_target + target_label: instance + - action: labelmap + regex: __meta_kubernetes_service_label_(.+) + - source_labels: + - __meta_kubernetes_namespace + target_label: kubernetes_namespace + - source_labels: + - __meta_kubernetes_service_name + target_label: kubernetes_name + - job_name: kubernetes-pods + kubernetes_sd_configs: + - role: pod + relabel_configs: + - action: keep + regex: true + source_labels: + - __meta_kubernetes_pod_annotation_prometheus_io_scrape + - action: replace + regex: (https?) + source_labels: + - __meta_kubernetes_pod_annotation_prometheus_io_scheme + target_label: __scheme__ + - action: replace + regex: (.+) + source_labels: + - __meta_kubernetes_pod_annotation_prometheus_io_path + target_label: __metrics_path__ + - action: replace + regex: ([^:]+)(?::\d+)?;(\d+) + replacement: $1:$2 + source_labels: + - __address__ + - __meta_kubernetes_pod_annotation_prometheus_io_port + target_label: __address__ + - action: labelmap + regex: __meta_kubernetes_pod_label_(.+) + - action: replace + source_labels: + - __meta_kubernetes_namespace + target_label: kubernetes_namespace + - action: replace + source_labels: + - __meta_kubernetes_pod_name + target_label: kubernetes_pod_name + - action: drop + regex: Pending|Succeeded|Failed + source_labels: + - __meta_kubernetes_pod_phase + - job_name: kubernetes-pods-slow + kubernetes_sd_configs: + - role: pod + relabel_configs: + - action: keep + regex: true + source_labels: + - __meta_kubernetes_pod_annotation_prometheus_io_scrape_slow + - action: replace + regex: (https?) + source_labels: + - __meta_kubernetes_pod_annotation_prometheus_io_scheme + target_label: __scheme__ + - action: replace + regex: (.+) + source_labels: + - __meta_kubernetes_pod_annotation_prometheus_io_path + target_label: __metrics_path__ + - action: replace + regex: ([^:]+)(?::\d+)?;(\d+) + replacement: $1:$2 + source_labels: + - __address__ + - __meta_kubernetes_pod_annotation_prometheus_io_port + target_label: __address__ + - action: labelmap + regex: __meta_kubernetes_pod_label_(.+) + - action: replace + source_labels: + - __meta_kubernetes_namespace + target_label: kubernetes_namespace + - action: replace + source_labels: + - __meta_kubernetes_pod_name + target_label: kubernetes_pod_name + - action: drop + regex: Pending|Succeeded|Failed + source_labels: + - __meta_kubernetes_pod_phase + scrape_interval: 5m + scrape_timeout: 30s + recording_rules.yml: | + {} + rules: | + {} +--- +# Source: prometheus/templates/server/clusterrole.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + component: "server" + app: prometheus + release: prometheus + chart: prometheus-13.6.0 + heritage: Helm + name: prometheus +rules: + - apiGroups: + - "" + resources: + - nodes + - nodes/proxy + - nodes/metrics + - services + - endpoints + - pods + - ingresses + - configmaps + verbs: + - get + - list + - watch + - apiGroups: + - "extensions" + - "networking.k8s.io" + resources: + - ingresses/status + - ingresses + verbs: + - get + - list + - watch + - nonResourceURLs: + - "/metrics" + verbs: + - get +--- +# Source: prometheus/templates/server/clusterrolebinding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + component: "server" + app: prometheus + release: prometheus + chart: prometheus-13.6.0 + heritage: Helm + name: prometheus +subjects: + - kind: ServiceAccount + name: prometheus + namespace: istio-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: prometheus +--- +# Source: prometheus/templates/server/service.yaml +apiVersion: v1 +kind: Service +metadata: + labels: + component: "server" + app: prometheus + release: prometheus + chart: prometheus-13.6.0 + heritage: Helm + name: prometheus + namespace: istio-system +spec: + ports: + - name: http + port: 9090 + protocol: TCP + targetPort: 9090 + selector: + component: "server" + app: prometheus + release: prometheus + sessionAffinity: None + type: "ClusterIP" +--- +# Source: prometheus/templates/server/deploy.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + component: "server" + app: prometheus + release: prometheus + chart: prometheus-13.6.0 + heritage: Helm + name: prometheus + namespace: istio-system +spec: + selector: + matchLabels: + component: "server" + app: prometheus + release: prometheus + replicas: 1 + template: + metadata: + annotations: + + sidecar.istio.io/inject: "false" + labels: + component: "server" + app: prometheus + release: prometheus + chart: prometheus-13.6.0 + heritage: Helm + spec: + serviceAccountName: prometheus + containers: + - name: prometheus-server-configmap-reload + image: "jimmidyson/configmap-reload:v0.5.0" + imagePullPolicy: "IfNotPresent" + args: + - --volume-dir=/etc/config + - --webhook-url=http://127.0.0.1:9090/-/reload + resources: + {} + volumeMounts: + - name: config-volume + mountPath: /etc/config + readOnly: true + + - name: prometheus-server + image: "prom/prometheus:v2.24.0" + imagePullPolicy: "IfNotPresent" + args: + - --storage.tsdb.retention.time=15d + - --config.file=/etc/config/prometheus.yml + - --storage.tsdb.path=/data + - --web.console.libraries=/etc/prometheus/console_libraries + - --web.console.templates=/etc/prometheus/consoles + - --web.enable-lifecycle + ports: + - containerPort: 9090 + readinessProbe: + httpGet: + path: /-/ready + port: 9090 + initialDelaySeconds: 0 + periodSeconds: 5 + timeoutSeconds: 4 + failureThreshold: 3 + successThreshold: 1 + livenessProbe: + httpGet: + path: /-/healthy + port: 9090 + initialDelaySeconds: 30 + periodSeconds: 15 + timeoutSeconds: 10 + failureThreshold: 3 + successThreshold: 1 + resources: + {} + volumeMounts: + - name: config-volume + mountPath: /etc/config + - name: storage-volume + mountPath: /data + subPath: "" + hostNetwork: false + dnsPolicy: ClusterFirst + securityContext: + fsGroup: 65534 + runAsGroup: 65534 + runAsNonRoot: true + runAsUser: 65534 + terminationGracePeriodSeconds: 300 + volumes: + - name: config-volume + configMap: + name: prometheus + - name: storage-volume + emptyDir: + {} diff --git a/staging/src/slime.io/slime/modules/limiter/install/rls.yaml b/staging/src/slime.io/slime/modules/limiter/install/rls.yaml new file mode 100644 index 00000000..dc53c5e1 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/install/rls.yaml @@ -0,0 +1,149 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: slime-rate-limit-config + namespace: istio-system +data: + config.yaml: | + domain: slime + +## redis 服务,依赖 configmap +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: rate-limit-redis + name: rate-limit-redis + namespace: istio-system +spec: + replicas: 1 + selector: + matchLabels: + app: rate-limit-redis + template: + metadata: + labels: + app: rate-limit-redis + spec: + containers: + - env: + - name: MASTER + value: "true" + image: redis:5 + imagePullPolicy: IfNotPresent + name: redis + ports: + - containerPort: 6379 + protocol: TCP + volumeMounts: + - mountPath: /redis-master-data + name: data + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + securityContext: {} + terminationGracePeriodSeconds: 30 + volumes: + - emptyDir: {} + name: data + +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: rate-limit-redis + name: rate-limit-redis + namespace: istio-system +spec: + ports: + - name: one-redis + port: 6379 + protocol: TCP + targetPort: 6379 + selector: + app: rate-limit-redis + type: ClusterIP + + +# rls +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: rate-limit + name: rate-limit + namespace: istio-system +spec: + replicas: 1 + selector: + matchLabels: + app: rate-limit + template: + metadata: + labels: + app: rate-limit + spec: + containers: + - command: + - /bin/ratelimit + env: + - name: GRPC_PORT + value: "18081" + - name: POD_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: LOG_LEVEL + value: debug + - name: USE_STATSD + value: "false" + - name: REDIS_URL + value: rate-limit-redis.istio-system:6379 + - name: REDIS_SOCKET_TYPE + value: tcp + - name: RUNTIME_ROOT + value: /data + - name: RUNTIME_SUBDIRECTORY + value: ratelimit + - name: RUNTIME_IGNOREDOTFILES + value: "true" + - name: RUNTIME_WATCH_ROOT + value: "false" + image: slimeio/ratelimit:v1.0 + imagePullPolicy: IfNotPresent + name: rate-limit + volumeMounts: + - mountPath: /data/ratelimit/config + name: slime-rate-limit-config + volumes: + - configMap: + defaultMode: 420 + name: slime-rate-limit-config + name: slime-rate-limit-config + +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: rate-limit + release: istio-system-rls + name: rate-limit + namespace: istio-system +spec: + ports: + - name: grpc + port: 18081 + protocol: TCP + targetPort: 18081 + - name: config-grpc + port: 16071 + protocol: TCP + targetPort: 6071 + selector: + app: rate-limit + type: ClusterIP diff --git a/staging/src/slime.io/slime/modules/limiter/main.go b/staging/src/slime.io/slime/modules/limiter/main.go new file mode 100644 index 00000000..d65c7c0a --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/main.go @@ -0,0 +1,27 @@ +/* + + +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 ( + "slime.io/slime/framework/model/module" + "slime.io/slime/modules/limiter/model" + modmodule "slime.io/slime/modules/limiter/module" +) + +func main() { + module.Main(model.ModuleName, []module.Module{&modmodule.Module{}}) +} diff --git a/staging/src/slime.io/slime/modules/limiter/media/SmartLimiter.png b/staging/src/slime.io/slime/modules/limiter/media/SmartLimiter.png new file mode 100644 index 00000000..c47515c1 Binary files /dev/null and b/staging/src/slime.io/slime/modules/limiter/media/SmartLimiter.png differ diff --git a/staging/src/slime.io/slime/modules/limiter/media/smartlimiter.jpg b/staging/src/slime.io/slime/modules/limiter/media/smartlimiter.jpg new file mode 100644 index 00000000..56089973 Binary files /dev/null and b/staging/src/slime.io/slime/modules/limiter/media/smartlimiter.jpg differ diff --git a/staging/src/slime.io/slime/modules/limiter/model/const.go b/staging/src/slime.io/slime/modules/limiter/model/const.go new file mode 100644 index 00000000..ad416601 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/model/const.go @@ -0,0 +1,50 @@ +package model + +const ( + ConfigMapName = "slime-rate-limit-config" + + ConfigMapNamespace = "istio-system" + + ConfigMapConfig = "config.yaml" + + GenericKey = "generic_key" + + HeaderValueMatch = "header_match" + + Domain = "slime" + + Inbound = "inbound" + + Outbound = "outbound" + + // AllowAllPort use the implicit semantic "empty means match-all" + AllowAllPort = "" + + GlobalSmartLimiter = "global" + + RateLimitService = "outbound|18081||rate-limit.istio-system.svc.cluster.local" + + TypeUrlEnvoyRateLimit = "type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit" + + StructDomain = "domain" + + StructRateLimitService = "rate_limit_service" + + TypePerFilterConfig = "typed_per_filter_config" + + EnvoyFiltersHttpRateLimit = "envoy.filters.http.ratelimit" + + EnvoyStatPrefix = "stat_prefix" + + EnvoyHttpLocalRateLimiterStatPrefix = "http_local_rate_limiter" + + MetricSourceTypePrometheus = "prometheus" + + MetricSourceTypeLocal = "local" + + MetricSourceType = "metric_source_type" + + InlineMetricPod = "pod" + + InboundDefaultRoute = "default" +) diff --git a/staging/src/slime.io/slime/modules/limiter/model/model.go b/staging/src/slime.io/slime/modules/limiter/model/model.go new file mode 100644 index 00000000..7b5f7357 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/model/model.go @@ -0,0 +1,26 @@ +package model + +import ( + "github.com/sirupsen/logrus" + frameworkmodel "slime.io/slime/framework/model" +) + +const ModuleName = "limiter" + +var ModuleLog = logrus.WithField(frameworkmodel.LogFieldKeyModule, ModuleName) + +type RateLimitConfig struct { + Domain string `yaml:"domain,omitempty"` + Descriptors []*Descriptor `yaml:"descriptors,omitempty"` +} + +type Descriptor struct { + Key string `yaml:"key,omitempty"` + Value string `yaml:"value,omitempty"` + RateLimit *RateLimit `yaml:"rate_limit,omitempty"` +} + +type RateLimit struct { + RequestsPerUnit uint32 `yaml:"requests_per_unit,omitempty"` + Unit string `yaml:"unit,omitempty"` +} diff --git a/staging/src/slime.io/slime/modules/limiter/module/module.go b/staging/src/slime.io/slime/modules/limiter/module/module.go new file mode 100644 index 00000000..34d2fe06 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/module/module.go @@ -0,0 +1,70 @@ +package module + +import ( + "os" + + "slime.io/slime/framework/model/module" + "slime.io/slime/modules/limiter/model" + + "github.com/golang/protobuf/proto" + log "github.com/sirupsen/logrus" + "k8s.io/apimachinery/pkg/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "sigs.k8s.io/controller-runtime/pkg/manager" + istioapi "slime.io/slime/framework/apis" + "slime.io/slime/framework/bootstrap" + istiocontroller "slime.io/slime/framework/controllers" + microservicev1alpha2 "slime.io/slime/modules/limiter/api/v1alpha2" + "slime.io/slime/modules/limiter/controllers" +) + +type Module struct { + config microservicev1alpha2.Limiter +} + +func (m *Module) Kind() string { + return model.ModuleName +} + +func (m *Module) Config() proto.Message { + return &m.config +} + +func (m *Module) InitScheme(scheme *runtime.Scheme) error { + for _, f := range []func(*runtime.Scheme) error{ + clientgoscheme.AddToScheme, + microservicev1alpha2.AddToScheme, + istioapi.AddToScheme, + } { + if err := f(scheme); err != nil { + return err + } + } + return nil +} + +func (m *Module) Clone() module.Module { + ret := *m + return &ret +} + +func (m *Module) InitManager(mgr manager.Manager, env bootstrap.Environment, cbs module.InitCallbacks) error { + reconciler := controllers.NewReconciler(mgr, env) + if err := reconciler.SetupWithManager(mgr); err != nil { + log.Errorf("unable to create controller SmartLimiter, %+v", err) + os.Exit(1) + } + + // add dr reconcile + if err := (&istiocontroller.DestinationRuleReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Env: &env, + }).SetupWithManager(mgr); err != nil { + log.Errorf("unable to create controller DestinationRule, %+v", err) + os.Exit(1) + } + + log.Infof("init manager successful") + return nil +} diff --git a/staging/src/slime.io/slime/modules/limiter/publish.sh b/staging/src/slime.io/slime/modules/limiter/publish.sh new file mode 100644 index 00000000..5d89fbf2 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/publish.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +export MOD=limiter +../../../../../../bin/publish.sh "$@" diff --git a/staging/src/slime.io/slime/modules/limiter/test/e2e/clean.sh b/staging/src/slime.io/slime/modules/limiter/test/e2e/clean.sh new file mode 100644 index 00000000..8f95ed9c --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/test/e2e/clean.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +sed -e 's/{{istioRevKey}}/istioRevKey/g' -e 's/{{istioRevValue}}/istioRevValue/g' ./testdata/install/samples/limit/productpage_smartlimiter.yaml > tmp_productpage_smartlimiter.yaml +kubectl delete -f tmp_productpage_smartlimiter.yaml + +sed -e 's/{{limitTag}}/limitTag/g' ./testdata/install/samples/limit/slimeboot_limit.yaml > tmp_slimeboot_limit.yaml +kubectl delete -f tmp_slimeboot_limit.yaml + +kubectl delete -f ./testdata/install/config/bookinfo.yaml + +sed -e 's/{{slimebootTag}}/slimebootTag/g' ./testdata/install/init/deployment_slime-boot.yaml > tmp_deployment_slime-boot.yaml +kubectl delete -f tmp_deployment_slime-boot.yaml + +kubectl delete -f ./testdata/install/init/crds.yaml + +kubectl delete ns temp + +kubectl delete ns mesh-operator + +rm -f tmp_* diff --git a/staging/src/slime.io/slime/modules/limiter/test/e2e/e2e_test.go b/staging/src/slime.io/slime/modules/limiter/test/e2e/e2e_test.go new file mode 100644 index 00000000..462b4c94 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/test/e2e/e2e_test.go @@ -0,0 +1,48 @@ +package e2e + +import ( + "fmt" + "os" + "path/filepath" + "testing" + + framework2 "slime.io/slime/framework/test/e2e/framework" + "slime.io/slime/framework/test/e2e/framework/testfiles" + + "github.com/golang/glog" + "github.com/onsi/ginkgo" + "github.com/onsi/ginkgo/config" + "github.com/onsi/ginkgo/reporters" + "github.com/onsi/gomega" +) + +func init() { + framework2.RegisterFlags() + if framework2.TestContext.RepoRoot != "" { + testfiles.AddFileSource(testfiles.RootFileSource{Root: framework2.TestContext.RepoRoot}) + } +} + +func TestE2E(t *testing.T) { + RunE2ETests(t) +} + +func RunE2ETests(t *testing.T) { + gomega.RegisterFailHandler(ginkgo.Fail) + + var r []ginkgo.Reporter + ReportDir := "reports" + + if framework2.TestContext.ReportDir != "" { + ReportDir = framework2.TestContext.ReportDir + } + + if err := os.Mkdir(ReportDir, os.ModePerm); err != nil && !os.IsExist(err) { + glog.Fatalf("Failed creating report directory %s ", ReportDir) + } + + r = append(r, reporters.NewJUnitReporter(filepath.Join(ReportDir, fmt.Sprintf("service_%02d.xml", config.GinkgoConfig.ParallelNode)))) + + framework2.Logf("Starting e2e run %q on ginkgo node %d \n", framework2.RunId, config.GinkgoConfig.ParallelNode) + ginkgo.RunSpecsWithDefaultAndCustomReporters(t, "e2e test suite", r) +} diff --git a/staging/src/slime.io/slime/modules/limiter/test/e2e/limiter_test.go b/staging/src/slime.io/slime/modules/limiter/test/e2e/limiter_test.go new file mode 100644 index 00000000..611f3d3b --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/test/e2e/limiter_test.go @@ -0,0 +1,254 @@ +package e2e + +import ( + "os" + "path/filepath" + "strings" + "time" + + "github.com/onsi/ginkgo" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + + "slime.io/slime/framework/test/e2e/framework" + e2epod "slime.io/slime/framework/test/e2e/framework/pod" + "slime.io/slime/framework/test/e2e/framework/testfiles" +) + +var _ = ginkgo.Describe("SmartLimiter e2e test", func() { + f := framework.NewDefaultFramework("limiter") + f.SkipNamespaceCreation = true + + ginkgo.It("prepare slimeboot limiter ns and bookinfos", func() { + _, err := f.CreateNamespace(nsSlime, nil) + framework.ExpectNoError(err) + _, err = f.CreateNamespace(nsApps, map[string]string{istioRevKey: substituteValue("istioRevValue", istioRevValue)}) + framework.ExpectNoError(err) + createSlimeBoot(f) + createSlimeModuleLimiter(f) + createExampleApps(f) + }) + + // all actions + ginkgo.It("all", func() { + _, err := f.CreateNamespace(nsSlime, nil) + framework.ExpectNoError(err) + _, err = f.CreateNamespace(nsApps, map[string]string{istioRevKey: substituteValue("istioRevValue", istioRevValue), "istio-injection": "enabled"}) + framework.ExpectNoError(err) + createSlimeBoot(f) + createSlimeModuleLimiter(f) + createExampleApps(f) + createSmartLimiter(f) + }) + + // just apply a smartlimiter + ginkgo.It("create smartlimiter and envoyfilters", func() { + createSmartLimiter(f) + }) + + // apply a smartlimiter and check whether the limiter take effect + ginkgo.It("verify limiter", func() { + createSmartLimiter(f) + limiterTackEffect(f) + }) +}) + +func createSlimeBoot(f *framework.Framework) { + crdYaml := readFile(test, "init/crds.yaml") + framework.RunKubectlOrDieInput("", crdYaml, "create", "-f", "-") + defer func() { + testResourceToDelete = append(testResourceToDelete, &TestResource{Namespace: "", Contents: crdYaml}) + }() + cs := f.ClientSet + deploySlimeBootYaml := readFile(test, "init/deployment_slime-boot.yaml") + deploySlimeBootYaml = strings.ReplaceAll(deploySlimeBootYaml, "{{slimebootTag}}", substituteValue("slimeBootTag", slimebootTag)) + defer func() { + testResourceToDelete = append(testResourceToDelete, &TestResource{Namespace: nsSlime, Contents: deploySlimeBootYaml}) + }() + framework.RunKubectlOrDieInput(nsSlime, deploySlimeBootYaml, "create", "-f", "-") + slimebootDeploymentInstalled := false + + for i := 0; i < 10; i++ { + pods, err := cs.CoreV1().Pods(nsSlime).List(metav1.ListOptions{}) + framework.ExpectNoError(err) + if len(pods.Items) == 0 { + time.Sleep(500 * time.Millisecond) + continue + } + for _, pod := range pods.Items { + err = e2epod.WaitTimeoutForPodReadyInNamespace(cs, pod.Name, nsSlime, framework.PodStartTimeout) + framework.ExpectNoError(err) + if strings.Contains(pod.Name, slimebootName) { + slimebootDeploymentInstalled = true + } + } + break + } + if !slimebootDeploymentInstalled { + framework.Failf("deployment slime-boot installation failed\n") + } + ginkgo.By("deployment slimeboot installs successfully") +} + +func createSlimeModuleLimiter(f *framework.Framework) { + cs := f.ClientSet + slimebootLimitYaml := readFile(test, "samples/limiter/slimeboot_limiter.yaml") + slimebootLimitYaml = strings.ReplaceAll(slimebootLimitYaml, "{{limitTag}}", substituteValue("limitTag", limitTag)) + framework.RunKubectlOrDieInput(nsSlime, slimebootLimitYaml, "create", "-f", "-") + defer func() { + testResourceToDelete = append(testResourceToDelete, &TestResource{Namespace: nsSlime, Contents: slimebootLimitYaml}) + }() + limitDeploymentInstalled := false + + for i := 0; i < 60; i++ { + pods, err := cs.CoreV1().Pods(nsSlime).List(metav1.ListOptions{}) + framework.ExpectNoError(err) + if len(pods.Items) <= 1 { + time.Sleep(1000 * time.Millisecond) + continue + } + for _, pod := range pods.Items { + err = e2epod.WaitTimeoutForPodReadyInNamespace(cs, pod.Name, nsSlime, framework.PodStartTimeout) + framework.ExpectNoError(err) + if strings.Contains(pod.Name, "limiter") { + limitDeploymentInstalled = true + break + } + } + } + if !limitDeploymentInstalled { + framework.Failf("deployment limiter installation failed\n") + } + ginkgo.By("slimemodule limit installs successfully") +} + +func createExampleApps(f *framework.Framework) { + cs := f.ClientSet + + exampleAppsYaml := readFile(test, "config/bookinfo.yaml") + framework.RunKubectlOrDieInput(nsApps, exampleAppsYaml, "create", "-f", "-") + //defer func() { + // testResourceToDelete = append(testResourceToDelete, &TestResource{Namespace: nsApps, Contents: exampleAppsYaml}) + //}() + + // check + for i := 0; i < 60; i++ { + pods, err := cs.CoreV1().Pods(nsApps).List(metav1.ListOptions{}) + framework.ExpectNoError(err) + if len(pods.Items) != 6 { + time.Sleep(500 * time.Millisecond) + continue + } + for _, pod := range pods.Items { + err = e2epod.WaitTimeoutForPodReadyInNamespace(cs, pod.Name, nsApps, framework.PodStartTimeout) + framework.ExpectNoError(err) + } + break + } + ginkgo.By("example apps install successfully") +} + +func createSmartLimiter(f *framework.Framework) { + smartLimiter := readFile(test, "samples/limiter/productpage_smartlimiter.yaml") + framework.RunKubectlOrDieInput(nsApps, smartLimiter, "create", "-f", "-") + defer func() { + testResourceToDelete = append(testResourceToDelete, &TestResource{Namespace: nsApps, Contents: smartLimiter}) + }() + + smartLimiterGVR := schema.GroupVersionResource{ + Group: slGroup, + Version: slVersion, + Resource: slResource, + } + created := false + for i := 0; i < 60; i++ { + _, err := f.DynamicClient.Resource(smartLimiterGVR).Namespace(nsApps).Get("productpage", metav1.GetOptions{}) + if err != nil { + time.Sleep(500 * time.Millisecond) + continue + } + created = true + break + } + if created != true { + framework.Failf("Failed to create smartLimiter.\n") + } + + created = false + envoyFilterGVR := schema.GroupVersionResource{ + Group: efGroup, + Version: efVersion, + Resource: efResource, + } + for i := 0; i < 30; i++ { + _, err := f.DynamicClient.Resource(envoyFilterGVR).Namespace(nsApps).Get("productpage.temp.ratelimit", metav1.GetOptions{}) + if err != nil { + time.Sleep(1000 * time.Millisecond) + continue + } + created = true + break + } + if created != true { + framework.Failf("Failed to create envoyFilter.\n") + } + ginkgo.By("smartLimiter and envoyFilter create successfully") +} + +// curl -I http://productpage:9080/productpage +// curl -I http://reviews:9080/ +func limiterTackEffect(f *framework.Framework) { + time.Sleep(20 * time.Second) + pods, err := f.ClientSet.CoreV1().Pods(nsApps).List(metav1.ListOptions{}) + framework.ExpectNoError(err) + for _, pod := range pods.Items { + if strings.Contains(pod.Name, "ratings") { + for i := 1; i <= 10; i++ { + output, _, err := f.ExecCommandInContainerWithFullOutput(pod.Name, "ratings", nsApps, "curl", "-I", "http://productpage:9080/productpage") + framework.ExpectNoError(err) + if i < 5 { + if !strings.Contains(output, "200") { + framework.Failf("servers productpage not found\n") + } + } else if !strings.Contains(output, "429") { + framework.Failf("the smartLimiter action 4/min not take effect .\n") + } + time.Sleep(2 * time.Second) + } + } + } + ginkgo.By("smartLimiter action 4/min take effect") +} + +func deleteTestResource() { + for i := len(testResourceToDelete) - 1; i >= 0; i-- { + cleanupKubectlInputs(testResourceToDelete[i].Namespace, testResourceToDelete[i].Contents) + time.Sleep(500 * time.Millisecond) + } +} + +func substituteValue(value, defaultValue string) string { + if os.Getenv(value) != "" { + return os.Getenv(value) + } + return defaultValue +} + +func readFile(test, file string) string { + from := filepath.Join(test, file) + data, err := testfiles.Read(from) + if err != nil { + framework.ExpectNoError(err, "failed to read file %s/%s", test, file) + } + return string(data) +} + +// Stops everything from filePath from namespace ns and checks if everything matching selectors from the given namespace is correctly stopped. +// Aware of the kubectl example files map. +func cleanupKubectlInputs(ns string, fileContents string, selectors ...string) { + ginkgo.By("using delete to clean up resources") + // support backward compatibility : file paths or raw json - since we are removing file path + // dependencies from this test. + framework.RunKubectlOrDieInput(ns, fileContents, "delete", "--grace-period=0", "--force", "-f", "-") + // assertCleanup(ns, selectors...) +} diff --git a/staging/src/slime.io/slime/modules/limiter/test/e2e/reports/service_01.xml b/staging/src/slime.io/slime/modules/limiter/test/e2e/reports/service_01.xml new file mode 100644 index 00000000..7ad8a976 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/test/e2e/reports/service_01.xml @@ -0,0 +1,16 @@ + + + + + + + D:/go/src/liuliluo.com/githubfork/limiter/test/e2e/limiter_test.go:32 Unexpected error: <*errors.errorString | 0xc00032b6b0>: { s: "timed out waiting for the condition", } timed out waiting for the condition occurred D:/go/src/liuliluo.com/githubfork/limiter/test/e2e/limiter_test.go:146 + �[1mSTEP�[0m: [BeforeEach] Creating a kubernetes clientSet Nov 2 23:28:49.420: INFO: >>> kubeConfig: C:\Users\liuliluo/.kube/config �[1mSTEP�[0m: [BeforeEach] Creating a kubernetes dynamic client Nov 2 23:28:49.516: INFO: Running 'C:\Program Files\Docker\Docker\resources\bin\kubectl.exe --kubeconfig=C:\Users\liuliluo/.kube/config create -f -' Nov 2 23:28:50.113: INFO: stderr: "" Nov 2 23:28:50.113: INFO: stdout: "customresourcedefinition.apiextensions.k8s.io/servicefences.microservice.slime.io created\ncustomresourcedefinition.apiextensions.k8s.io/smartlimiters.microservice.slime.io created\ncustomresourcedefinition.apiextensions.k8s.io/envoyplugins.microservice.slime.io created\ncustomresourcedefinition.apiextensions.k8s.io/pluginmanagers.microservice.slime.io created\ncustomresourcedefinition.apiextensions.k8s.io/slimeboots.config.netease.com created\n" Nov 2 23:28:50.208: INFO: Running 'C:\Program Files\Docker\Docker\resources\bin\kubectl.exe --kubeconfig=C:\Users\liuliluo/.kube/config --namespace=mesh-operator create -f -' Nov 2 23:28:50.838: INFO: stderr: "" Nov 2 23:28:50.838: INFO: stdout: "deployment.apps/slime-boot created\nrolebinding.rbac.authorization.k8s.io/slime-boot created\nserviceaccount/slime-boot created\nclusterrolebinding.rbac.authorization.k8s.io/slime-boot created\n" Nov 2 23:28:51.090: INFO: The status of Pod slime-boot-7575d86c95-fwp7p is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:28:53.093: INFO: The status of Pod slime-boot-7575d86c95-fwp7p is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:28:55.092: INFO: The status of Pod slime-boot-7575d86c95-fwp7p is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:28:57.102: INFO: The status of Pod slime-boot-7575d86c95-fwp7p is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:28:59.095: INFO: The status of Pod slime-boot-7575d86c95-fwp7p is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:29:01.105: INFO: The status of Pod slime-boot-7575d86c95-fwp7p is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:29:03.104: INFO: The status of Pod slime-boot-7575d86c95-fwp7p is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:29:05.100: INFO: The status of Pod slime-boot-7575d86c95-fwp7p is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:29:07.093: INFO: The status of Pod slime-boot-7575d86c95-fwp7p is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:29:09.093: INFO: The status of Pod slime-boot-7575d86c95-fwp7p is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:29:11.109: INFO: The status of Pod slime-boot-7575d86c95-fwp7p is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:29:13.094: INFO: The status of Pod slime-boot-7575d86c95-fwp7p is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:29:15.106: INFO: The status of Pod slime-boot-7575d86c95-fwp7p is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:29:17.094: INFO: The status of Pod slime-boot-7575d86c95-fwp7p is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:29:19.094: INFO: The status of Pod slime-boot-7575d86c95-fwp7p is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:29:21.094: INFO: The status of Pod slime-boot-7575d86c95-fwp7p is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:29:23.093: INFO: The status of Pod slime-boot-7575d86c95-fwp7p is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:29:25.107: INFO: The status of Pod slime-boot-7575d86c95-fwp7p is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:29:27.104: INFO: The status of Pod slime-boot-7575d86c95-fwp7p is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:29:29.094: INFO: The status of Pod slime-boot-7575d86c95-fwp7p is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:29:31.094: INFO: The status of Pod slime-boot-7575d86c95-fwp7p is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:29:33.097: INFO: The status of Pod slime-boot-7575d86c95-fwp7p is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:29:35.098: INFO: The status of Pod slime-boot-7575d86c95-fwp7p is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:29:37.094: INFO: The status of Pod slime-boot-7575d86c95-fwp7p is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:29:39.094: INFO: The status of Pod slime-boot-7575d86c95-fwp7p is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:29:41.094: INFO: The status of Pod slime-boot-7575d86c95-fwp7p is Running (Ready = true) �[1mSTEP�[0m: deployment slimeboot installs successfully Nov 2 23:29:41.128: INFO: Running 'C:\Program Files\Docker\Docker\resources\bin\kubectl.exe --kubeconfig=C:\Users\liuliluo/.kube/config --namespace=mesh-operator create -f -' Nov 2 23:29:41.606: INFO: stderr: "" Nov 2 23:29:41.606: INFO: stdout: "slimeboot.config.netease.com/limiter created\n" Nov 2 23:29:43.622: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:29:45.629: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:29:47.639: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:29:49.634: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:29:51.625: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:29:53.631: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:29:55.640: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:29:57.628: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:29:59.638: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:30:01.638: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:30:03.630: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:30:05.635: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:30:07.626: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:30:09.634: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:30:11.636: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:30:13.635: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:30:15.634: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:30:17.635: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:30:19.626: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:30:21.639: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:30:23.626: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:30:25.626: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:30:27.639: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:30:29.628: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:30:31.630: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:30:33.635: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:30:35.636: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:30:37.640: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:30:39.627: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:30:41.637: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:30:43.627: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:30:45.634: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:30:47.643: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:30:49.631: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:30:51.627: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:30:53.631: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:30:55.637: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:30:57.641: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:30:59.635: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:31:01.627: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:31:03.642: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:31:05.630: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:31:07.631: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:31:09.642: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:31:11.636: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:31:13.637: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:31:15.637: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:31:17.632: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:31:19.629: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:31:21.629: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:31:23.628: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:31:25.628: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:31:27.631: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:31:29.637: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:31:31.635: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:31:33.629: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:31:35.628: INFO: The status of Pod limiter-94855cbd7-4rgfq is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:31:37.640: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = false) Nov 2 23:31:39.627: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = false) Nov 2 23:31:41.629: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = false) Nov 2 23:31:43.642: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:43.649: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:43.653: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:43.659: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:43.665: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:43.846: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:44.242: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:44.643: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:45.049: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:45.454: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:45.843: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:46.251: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:46.656: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:47.047: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:47.453: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:47.856: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:48.242: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:48.642: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:49.042: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:49.442: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:49.842: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:50.243: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:50.651: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:51.042: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:51.447: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:51.852: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:52.256: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:52.649: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:53.054: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:53.446: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:53.842: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:54.242: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:54.647: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:55.042: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:55.443: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:55.842: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:56.243: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:56.642: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:57.041: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:57.453: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:57.842: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:58.246: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:58.642: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:59.055: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:59.445: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:31:59.843: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:32:00.241: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:32:00.643: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:32:01.054: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:32:01.450: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:32:01.843: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:32:02.243: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:32:02.642: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:32:03.062: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:32:03.443: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:32:03.848: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:32:04.242: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) Nov 2 23:32:04.643: INFO: The status of Pod limiter-94855cbd7-4rgfq is Running (Ready = true) �[1mSTEP�[0m: slimemodule limit installs successfully Nov 2 23:32:04.683: INFO: Running 'C:\Program Files\Docker\Docker\resources\bin\kubectl.exe --kubeconfig=C:\Users\liuliluo/.kube/config --namespace=temp create -f -' Nov 2 23:32:05.830: INFO: stderr: "" Nov 2 23:32:05.830: INFO: stdout: "service/details created\nserviceaccount/bookinfo-details created\ndeployment.apps/details-v1 created\nservice/ratings created\nserviceaccount/bookinfo-ratings created\ndeployment.apps/ratings-v1 created\nservice/reviews created\nserviceaccount/bookinfo-reviews created\ndeployment.apps/reviews-v1 created\ndeployment.apps/reviews-v2 created\ndeployment.apps/reviews-v3 created\nservice/productpage created\nserviceaccount/bookinfo-productpage created\ndeployment.apps/productpage-v1 created\n" Nov 2 23:32:06.424: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:32:08.428: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:32:10.441: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:32:12.436: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:32:14.430: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:32:16.435: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:32:18.428: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:32:20.442: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:32:22.428: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:32:24.427: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:32:26.435: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:32:28.429: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:32:30.428: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:32:32.438: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:32:34.431: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:32:36.437: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:32:38.435: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:32:40.437: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:32:42.434: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:32:44.428: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:32:46.533: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:32:48.428: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:32:50.430: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:32:52.440: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:32:54.427: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:32:56.438: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:32:58.440: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:33:00.428: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:33:02.443: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:33:04.486: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:33:06.428: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:33:08.436: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:33:10.439: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:33:12.427: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:33:14.427: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:33:16.437: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:33:18.432: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:33:20.440: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:33:22.432: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:33:24.427: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:33:26.443: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:33:28.428: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:33:30.440: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:33:32.436: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:33:34.428: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:33:36.432: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:33:38.431: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:33:40.441: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:33:42.432: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:33:44.427: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:33:46.438: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:33:48.427: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:33:50.442: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:33:52.427: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:33:54.427: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:33:56.427: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:33:58.439: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:34:00.427: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:34:02.428: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:34:04.428: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:34:06.438: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:34:08.428: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:34:10.432: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:34:12.435: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:34:14.430: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:34:16.439: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:34:18.437: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:34:20.427: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:34:22.435: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:34:24.442: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:34:26.427: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:34:28.437: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:34:30.428: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:34:32.437: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:34:34.428: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:34:36.442: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:34:38.428: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:34:40.438: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:34:42.443: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:34:44.428: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:34:46.428: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:34:48.427: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:34:50.437: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:34:52.428: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:34:54.428: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Running (Ready = false) Nov 2 23:34:56.432: INFO: The status of Pod details-v1-79f774bdb9-fljmr is Running (Ready = true) Nov 2 23:34:56.435: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:34:58.439: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:35:00.451: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:35:02.447: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:35:04.439: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:35:06.451: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:35:08.445: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:35:10.444: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:35:12.453: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:35:14.453: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:35:16.447: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:35:18.440: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:35:20.439: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:35:22.439: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:35:24.440: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:35:26.440: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:35:28.440: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:35:30.447: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:35:32.440: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:35:34.441: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:35:36.439: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:35:38.441: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:35:40.440: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:35:42.454: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:35:44.446: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:35:46.450: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:35:48.439: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:35:50.439: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:35:52.447: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:35:54.447: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:35:56.439: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:35:58.442: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:36:00.452: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:36:02.449: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:36:04.440: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:36:06.440: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:36:08.439: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:36:10.445: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:36:12.439: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:36:14.439: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:36:16.439: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:36:18.439: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:36:20.440: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:36:22.440: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:36:24.439: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:36:26.438: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:36:28.440: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:36:30.443: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:36:32.449: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:36:34.449: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:36:36.453: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:36:38.447: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:36:40.446: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:36:42.442: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:36:44.441: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:36:46.441: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:36:48.445: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:36:50.447: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:36:52.453: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:36:54.452: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:36:56.454: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:36:58.440: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:37:00.439: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:37:02.439: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:37:04.449: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:37:06.446: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:37:08.447: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:37:10.450: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:37:12.448: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:37:14.441: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:37:16.448: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:37:18.439: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:37:20.441: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:37:22.442: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:37:24.443: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:37:26.437: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:37:28.444: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:37:30.443: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:37:32.439: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:37:34.443: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:37:36.445: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:37:38.451: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:37:40.439: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:37:42.442: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:37:44.442: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:37:46.445: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:37:48.445: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:37:50.447: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:37:52.447: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:37:54.440: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:37:56.455: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:37:58.447: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:38:00.446: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:38:02.453: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:38:04.440: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:38:06.442: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:38:08.446: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:38:10.441: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:38:12.453: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:38:14.441: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:38:16.443: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:38:18.449: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:38:20.444: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:38:22.442: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:38:24.451: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:38:26.442: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:38:28.440: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:38:30.439: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:38:32.451: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:38:34.438: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:38:36.448: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:38:38.447: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:38:40.452: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:38:42.448: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:38:44.448: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:38:46.450: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:38:48.448: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:38:50.449: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:38:52.450: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:38:54.439: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:38:56.446: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:38:58.440: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:39:00.452: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:39:02.440: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:39:04.447: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:39:06.449: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:39:08.444: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:39:10.439: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:39:12.447: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:39:14.441: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:39:16.439: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:39:18.448: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:39:20.439: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:39:22.439: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:39:24.443: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:39:26.439: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:39:28.449: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:39:30.451: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:39:32.446: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:39:34.439: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:39:36.439: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:39:38.452: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:39:40.440: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:39:42.447: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:39:44.438: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:39:46.438: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:39:48.466: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:39:50.443: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:39:52.448: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:39:54.444: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:39:56.445: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:39:56.447: INFO: The status of Pod productpage-v1-6b746f74dc-q42th is Pending, waiting for it to be Running (with Ready = true) Nov 2 23:39:56.450: INFO: Found DeleteNamespaceOnFailure=false and current test failed, skipping namespace deletion! + + + + + + + + \ No newline at end of file diff --git a/staging/src/slime.io/slime/modules/limiter/test/e2e/testdata/install/config/bookinfo.yaml b/staging/src/slime.io/slime/modules/limiter/test/e2e/testdata/install/config/bookinfo.yaml new file mode 100644 index 00000000..bb5d77dd --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/test/e2e/testdata/install/config/bookinfo.yaml @@ -0,0 +1,343 @@ +# Copyright Istio Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +################################################################################################## +# This file defines the services, service accounts, and deployments for the Bookinfo sample. +# +# To apply all 4 Bookinfo services, their corresponding service accounts, and deployments: +# +# kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml +# +# Alternatively, you can deploy any resource separately: +# +# kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml -l service=reviews # reviews Service +# kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml -l account=reviews # reviews ServiceAccount +# kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml -l app=reviews,version=v3 # reviews-v3 Deployment +################################################################################################## + +################################################################################################## +# Details service +################################################################################################## +apiVersion: v1 +kind: Service +metadata: + name: details + labels: + app: details + service: details +spec: + ports: + - port: 9080 + name: http + selector: + app: details +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: bookinfo-details + labels: + account: details +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: details-v1 + labels: + app: details + version: v1 +spec: + replicas: 1 + selector: + matchLabels: + app: details + version: v1 + template: + metadata: + labels: + app: details + version: v1 + spec: + serviceAccountName: bookinfo-details + containers: + - name: details + image: docker.io/istio/examples-bookinfo-details-v1:1.16.2 + imagePullPolicy: IfNotPresent + ports: + - containerPort: 9080 + securityContext: + runAsUser: 1000 +--- +################################################################################################## +# Ratings service +################################################################################################## +apiVersion: v1 +kind: Service +metadata: + name: ratings + labels: + app: ratings + service: ratings +spec: + ports: + - port: 9080 + name: http + selector: + app: ratings +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: bookinfo-ratings + labels: + account: ratings +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: ratings-v1 + labels: + app: ratings + version: v1 +spec: + replicas: 1 + selector: + matchLabels: + app: ratings + version: v1 + template: + metadata: + labels: + app: ratings + version: v1 + spec: + serviceAccountName: bookinfo-ratings + containers: + - name: ratings + image: docker.io/istio/examples-bookinfo-ratings-v1:1.16.2 + imagePullPolicy: IfNotPresent + ports: + - containerPort: 9080 + securityContext: + runAsUser: 1000 +--- +################################################################################################## +# Reviews service +################################################################################################## +apiVersion: v1 +kind: Service +metadata: + name: reviews + labels: + app: reviews + service: reviews +spec: + ports: + - port: 9080 + name: http + selector: + app: reviews +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: bookinfo-reviews + labels: + account: reviews +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: reviews-v1 + labels: + app: reviews + version: v1 +spec: + replicas: 1 + selector: + matchLabels: + app: reviews + version: v1 + template: + metadata: + labels: + app: reviews + version: v1 + spec: + serviceAccountName: bookinfo-reviews + containers: + - name: reviews + image: docker.io/istio/examples-bookinfo-reviews-v1:1.16.2 + imagePullPolicy: IfNotPresent + env: + - name: LOG_DIR + value: "/tmp/logs" + ports: + - containerPort: 9080 + volumeMounts: + - name: tmp + mountPath: /tmp + - name: wlp-output + mountPath: /opt/ibm/wlp/output + securityContext: + runAsUser: 1000 + volumes: + - name: wlp-output + emptyDir: {} + - name: tmp + emptyDir: {} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: reviews-v2 + labels: + app: reviews + version: v2 +spec: + replicas: 1 + selector: + matchLabels: + app: reviews + version: v2 + template: + metadata: + labels: + app: reviews + version: v2 + spec: + serviceAccountName: bookinfo-reviews + containers: + - name: reviews + image: docker.io/istio/examples-bookinfo-reviews-v2:1.16.2 + imagePullPolicy: IfNotPresent + env: + - name: LOG_DIR + value: "/tmp/logs" + ports: + - containerPort: 9080 + volumeMounts: + - name: tmp + mountPath: /tmp + - name: wlp-output + mountPath: /opt/ibm/wlp/output + securityContext: + runAsUser: 1000 + volumes: + - name: wlp-output + emptyDir: {} + - name: tmp + emptyDir: {} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: reviews-v3 + labels: + app: reviews + version: v3 +spec: + replicas: 1 + selector: + matchLabels: + app: reviews + version: v3 + template: + metadata: + labels: + app: reviews + version: v3 + spec: + serviceAccountName: bookinfo-reviews + containers: + - name: reviews + image: docker.io/istio/examples-bookinfo-reviews-v3:1.16.2 + imagePullPolicy: IfNotPresent + env: + - name: LOG_DIR + value: "/tmp/logs" + ports: + - containerPort: 9080 + volumeMounts: + - name: tmp + mountPath: /tmp + - name: wlp-output + mountPath: /opt/ibm/wlp/output + securityContext: + runAsUser: 1000 + volumes: + - name: wlp-output + emptyDir: {} + - name: tmp + emptyDir: {} +--- +################################################################################################## +# Productpage services +################################################################################################## +apiVersion: v1 +kind: Service +metadata: + name: productpage + labels: + app: productpage + service: productpage +spec: + ports: + - port: 9080 + name: http + selector: + app: productpage +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: bookinfo-productpage + labels: + account: productpage +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: productpage-v1 + labels: + app: productpage + version: v1 +spec: + replicas: 1 + selector: + matchLabels: + app: productpage + version: v1 + template: + metadata: + labels: + app: productpage + version: v1 + spec: + serviceAccountName: bookinfo-productpage + containers: + - name: productpage + image: docker.io/istio/examples-bookinfo-productpage-v1:1.16.2 + imagePullPolicy: IfNotPresent + ports: + - containerPort: 9080 + volumeMounts: + - name: tmp + mountPath: /tmp + securityContext: + runAsUser: 1000 + volumes: + - name: tmp + emptyDir: {} +--- diff --git a/staging/src/slime.io/slime/modules/limiter/test/e2e/testdata/install/init/crds.yaml b/staging/src/slime.io/slime/modules/limiter/test/e2e/testdata/install/init/crds.yaml new file mode 100644 index 00000000..9553318c --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/test/e2e/testdata/install/init/crds.yaml @@ -0,0 +1,101 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: servicefences.microservice.slime.io +spec: + group: microservice.slime.io + names: + shortNames: + - svf + kind: ServiceFence + listKind: ServiceFenceList + plural: servicefences + singular: servicefence + scope: Namespaced + subresources: + status: { } + versions: + - name: v1alpha1 + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: smartlimiters.microservice.slime.io +spec: + group: microservice.slime.io + names: + kind: SmartLimiter + listKind: SmartLimiterList + plural: smartlimiters + singular: smartlimiter + scope: Namespaced + subresources: + status: {} + version: v1alpha2 + versions: + - name: v1alpha2 + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: envoyplugins.microservice.slime.io +spec: + group: microservice.slime.io + names: + shortNames: + - evp + kind: EnvoyPlugin + listKind: EnvoyPluginList + plural: envoyplugins + singular: envoyplugin + scope: Namespaced + versions: + - name: v1alpha1 + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: pluginmanagers.microservice.slime.io +spec: + group: microservice.slime.io + names: + shortNames: + - plm + kind: PluginManager + listKind: PluginManagerList + plural: pluginmanagers + singular: pluginmanager + scope: Namespaced + versions: + - name: v1alpha1 + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: slimeboots.config.netease.com +spec: + group: config.netease.com + names: + kind: SlimeBoot + listKind: SlimeBootList + plural: slimeboots + singular: slimeboot + scope: Namespaced + subresources: + status: {} + validation: + openAPIV3Schema: + type: object + x-kubernetes-preserve-unknown-fields: true + versions: + - name: v1alpha1 + served: true + storage: true diff --git a/staging/src/slime.io/slime/modules/limiter/test/e2e/testdata/install/init/deployment_slime-boot.yaml b/staging/src/slime.io/slime/modules/limiter/test/e2e/testdata/install/init/deployment_slime-boot.yaml new file mode 100644 index 00000000..989d33c9 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/test/e2e/testdata/install/init/deployment_slime-boot.yaml @@ -0,0 +1,63 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: slime-boot + namespace: mesh-operator +spec: + replicas: 1 + selector: + matchLabels: + name: slime-boot + template: + metadata: + labels: + name: slime-boot + spec: + serviceAccountName: slime-boot + containers: + - name: slime-boot + # Replace this with the built image name + image: docker.io/slimeio/slime-boot:{{slimebootTag}} + imagePullPolicy: Always + env: + - name: WATCH_NAMESPACE + value: "" + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: OPERATOR_NAME + value: "slime-boot" +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: slime-boot + namespace: mesh-operator +subjects: + - kind: ServiceAccount + name: slime-boot + namespace: mesh-operator +roleRef: + kind: Role + name: slime-boot + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: slime-boot + namespace: mesh-operator +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: slime-boot +subjects: + - kind: ServiceAccount + name: slime-boot + namespace: mesh-operator +roleRef: + kind: ClusterRole + name: cluster-admin + apiGroup: rbac.authorization.k8s.io diff --git a/staging/src/slime.io/slime/modules/limiter/test/e2e/testdata/install/samples/limiter/productpage_smartlimiter.yaml b/staging/src/slime.io/slime/modules/limiter/test/e2e/testdata/install/samples/limiter/productpage_smartlimiter.yaml new file mode 100644 index 00000000..689ae2fb --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/test/e2e/testdata/install/samples/limiter/productpage_smartlimiter.yaml @@ -0,0 +1,17 @@ +apiVersion: microservice.slime.io/v1alpha2 +kind: SmartLimiter +metadata: + name: productpage + namespace: temp +spec: + sets: + _base: + descriptor: + - action: + fill_interval: + seconds: 60 + quota: "4/{{._base.pod}}" + stragety: "average" + condition: "true" + target: + port: 9080 diff --git a/staging/src/slime.io/slime/modules/limiter/test/e2e/testdata/install/samples/limiter/slimeboot_limiter.yaml b/staging/src/slime.io/slime/modules/limiter/test/e2e/testdata/install/samples/limiter/slimeboot_limiter.yaml new file mode 100644 index 00000000..ced004ba --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/test/e2e/testdata/install/samples/limiter/slimeboot_limiter.yaml @@ -0,0 +1,33 @@ +--- +apiVersion: config.netease.com/v1alpha1 +kind: SlimeBoot +metadata: + name: limiter + namespace: mesh-operator +spec: + image: + pullPolicy: Always + repository: liuliluo/limiter + tag: {{limitTag}} + module: + - enable: true + limiter: + backend: 1 + metric: + k8s: + handlers: + - pod + prometheus: + address: 'http://prometheus.istio-system:9090' + handlers: + cpu.max: + query: > + max(container_cpu_usage_seconds_total{namespace="$namespace",pod=~"$pod_name",image=""}) + cpu.sum: + query: > + sum(container_cpu_usage_seconds_total{namespace="$namespace",pod=~"$pod_name",image=""}) + rt99: + query: > + histogram_quantile(0.99, + sum(rate(istio_request_duration_milliseconds_bucket{kubernetes_pod_name=~"$pod_name"}[2m]))by(le)) + name: limiter diff --git a/staging/src/slime.io/slime/modules/limiter/test/e2e/values.go b/staging/src/slime.io/slime/modules/limiter/test/e2e/values.go new file mode 100644 index 00000000..95107f14 --- /dev/null +++ b/staging/src/slime.io/slime/modules/limiter/test/e2e/values.go @@ -0,0 +1,35 @@ +package e2e + +// the values normally you do not need to change +var ( + testResourceToDelete []*TestResource + slGroup = "microservice.slime.io" + slVersion = "v1alpha2" + slResource = "smartlimiters" + + efGroup = "networking.istio.io" + efVersion = "v1alpha3" + efResource = "envoyfilters" + + istioRevKey = "istio.io/rev" +) + +// the values you can change +// these are only example values, you can change them according to your situation +// you can also use env to override these default values avoiding changing values.go all the time, see func substituteValue in lazyload_test.go +// latest image tag link https://github.com/slime-io/slime/wiki/Slime-Project-Tag-and-Image-Tag-Mapping-Table +var ( + nsSlime = "mesh-operator" // namespace deployed slime + nsApps = "temp" // namespace deployed demo apps + test = "test/e2e/testdata/install" // testdata path + slimebootName = "slime-boot" // consistent with your deployment_slime-boot.yaml + istioRevValue = "1-10-2" // istio revision + slimebootTag = "v0.2.3-5bf313f" // slime-boot image tag, using the link above as reference + limitTag = "v0.4" // limit image tag, using the link above as reference +) + +type TestResource struct { + Namespace string + Contents string + Selectors []string +} diff --git a/staging/src/slime.io/slime/modules/plugin/.idea/.gitignore b/staging/src/slime.io/slime/modules/plugin/.idea/.gitignore new file mode 100644 index 00000000..e69de29b diff --git a/staging/src/slime.io/slime/modules/plugin/.idea/modules.xml b/staging/src/slime.io/slime/modules/plugin/.idea/modules.xml new file mode 100644 index 00000000..c9bea81f --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/staging/src/slime.io/slime/modules/plugin/.idea/plugin.iml b/staging/src/slime.io/slime/modules/plugin/.idea/plugin.iml new file mode 100644 index 00000000..5e764c4f --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/.idea/plugin.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/staging/src/slime.io/slime/modules/plugin/.idea/vcs.xml b/staging/src/slime.io/slime/modules/plugin/.idea/vcs.xml new file mode 100644 index 00000000..94a25f7f --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/staging/src/slime.io/slime/modules/plugin/.idea/workspace.xml b/staging/src/slime.io/slime/modules/plugin/.idea/workspace.xml new file mode 100644 index 00000000..e61bca9b --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/.idea/workspace.xml @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + \ No newline at end of file diff --git a/staging/src/slime.io/slime/modules/plugin/Dockerfile b/staging/src/slime.io/slime/modules/plugin/Dockerfile new file mode 100644 index 00000000..65546b6a --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/Dockerfile @@ -0,0 +1,7 @@ +# Use distroless as minimal base image to package the manager binary +# Refer to https://github.com/GoogleContainerTools/distroless for more details +FROM ubuntu:bionic +WORKDIR / +COPY manager.exe ./manager + +ENTRYPOINT ["/manager"] diff --git a/staging/src/slime.io/slime/modules/plugin/PROJECT b/staging/src/slime.io/slime/modules/plugin/PROJECT new file mode 100644 index 00000000..11f6e064 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/PROJECT @@ -0,0 +1,10 @@ +domain: my.domain +repo: slime.io/slime +resources: +- group: microservice.slime.io + kind: PluginManager + version: v1alpha1 +- group: microservice.slime.io + kind: EnvoyPlugin + version: v1alpha1 +version: "2" diff --git a/staging/src/slime.io/slime/modules/plugin/README.md b/staging/src/slime.io/slime/modules/plugin/README.md new file mode 100644 index 00000000..516191f9 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/README.md @@ -0,0 +1,286 @@ +- [Http Plugin Management](#http-plugin-management) + - [Install & Use](#install--use) + - [Inline plugin](#inline-plugin) + - [PluginManager Example](#pluginmanager-example) + - [EnvoyPlugin Example](#envoyplugin-example) + +[中文](./README_zh.md) + +### Http Plugin Management + +#### Install & Use + +Use the following configuration to install the HTTP plugin management module: + +```yaml +apiVersion: config.netease.com/v1alpha1 +kind: SlimeBoot +metadata: + name: plugin + namespace: mesh-operator +spec: + module: + - name: plugin # custom value + kind: plugin # should be "plugin" + enable: true + image: + pullPolicy: Always + repository: docker.io/slimeio/slime-plugin + tag: {{your_plugin_tag}} +``` + +[Example](./install/samples/plugin/slimeboot_plugin.yaml) + + + +#### Inline plugin + +**Note:** Envoy binary needs to support extension plugins +**enable/disable** +Configure PluginManager in the following format to open the built-in plugin: + +```yaml +apiVersion: microservice.slime.io/v1alpha1 +kind: PluginManager +metadata: + name: reviews-pm + namespace: default +spec: + workload_labels: + app: reviews + plugin: + - enable: true + name: {{plugin-1}} # plugin name + # ... + - enable: true + name: {{plugin-N}} +``` + +{{plugin-N}} is the name of the plug-in, and the sort in PluginManager is the execution order of the plug-in. Set the enable field to false to disable the plugin. + + + +**Global configuration** + +The global configuration corresponds to the plug-in configuration in LDS. Set the global configuration in the following format: + +```yaml +apiVersion: microservice.slime.io/v1alpha1 +kind: PluginManager +metadata: + name: my-plugin + namespace: default +spec: + workload_labels: + app: my-app + plugin: + - enable: true # switch + name: {{plugin-1}} # plugin name + inline: + settings: + {{plugin settings}} # plugin settings + # ... + - enable: true + name: {{plugin-N}} +``` + + + +#### PluginManager Example + +Use the yaml file below to create plugin manager, and enable the plugin reviews-ep + +```yaml +apiVersion: microservice.slime.io/v1alpha1 +kind: PluginManager +metadata: + name: reviews-pm + namespace: default +spec: + workload_labels: + app: reviews + plugin: + - enable: true + name: reviews-ep # plugin name + inline: + settings: + rate_limits: + - actions: + - header_value_match: + descriptor_value: Service[a.powerful]-User[none]-Gateway[null]-Api[null]-Id[hash:-1414739194] + headers: + - invert_match: false + name: testaaa + safe_regex_match: + google_re2: {} + regex: testt + stage: 0 +``` + +And you will get the envoyfilter + +```yaml +$ kubectl -n default get envoyfilter reviews-pm -oyaml +apiVersion: networking.istio.io/v1alpha3 +kind: EnvoyFilter +metadata: + creationTimestamp: "2021-08-26T08:20:56Z" + generation: 1 + name: reviews-pm + namespace: default + ownerReferences: + - apiVersion: microservice.slime.io/v1alpha1 + blockOwnerDeletion: true + controller: true + kind: PluginManager + name: reviews-pm + uid: 00a65d02-4025-4d0c-a08a-0a8901cd0fa2 + resourceVersion: "658741" + uid: 2e8c8a96-fc0d-4e92-9f7a-e3336a53a806 +spec: + configPatches: + - applyTo: HTTP_FILTER + match: + context: SIDECAR_OUTBOUND + listener: + filterChain: + filter: + name: envoy.http_connection_manager + subFilter: + name: envoy.router + patch: + operation: INSERT_BEFORE + value: + name: reviews-ep + typed_config: + '@type': type.googleapis.com/udpa.type.v1.TypedStruct + type_url: "" + value: + rate_limits: + - actions: + - header_value_match: + descriptor_value: Service[a.powerful]-User[none]-Gateway[null]-Api[null]-Id[hash:-1414739194] + headers: + - invert_match: false + name: testaaa + safe_regex_match: + google_re2: {} + regex: testt + stage: 0 + workloadSelector: + labels: + app: reviews +``` + + + +#### EnvoyPlugin Example + +Use the yaml file below to create envoy plugin + +```yaml +apiVersion: microservice.slime.io/v1alpha1 +kind: EnvoyPlugin +metadata: + name: reviews-ep + namespace: default +spec: + workloadSelector: + labels: + app: reviews + route: + - inbound|http|80/default + plugins: + - name: envoy.filters.network.ratelimit + enable: true + inline: + settings: + rate_limits: + - actions: + - header_value_match: + descriptor_value: Service[a.powerful]-User[none]-Gateway[null]-Api[null]-Id[hash:-1414739194] + headers: + - invert_match: false + name: testaaa + safe_regex_match: + google_re2: {} + regex: testt + stage: 0 + - name: envoy.filters.http.cors + enable: true + inline: + settings: + cors: + allow_origin_string_match: + - string_match: + safe_regex_match: + google_re2: {} + regex: www.163.com| +``` + +And you will get the envoyfilter + +```yaml +$ kubectl -n default get envoyfilter reviews-ep -oyaml +apiVersion: networking.istio.io/v1alpha3 +kind: EnvoyFilter +metadata: + creationTimestamp: "2021-08-26T08:13:56Z" + generation: 1 + name: reviews-ep + namespace: default + ownerReferences: + - apiVersion: microservice.slime.io/v1alpha1 + blockOwnerDeletion: true + controller: true + kind: EnvoyPlugin + name: reviews-ep + uid: fcf9d63b-115f-4a2a-bfc4-40d5ce1bcfee + resourceVersion: "658067" + uid: 762768a7-48ae-4939-afa3-f687e0cca826 +spec: + configPatches: + - applyTo: HTTP_ROUTE + match: + routeConfiguration: + vhost: + name: inbound|http|80 + route: + name: default + patch: + operation: MERGE + value: + route: + rate_limits: + - actions: + - header_value_match: + descriptor_value: Service[a.powerful]-User[none]-Gateway[null]-Api[null]-Id[hash:-1414739194] + headers: + - invert_match: false + name: testaaa + safe_regex_match: + google_re2: {} + regex: testt + stage: 0 + - applyTo: HTTP_ROUTE + match: + routeConfiguration: + vhost: + name: inbound|http|80 + route: + name: default + patch: + operation: MERGE + value: + route: + cors: + allow_origin_string_match: + - string_match: + safe_regex_match: + google_re2: {} + regex: www.163.com| + workloadSelector: + labels: + app: reviews +``` + diff --git a/staging/src/slime.io/slime/modules/plugin/README_zh.md b/staging/src/slime.io/slime/modules/plugin/README_zh.md new file mode 100644 index 00000000..b62351d3 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/README_zh.md @@ -0,0 +1,290 @@ +- [HTTP插件管理](#http插件管理) + - [安装和使用](#安装和使用) + - [内建插件](#内建插件) + - [PluginManager样例](#pluginmanager样例) + - [EnvoyPlugin样例](#envoyplugin样例) + +[English](./README.md) + +### HTTP插件管理 + +#### 安装和使用 + +使用如下配置安装HTTP插件管理模块: + +```yaml +apiVersion: config.netease.com/v1alpha1 +kind: SlimeBoot +metadata: + name: plugin + namespace: mesh-operator +spec: + module: + - name: plugin # custom value + kind: plugin # should be "plugin" + enable: true + image: + pullPolicy: Always + repository: docker.io/slimeio/slime-plugin + tag: {{your_plugin_tag}} +``` + +[完整样例](./install/samples/plugin/slimeboot_plugin.yaml) + +pluginmanager和envoyplugin是平级关系。每个envoyplugin可以管理一个envoyfilter,而pluginmanager可以管理多个envoyfilter。 + + + +#### 内建插件 + +**注意:** envoy的二进制需支持扩展插件 + +**打开/停用** + +按如下格式配置PluginManager,即可打开内建插件: + +```yaml +apiVersion: microservice.slime.io/v1alpha1 +kind: PluginManager +metadata: + name: reviews-pm + namespace: default +spec: + workload_labels: + app: reviews + plugin: + - enable: true + name: {{plugin-1}} # plugin name + # ... + - enable: true + name: {{plugin-N}} +``` + +其中,{{plugin-N}}为插件名称,PluginManager中的排序为插件执行顺序。将enable字段设置为false即可停用插件。 + + + +**全局配置** + +全局配置对应LDS中的插件配置,按如下格式设置全局配置: + +```yaml +apiVersion: microservice.slime.io/v1alpha1 +kind: PluginManager +metadata: + name: my-plugin + namespace: default +spec: + workload_labels: + app: my-app + plugin: + - enable: true # switch + name: {{plugin-1}} # plugin name + inline: + settings: + {{plugin_settings}} # plugin settings + # ... + - enable: true + name: {{plugin-N}} +``` + + + +#### PluginManager样例 + +按如下格式配置PluginManager,启用reviews-ep + +```yaml +apiVersion: microservice.slime.io/v1alpha1 +kind: PluginManager +metadata: + name: reviews-pm + namespace: default +spec: + workload_labels: + app: reviews + plugin: + - enable: true + name: reviews-ep # plugin name + inline: + settings: + rate_limits: + - actions: + - header_value_match: + descriptor_value: Service[a.powerful]-User[none]-Gateway[null]-Api[null]-Id[hash:-1414739194] + headers: + - invert_match: false + name: testaaa + safe_regex_match: + google_re2: {} + regex: testt + stage: 0 +``` + +生成EnvoyFilter如下 + +```yaml +$ kubectl -n default get envoyfilter reviews-pm -oyaml +apiVersion: networking.istio.io/v1alpha3 +kind: EnvoyFilter +metadata: + creationTimestamp: "2021-08-26T08:20:56Z" + generation: 1 + name: reviews-pm + namespace: default + ownerReferences: + - apiVersion: microservice.slime.io/v1alpha1 + blockOwnerDeletion: true + controller: true + kind: PluginManager + name: reviews-pm + uid: 00a65d02-4025-4d0c-a08a-0a8901cd0fa2 + resourceVersion: "658741" + uid: 2e8c8a96-fc0d-4e92-9f7a-e3336a53a806 +spec: + configPatches: + - applyTo: HTTP_FILTER + match: + context: SIDECAR_OUTBOUND + listener: + filterChain: + filter: + name: envoy.http_connection_manager + subFilter: + name: envoy.router + patch: + operation: INSERT_BEFORE + value: + name: reviews-ep + typed_config: + '@type': type.googleapis.com/udpa.type.v1.TypedStruct + type_url: "" + value: + rate_limits: + - actions: + - header_value_match: + descriptor_value: Service[a.powerful]-User[none]-Gateway[null]-Api[null]-Id[hash:-1414739194] + headers: + - invert_match: false + name: testaaa + safe_regex_match: + google_re2: {} + regex: testt + stage: 0 + workloadSelector: + labels: + app: reviews +``` + + + +#### EnvoyPlugin样例 + +按如下格式配置EnvoyPlugin: + +```yaml +apiVersion: microservice.slime.io/v1alpha1 +kind: EnvoyPlugin +metadata: + name: reviews-ep + namespace: default +spec: + workloadSelector: + labels: + app: reviews + route: + - inbound|http|80/default + plugins: + - name: envoy.filters.network.ratelimit + enable: true + inline: + settings: + rate_limits: + - actions: + - header_value_match: + descriptor_value: Service[a.powerful]-User[none]-Gateway[null]-Api[null]-Id[hash:-1414739194] + headers: + - invert_match: false + name: testaaa + safe_regex_match: + google_re2: {} + regex: testt + stage: 0 + - name: envoy.filters.http.cors + enable: true + inline: + settings: + cors: + allow_origin_string_match: + - string_match: + safe_regex_match: + google_re2: {} + regex: www.163.com| +``` + +生成的envoyfilter如下 + +```sh +$ kubectl -n default get envoyfilter reviews-ep -oyaml +apiVersion: networking.istio.io/v1alpha3 +kind: EnvoyFilter +metadata: + creationTimestamp: "2021-08-26T08:13:56Z" + generation: 1 + name: reviews-ep + namespace: default + ownerReferences: + - apiVersion: microservice.slime.io/v1alpha1 + blockOwnerDeletion: true + controller: true + kind: EnvoyPlugin + name: reviews-ep + uid: fcf9d63b-115f-4a2a-bfc4-40d5ce1bcfee + resourceVersion: "658067" + uid: 762768a7-48ae-4939-afa3-f687e0cca826 +spec: + configPatches: + - applyTo: HTTP_ROUTE + match: + routeConfiguration: + vhost: + name: inbound|http|80 + route: + name: default + patch: + operation: MERGE + value: + route: + rate_limits: + - actions: + - header_value_match: + descriptor_value: Service[a.powerful]-User[none]-Gateway[null]-Api[null]-Id[hash:-1414739194] + headers: + - invert_match: false + name: testaaa + safe_regex_match: + google_re2: {} + regex: testt + stage: 0 + - applyTo: HTTP_ROUTE + match: + routeConfiguration: + vhost: + name: inbound|http|80 + route: + name: default + patch: + operation: MERGE + value: + route: + cors: + allow_origin_string_match: + - string_match: + safe_regex_match: + google_re2: {} + regex: www.163.com| + workloadSelector: + labels: + app: reviews +``` + diff --git a/staging/src/slime.io/slime/modules/plugin/api/v1alpha1/envoy_plugin.pb.go b/staging/src/slime.io/slime/modules/plugin/api/v1alpha1/envoy_plugin.pb.go new file mode 100644 index 00000000..a46afc09 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/api/v1alpha1/envoy_plugin.pb.go @@ -0,0 +1,266 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: envoy_plugin.proto + +package v1alpha1 + +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// `WorkloadSelector` specifies the criteria used to determine if the +// `Gateway`, `Sidecar`, or `EnvoyFilter` or `ServiceEntry` +// configuration can be applied to a proxy. The matching criteria +// includes the metadata associated with a proxy, workload instance +// info such as labels attached to the pod/VM, or any other info that +// the proxy provides to Istio during the initial handshake. If +// multiple conditions are specified, all conditions need to match in +// order for the workload instance to be selected. Currently, only +// label based selection mechanism is supported. +type WorkloadSelector struct { + // One or more labels that indicate a specific set of pods/VMs + // on which the configuration should be applied. The scope of + // label search is restricted to the configuration namespace in which the + // the resource is present. + Labels map[string]string `protobuf:"bytes,1,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *WorkloadSelector) Reset() { *m = WorkloadSelector{} } +func (m *WorkloadSelector) String() string { return proto.CompactTextString(m) } +func (*WorkloadSelector) ProtoMessage() {} +func (*WorkloadSelector) Descriptor() ([]byte, []int) { + return fileDescriptor_c5a818f331e9d367, []int{0} +} +func (m *WorkloadSelector) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_WorkloadSelector.Unmarshal(m, b) +} +func (m *WorkloadSelector) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_WorkloadSelector.Marshal(b, m, deterministic) +} +func (m *WorkloadSelector) XXX_Merge(src proto.Message) { + xxx_messageInfo_WorkloadSelector.Merge(m, src) +} +func (m *WorkloadSelector) XXX_Size() int { + return xxx_messageInfo_WorkloadSelector.Size(m) +} +func (m *WorkloadSelector) XXX_DiscardUnknown() { + xxx_messageInfo_WorkloadSelector.DiscardUnknown(m) +} + +var xxx_messageInfo_WorkloadSelector proto.InternalMessageInfo + +func (m *WorkloadSelector) GetLabels() map[string]string { + if m != nil { + return m.Labels + } + return nil +} + +type EnvoyPlugin struct { + WorkloadSelector *WorkloadSelector `protobuf:"bytes,9,opt,name=workload_selector,json=workloadSelector,proto3" json:"workload_selector,omitempty"` + // route level plugin + Route []string `protobuf:"bytes,1,rep,name=route,proto3" json:"route,omitempty"` + // host level plugin + Host []string `protobuf:"bytes,2,rep,name=host,proto3" json:"host,omitempty"` + // service level plugin + Service []string `protobuf:"bytes,3,rep,name=service,proto3" json:"service,omitempty"` + Plugins []*Plugin `protobuf:"bytes,4,rep,name=plugins,proto3" json:"plugins,omitempty"` + // which gateway should use this plugin setting + Gateway []string `protobuf:"bytes,5,rep,name=gateway,proto3" json:"gateway,omitempty"` + // which user should use this plugin setting + User []string `protobuf:"bytes,6,rep,name=user,proto3" json:"user,omitempty"` + // Deprecated + IsGroupSetting bool `protobuf:"varint,7,opt,name=isGroupSetting,proto3" json:"isGroupSetting,omitempty"` + // listener level + Listener []*EnvoyPlugin_Listener `protobuf:"bytes,8,rep,name=listener,proto3" json:"listener,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *EnvoyPlugin) Reset() { *m = EnvoyPlugin{} } +func (m *EnvoyPlugin) String() string { return proto.CompactTextString(m) } +func (*EnvoyPlugin) ProtoMessage() {} +func (*EnvoyPlugin) Descriptor() ([]byte, []int) { + return fileDescriptor_c5a818f331e9d367, []int{1} +} +func (m *EnvoyPlugin) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_EnvoyPlugin.Unmarshal(m, b) +} +func (m *EnvoyPlugin) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_EnvoyPlugin.Marshal(b, m, deterministic) +} +func (m *EnvoyPlugin) XXX_Merge(src proto.Message) { + xxx_messageInfo_EnvoyPlugin.Merge(m, src) +} +func (m *EnvoyPlugin) XXX_Size() int { + return xxx_messageInfo_EnvoyPlugin.Size(m) +} +func (m *EnvoyPlugin) XXX_DiscardUnknown() { + xxx_messageInfo_EnvoyPlugin.DiscardUnknown(m) +} + +var xxx_messageInfo_EnvoyPlugin proto.InternalMessageInfo + +func (m *EnvoyPlugin) GetWorkloadSelector() *WorkloadSelector { + if m != nil { + return m.WorkloadSelector + } + return nil +} + +func (m *EnvoyPlugin) GetRoute() []string { + if m != nil { + return m.Route + } + return nil +} + +func (m *EnvoyPlugin) GetHost() []string { + if m != nil { + return m.Host + } + return nil +} + +func (m *EnvoyPlugin) GetService() []string { + if m != nil { + return m.Service + } + return nil +} + +func (m *EnvoyPlugin) GetPlugins() []*Plugin { + if m != nil { + return m.Plugins + } + return nil +} + +func (m *EnvoyPlugin) GetGateway() []string { + if m != nil { + return m.Gateway + } + return nil +} + +func (m *EnvoyPlugin) GetUser() []string { + if m != nil { + return m.User + } + return nil +} + +func (m *EnvoyPlugin) GetIsGroupSetting() bool { + if m != nil { + return m.IsGroupSetting + } + return false +} + +func (m *EnvoyPlugin) GetListener() []*EnvoyPlugin_Listener { + if m != nil { + return m.Listener + } + return nil +} + +type EnvoyPlugin_Listener struct { + Port uint32 `protobuf:"varint,1,opt,name=port,proto3" json:"port,omitempty"` + Outbound bool `protobuf:"varint,2,opt,name=outbound,proto3" json:"outbound,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *EnvoyPlugin_Listener) Reset() { *m = EnvoyPlugin_Listener{} } +func (m *EnvoyPlugin_Listener) String() string { return proto.CompactTextString(m) } +func (*EnvoyPlugin_Listener) ProtoMessage() {} +func (*EnvoyPlugin_Listener) Descriptor() ([]byte, []int) { + return fileDescriptor_c5a818f331e9d367, []int{1, 0} +} +func (m *EnvoyPlugin_Listener) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_EnvoyPlugin_Listener.Unmarshal(m, b) +} +func (m *EnvoyPlugin_Listener) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_EnvoyPlugin_Listener.Marshal(b, m, deterministic) +} +func (m *EnvoyPlugin_Listener) XXX_Merge(src proto.Message) { + xxx_messageInfo_EnvoyPlugin_Listener.Merge(m, src) +} +func (m *EnvoyPlugin_Listener) XXX_Size() int { + return xxx_messageInfo_EnvoyPlugin_Listener.Size(m) +} +func (m *EnvoyPlugin_Listener) XXX_DiscardUnknown() { + xxx_messageInfo_EnvoyPlugin_Listener.DiscardUnknown(m) +} + +var xxx_messageInfo_EnvoyPlugin_Listener proto.InternalMessageInfo + +func (m *EnvoyPlugin_Listener) GetPort() uint32 { + if m != nil { + return m.Port + } + return 0 +} + +func (m *EnvoyPlugin_Listener) GetOutbound() bool { + if m != nil { + return m.Outbound + } + return false +} + +func init() { + proto.RegisterType((*WorkloadSelector)(nil), "slime.microservice.plugin.v1alpha1.WorkloadSelector") + proto.RegisterMapType((map[string]string)(nil), "slime.microservice.plugin.v1alpha1.WorkloadSelector.LabelsEntry") + proto.RegisterType((*EnvoyPlugin)(nil), "slime.microservice.plugin.v1alpha1.EnvoyPlugin") + proto.RegisterType((*EnvoyPlugin_Listener)(nil), "slime.microservice.plugin.v1alpha1.EnvoyPlugin.Listener") +} + +func init() { proto.RegisterFile("envoy_plugin.proto", fileDescriptor_c5a818f331e9d367) } + +var fileDescriptor_c5a818f331e9d367 = []byte{ + // 410 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x92, 0xcf, 0x6a, 0xdb, 0x40, + 0x10, 0xc6, 0x91, 0x1d, 0xdb, 0xf2, 0x98, 0x16, 0x77, 0xf1, 0x61, 0xf1, 0x49, 0xf8, 0x50, 0x44, + 0x28, 0x12, 0x49, 0x7b, 0x48, 0x73, 0x2a, 0xa5, 0xa1, 0x97, 0x1c, 0xca, 0xa6, 0xd0, 0xd2, 0x8b, + 0x59, 0xdb, 0x83, 0xb2, 0x64, 0xa5, 0x15, 0xfb, 0x47, 0x46, 0x4f, 0xd4, 0xf7, 0xea, 0x93, 0x14, + 0xed, 0x4a, 0x21, 0xf8, 0x52, 0xd3, 0xdb, 0xcc, 0x87, 0xe6, 0xfb, 0x7e, 0x3b, 0x23, 0x20, 0x58, + 0x35, 0xaa, 0xdd, 0xd6, 0xd2, 0x15, 0xa2, 0xca, 0x6a, 0xad, 0xac, 0x22, 0x1b, 0x23, 0x45, 0x89, + 0x59, 0x29, 0xf6, 0x5a, 0x19, 0xd4, 0x8d, 0xd8, 0x63, 0xd6, 0x7f, 0xd0, 0x5c, 0x71, 0x59, 0x3f, + 0xf2, 0xab, 0xf5, 0x2a, 0x08, 0xdb, 0x92, 0x57, 0xbc, 0x40, 0x1d, 0x26, 0x37, 0xbf, 0x23, 0x58, + 0xfe, 0x50, 0xfa, 0x49, 0x2a, 0x7e, 0x78, 0x40, 0x89, 0x7b, 0xab, 0x34, 0xf9, 0x09, 0x53, 0xc9, + 0x77, 0x28, 0x0d, 0x8d, 0x92, 0x71, 0xba, 0xb8, 0xfe, 0x94, 0xfd, 0xdb, 0x3f, 0x3b, 0x75, 0xc9, + 0xee, 0xbd, 0xc5, 0x5d, 0x65, 0x75, 0xcb, 0x7a, 0xbf, 0xf5, 0x47, 0x58, 0xbc, 0x90, 0xc9, 0x12, + 0xc6, 0x4f, 0xd8, 0xd2, 0x28, 0x89, 0xd2, 0x39, 0xeb, 0x4a, 0xb2, 0x82, 0x49, 0xc3, 0xa5, 0x43, + 0x3a, 0xf2, 0x5a, 0x68, 0x6e, 0x47, 0x37, 0xd1, 0xe6, 0xcf, 0x18, 0x16, 0x77, 0xdd, 0xd3, 0xbf, + 0xf9, 0x60, 0xc2, 0xe1, 0xcd, 0xb1, 0x8f, 0xdc, 0x9a, 0x3e, 0x93, 0xce, 0x93, 0x28, 0x5d, 0x5c, + 0x7f, 0xf8, 0x1f, 0x5e, 0xb6, 0x3c, 0x9e, 0xee, 0x61, 0x05, 0x13, 0xad, 0x9c, 0x45, 0xbf, 0x86, + 0x39, 0x0b, 0x0d, 0x21, 0x70, 0xf1, 0xa8, 0x8c, 0xa5, 0x23, 0x2f, 0xfa, 0x9a, 0x50, 0x98, 0xf5, + 0x39, 0x74, 0xec, 0xe5, 0xa1, 0x25, 0x5f, 0x60, 0x16, 0x92, 0x0d, 0xbd, 0xf0, 0xcb, 0xbc, 0x3c, + 0x07, 0x2e, 0xbc, 0x91, 0x0d, 0xa3, 0x9d, 0x7f, 0xc1, 0x2d, 0x1e, 0x79, 0x4b, 0x27, 0xc1, 0xbf, + 0x6f, 0x3b, 0x1a, 0x67, 0x50, 0xd3, 0x69, 0xa0, 0xe9, 0x6a, 0xf2, 0x16, 0x5e, 0x0b, 0xf3, 0x55, + 0x2b, 0x57, 0x3f, 0xa0, 0xb5, 0xa2, 0x2a, 0xe8, 0x2c, 0x89, 0xd2, 0x98, 0x9d, 0xa8, 0xe4, 0x3b, + 0xc4, 0x52, 0x18, 0x8b, 0x15, 0x6a, 0x1a, 0x7b, 0xb8, 0x9b, 0x73, 0xe0, 0x5e, 0x5c, 0x21, 0xbb, + 0xef, 0xe7, 0xd9, 0xb3, 0xd3, 0xfa, 0x16, 0xe2, 0x41, 0xed, 0xe8, 0x6a, 0xa5, 0xad, 0xbf, 0xf0, + 0x2b, 0xe6, 0x6b, 0xb2, 0x86, 0x58, 0x39, 0xbb, 0x53, 0xae, 0x3a, 0xf8, 0x2b, 0xc7, 0xec, 0xb9, + 0xff, 0xfc, 0xee, 0xd7, 0x65, 0x00, 0x10, 0x2a, 0xf7, 0x45, 0x5e, 0xaa, 0x83, 0x93, 0x68, 0xf2, + 0x00, 0x91, 0xf3, 0x5a, 0xe4, 0x03, 0xc8, 0x6e, 0xea, 0xff, 0xe1, 0xf7, 0x7f, 0x03, 0x00, 0x00, + 0xff, 0xff, 0xdf, 0x95, 0x9b, 0xb4, 0x13, 0x03, 0x00, 0x00, +} diff --git a/staging/src/slime.io/slime/modules/plugin/api/v1alpha1/envoy_plugin.proto b/staging/src/slime.io/slime/modules/plugin/api/v1alpha1/envoy_plugin.proto new file mode 100644 index 00000000..923100bf --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/api/v1alpha1/envoy_plugin.proto @@ -0,0 +1,99 @@ +/* +* @Author: yangdihang +* @Date: 2020/5/21 +*/ + +syntax = "proto3"; + +// EnvoyPlugin 用于设置网关扩展插件配置,配置生效范围有3个级别 +// route级别,对应同一route name下的路由规则 +// host级别,对应同一host下的路由规则 + +// 示例如下: +// apiVersion: microservice.slime.io/v1alpha1 +// kind: EnvoyPlugin +// metadata: +// name: project1-3-458-rewrite +// spec: +// gateway: +// - gateway-proxy-1 +// host: +// - 103.196.65.178 +// plugins: +// - name: com.netease.rewrite +// settings: +// request_transformations: +// - conditions: +// - headers: +// - name: :path +// regex_match: /aaaaaaa/(.*) +// transformation_template: +// extractors: +// $1: +// header: :path +// regex: /aaaaaaa/(.*) +// subgroup: 1 +// headers: +// :path: +// text: /{{$1}} +// parse_body_behavior: DontParse +import "plugin_manager.proto"; + +package slime.microservice.plugin.v1alpha1; + +option go_package = "slime.io/slime/modules/plugin/api/v1alpha1"; + + +// `WorkloadSelector` specifies the criteria used to determine if the +// `Gateway`, `Sidecar`, or `EnvoyFilter` or `ServiceEntry` +// configuration can be applied to a proxy. The matching criteria +// includes the metadata associated with a proxy, workload instance +// info such as labels attached to the pod/VM, or any other info that +// the proxy provides to Istio during the initial handshake. If +// multiple conditions are specified, all conditions need to match in +// order for the workload instance to be selected. Currently, only +// label based selection mechanism is supported. +message WorkloadSelector { + // One or more labels that indicate a specific set of pods/VMs + // on which the configuration should be applied. The scope of + // label search is restricted to the configuration namespace in which the + // the resource is present. + map labels = 1; + + // $hide_from_docs + // other forms of identification supplied by the proxy + // when connecting to Pilot, such as X509 fields, tenant IDs, JWT, + // etc. This has nothing to do with the request level authN etc. +} + +message EnvoyPlugin { + WorkloadSelector workload_selector = 9; + + // route level plugin + repeated string route = 1; + + // host level plugin + repeated string host = 2; + + // service level plugin + repeated string service = 3; + + repeated Plugin plugins = 4; + + // which gateway should use this plugin setting + repeated string gateway = 5; + + // which user should use this plugin setting + repeated string user = 6; + + // Deprecated + bool isGroupSetting = 7; + + message Listener { + uint32 port = 1; + bool outbound = 2; + } + + // listener level + repeated Listener listener = 8; +} diff --git a/staging/src/slime.io/slime/modules/plugin/api/v1alpha1/groupversion_info.go b/staging/src/slime.io/slime/modules/plugin/api/v1alpha1/groupversion_info.go new file mode 100644 index 00000000..1309f2c9 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/api/v1alpha1/groupversion_info.go @@ -0,0 +1,36 @@ +/* + + +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 v1alpha1 contains API Schema definitions for the microservice.slime.io v1alpha1 API group +// +kubebuilder:object:generate=true +// +groupName=microservice.slime.io.my.domain +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "microservice.slime.io", Version: "v1alpha1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/staging/src/slime.io/slime/modules/plugin/api/v1alpha1/plugin_manager.pb.go b/staging/src/slime.io/slime/modules/plugin/api/v1alpha1/plugin_manager.pb.go new file mode 100644 index 00000000..47391dac --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/api/v1alpha1/plugin_manager.pb.go @@ -0,0 +1,396 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: plugin_manager.proto + +package v1alpha1 + +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + types "github.com/gogo/protobuf/types" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type Plugin_ListenerType int32 + +const ( + Plugin_Outbound Plugin_ListenerType = 0 + Plugin_Inbound Plugin_ListenerType = 1 + Plugin_Gateway Plugin_ListenerType = 2 +) + +var Plugin_ListenerType_name = map[int32]string{ + 0: "Outbound", + 1: "Inbound", + 2: "Gateway", +} + +var Plugin_ListenerType_value = map[string]int32{ + "Outbound": 0, + "Inbound": 1, + "Gateway": 2, +} + +func (x Plugin_ListenerType) String() string { + return proto.EnumName(Plugin_ListenerType_name, int32(x)) +} + +func (Plugin_ListenerType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_58d89e785e3ecf0c, []int{1, 0} +} + +type PluginManager struct { + // Zero or more labels that indicate a specific set of pods/VMs whose + // proxies should be configured to use these additional filters. The + // scope of label search is platform dependent. On Kubernetes, for + // example, the scope includes pods running in all reachable + // namespaces. Omitting the selector applies the filter to all proxies in + // the mesh. + WorkloadLabels map[string]string `protobuf:"bytes,1,rep,name=workload_labels,json=workloadLabels,proto3" json:"workload_labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Plugin []*Plugin `protobuf:"bytes,2,rep,name=plugin,proto3" json:"plugin,omitempty"` + // Names of gateways where the rule should be applied to. Gateway names + // at the top of the VirtualService (if any) are overridden. The gateway + // match is independent of sourceLabels. + Gateways []string `protobuf:"bytes,3,rep,name=gateways,proto3" json:"gateways,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PluginManager) Reset() { *m = PluginManager{} } +func (m *PluginManager) String() string { return proto.CompactTextString(m) } +func (*PluginManager) ProtoMessage() {} +func (*PluginManager) Descriptor() ([]byte, []int) { + return fileDescriptor_58d89e785e3ecf0c, []int{0} +} +func (m *PluginManager) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PluginManager.Unmarshal(m, b) +} +func (m *PluginManager) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PluginManager.Marshal(b, m, deterministic) +} +func (m *PluginManager) XXX_Merge(src proto.Message) { + xxx_messageInfo_PluginManager.Merge(m, src) +} +func (m *PluginManager) XXX_Size() int { + return xxx_messageInfo_PluginManager.Size(m) +} +func (m *PluginManager) XXX_DiscardUnknown() { + xxx_messageInfo_PluginManager.DiscardUnknown(m) +} + +var xxx_messageInfo_PluginManager proto.InternalMessageInfo + +func (m *PluginManager) GetWorkloadLabels() map[string]string { + if m != nil { + return m.WorkloadLabels + } + return nil +} + +func (m *PluginManager) GetPlugin() []*Plugin { + if m != nil { + return m.Plugin + } + return nil +} + +func (m *PluginManager) GetGateways() []string { + if m != nil { + return m.Gateways + } + return nil +} + +type Plugin struct { + Enable bool `protobuf:"varint,1,opt,name=enable,proto3" json:"enable,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + // Deprecated + Settings *types.Struct `protobuf:"bytes,3,opt,name=settings,proto3" json:"settings,omitempty"` + ListenerType Plugin_ListenerType `protobuf:"varint,4,opt,name=listenerType,proto3,enum=slime.microservice.plugin.v1alpha1.Plugin_ListenerType" json:"listenerType,omitempty"` + TypeUrl string `protobuf:"bytes,5,opt,name=type_url,json=typeUrl,proto3" json:"type_url,omitempty"` + // Types that are valid to be assigned to PluginSettings: + // *Plugin_Wasm + // *Plugin_Inline + PluginSettings isPlugin_PluginSettings `protobuf_oneof:"plugin_settings"` + Port uint32 `protobuf:"varint,8,opt,name=port,proto3" json:"port,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Plugin) Reset() { *m = Plugin{} } +func (m *Plugin) String() string { return proto.CompactTextString(m) } +func (*Plugin) ProtoMessage() {} +func (*Plugin) Descriptor() ([]byte, []int) { + return fileDescriptor_58d89e785e3ecf0c, []int{1} +} +func (m *Plugin) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Plugin.Unmarshal(m, b) +} +func (m *Plugin) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Plugin.Marshal(b, m, deterministic) +} +func (m *Plugin) XXX_Merge(src proto.Message) { + xxx_messageInfo_Plugin.Merge(m, src) +} +func (m *Plugin) XXX_Size() int { + return xxx_messageInfo_Plugin.Size(m) +} +func (m *Plugin) XXX_DiscardUnknown() { + xxx_messageInfo_Plugin.DiscardUnknown(m) +} + +var xxx_messageInfo_Plugin proto.InternalMessageInfo + +type isPlugin_PluginSettings interface { + isPlugin_PluginSettings() +} + +type Plugin_Wasm struct { + Wasm *Wasm `protobuf:"bytes,6,opt,name=wasm,proto3,oneof" json:"wasm,omitempty"` +} +type Plugin_Inline struct { + Inline *Inline `protobuf:"bytes,7,opt,name=inline,proto3,oneof" json:"inline,omitempty"` +} + +func (*Plugin_Wasm) isPlugin_PluginSettings() {} +func (*Plugin_Inline) isPlugin_PluginSettings() {} + +func (m *Plugin) GetPluginSettings() isPlugin_PluginSettings { + if m != nil { + return m.PluginSettings + } + return nil +} + +func (m *Plugin) GetEnable() bool { + if m != nil { + return m.Enable + } + return false +} + +func (m *Plugin) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Plugin) GetSettings() *types.Struct { + if m != nil { + return m.Settings + } + return nil +} + +func (m *Plugin) GetListenerType() Plugin_ListenerType { + if m != nil { + return m.ListenerType + } + return Plugin_Outbound +} + +func (m *Plugin) GetTypeUrl() string { + if m != nil { + return m.TypeUrl + } + return "" +} + +func (m *Plugin) GetWasm() *Wasm { + if x, ok := m.GetPluginSettings().(*Plugin_Wasm); ok { + return x.Wasm + } + return nil +} + +func (m *Plugin) GetInline() *Inline { + if x, ok := m.GetPluginSettings().(*Plugin_Inline); ok { + return x.Inline + } + return nil +} + +func (m *Plugin) GetPort() uint32 { + if m != nil { + return m.Port + } + return 0 +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*Plugin) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*Plugin_Wasm)(nil), + (*Plugin_Inline)(nil), + } +} + +type Wasm struct { + RootID string `protobuf:"bytes,1,opt,name=rootID,proto3" json:"rootID,omitempty"` + FileName string `protobuf:"bytes,2,opt,name=fileName,proto3" json:"fileName,omitempty"` + Settings *types.Struct `protobuf:"bytes,3,opt,name=settings,proto3" json:"settings,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Wasm) Reset() { *m = Wasm{} } +func (m *Wasm) String() string { return proto.CompactTextString(m) } +func (*Wasm) ProtoMessage() {} +func (*Wasm) Descriptor() ([]byte, []int) { + return fileDescriptor_58d89e785e3ecf0c, []int{2} +} +func (m *Wasm) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Wasm.Unmarshal(m, b) +} +func (m *Wasm) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Wasm.Marshal(b, m, deterministic) +} +func (m *Wasm) XXX_Merge(src proto.Message) { + xxx_messageInfo_Wasm.Merge(m, src) +} +func (m *Wasm) XXX_Size() int { + return xxx_messageInfo_Wasm.Size(m) +} +func (m *Wasm) XXX_DiscardUnknown() { + xxx_messageInfo_Wasm.DiscardUnknown(m) +} + +var xxx_messageInfo_Wasm proto.InternalMessageInfo + +func (m *Wasm) GetRootID() string { + if m != nil { + return m.RootID + } + return "" +} + +func (m *Wasm) GetFileName() string { + if m != nil { + return m.FileName + } + return "" +} + +func (m *Wasm) GetSettings() *types.Struct { + if m != nil { + return m.Settings + } + return nil +} + +type Inline struct { + Settings *types.Struct `protobuf:"bytes,1,opt,name=settings,proto3" json:"settings,omitempty"` + DirectPatch bool `protobuf:"varint,2,opt,name=directPatch,proto3" json:"directPatch,omitempty"` + FieldPatchTo string `protobuf:"bytes,3,opt,name=fieldPatchTo,proto3" json:"fieldPatchTo,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Inline) Reset() { *m = Inline{} } +func (m *Inline) String() string { return proto.CompactTextString(m) } +func (*Inline) ProtoMessage() {} +func (*Inline) Descriptor() ([]byte, []int) { + return fileDescriptor_58d89e785e3ecf0c, []int{3} +} +func (m *Inline) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Inline.Unmarshal(m, b) +} +func (m *Inline) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Inline.Marshal(b, m, deterministic) +} +func (m *Inline) XXX_Merge(src proto.Message) { + xxx_messageInfo_Inline.Merge(m, src) +} +func (m *Inline) XXX_Size() int { + return xxx_messageInfo_Inline.Size(m) +} +func (m *Inline) XXX_DiscardUnknown() { + xxx_messageInfo_Inline.DiscardUnknown(m) +} + +var xxx_messageInfo_Inline proto.InternalMessageInfo + +func (m *Inline) GetSettings() *types.Struct { + if m != nil { + return m.Settings + } + return nil +} + +func (m *Inline) GetDirectPatch() bool { + if m != nil { + return m.DirectPatch + } + return false +} + +func (m *Inline) GetFieldPatchTo() string { + if m != nil { + return m.FieldPatchTo + } + return "" +} + +func init() { + proto.RegisterEnum("slime.microservice.plugin.v1alpha1.Plugin_ListenerType", Plugin_ListenerType_name, Plugin_ListenerType_value) + proto.RegisterType((*PluginManager)(nil), "slime.microservice.plugin.v1alpha1.PluginManager") + proto.RegisterMapType((map[string]string)(nil), "slime.microservice.plugin.v1alpha1.PluginManager.WorkloadLabelsEntry") + proto.RegisterType((*Plugin)(nil), "slime.microservice.plugin.v1alpha1.Plugin") + proto.RegisterType((*Wasm)(nil), "slime.microservice.plugin.v1alpha1.Wasm") + proto.RegisterType((*Inline)(nil), "slime.microservice.plugin.v1alpha1.Inline") +} + +func init() { proto.RegisterFile("plugin_manager.proto", fileDescriptor_58d89e785e3ecf0c) } + +var fileDescriptor_58d89e785e3ecf0c = []byte{ + // 543 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x53, 0x51, 0x6b, 0xdb, 0x3c, + 0x14, 0xad, 0x93, 0xd4, 0x75, 0x6f, 0xd2, 0x36, 0x9f, 0xbe, 0xb2, 0x79, 0x61, 0x0f, 0xc6, 0x4f, + 0xa6, 0x0c, 0x9b, 0xa6, 0xb0, 0x8d, 0x3d, 0x0c, 0x16, 0x5a, 0xd6, 0x40, 0xb7, 0x15, 0xad, 0xa3, + 0xb0, 0x3d, 0x04, 0x25, 0x51, 0x5c, 0x51, 0x59, 0x32, 0xb2, 0x9c, 0x90, 0xe7, 0xfd, 0x93, 0xfd, + 0x98, 0xfd, 0xae, 0x61, 0xc9, 0xc9, 0x12, 0x18, 0x2c, 0xec, 0xed, 0x9e, 0x8b, 0xce, 0xb9, 0x47, + 0xf7, 0x48, 0x70, 0x9a, 0xf3, 0x32, 0x65, 0x62, 0x94, 0x11, 0x41, 0x52, 0xaa, 0xe2, 0x5c, 0x49, + 0x2d, 0x51, 0x58, 0x70, 0x96, 0xd1, 0x38, 0x63, 0x13, 0x25, 0x0b, 0xaa, 0xe6, 0x6c, 0x42, 0x63, + 0x7b, 0x30, 0x9e, 0x9f, 0x13, 0x9e, 0x3f, 0x90, 0xf3, 0xde, 0xf3, 0x54, 0xca, 0x94, 0xd3, 0xc4, + 0x30, 0xc6, 0xe5, 0x2c, 0x29, 0xb4, 0x2a, 0x27, 0xda, 0x2a, 0x84, 0x3f, 0x1a, 0x70, 0x74, 0x6b, + 0x18, 0x1f, 0xac, 0x32, 0x12, 0x70, 0xb2, 0x90, 0xea, 0x91, 0x4b, 0x32, 0x1d, 0x71, 0x32, 0xa6, + 0xbc, 0xf0, 0x9d, 0xa0, 0x19, 0xb5, 0xfb, 0x57, 0xf1, 0xdf, 0xa7, 0xc5, 0x5b, 0x5a, 0xf1, 0x7d, + 0x2d, 0x74, 0x63, 0x74, 0xae, 0x84, 0x56, 0x4b, 0x7c, 0xbc, 0xd8, 0x6a, 0xa2, 0x01, 0xb8, 0x56, + 0xc4, 0x6f, 0x98, 0x31, 0x67, 0xbb, 0x8f, 0xc1, 0x35, 0x13, 0xf5, 0xc0, 0x4b, 0x89, 0xa6, 0x0b, + 0xb2, 0x2c, 0xfc, 0x66, 0xd0, 0x8c, 0x0e, 0xf1, 0x1a, 0xf7, 0xde, 0xc1, 0xff, 0x7f, 0xb0, 0x81, + 0xba, 0xd0, 0x7c, 0xa4, 0x4b, 0xdf, 0x09, 0x9c, 0xe8, 0x10, 0x57, 0x25, 0x3a, 0x85, 0xfd, 0x39, + 0xe1, 0x25, 0xf5, 0x1b, 0xa6, 0x67, 0xc1, 0x9b, 0xc6, 0x6b, 0x27, 0xfc, 0xd9, 0x04, 0xd7, 0x4e, + 0x44, 0x4f, 0xc0, 0xa5, 0x82, 0x8c, 0x39, 0x35, 0x4c, 0x0f, 0xd7, 0x08, 0x21, 0x68, 0x09, 0x92, + 0xad, 0xb8, 0xa6, 0x46, 0x17, 0xe0, 0x15, 0x54, 0x6b, 0x26, 0xd2, 0xca, 0x95, 0x13, 0xb5, 0xfb, + 0x4f, 0x63, 0x1b, 0x46, 0xbc, 0x0a, 0x23, 0xfe, 0x6c, 0xc2, 0xc0, 0xeb, 0x83, 0xe8, 0x1b, 0x74, + 0x38, 0x2b, 0x34, 0x15, 0x54, 0xdd, 0x2d, 0x73, 0xea, 0xb7, 0x02, 0x27, 0x3a, 0xee, 0xbf, 0xda, + 0x7d, 0x29, 0xf1, 0xcd, 0x06, 0x1d, 0x6f, 0x89, 0xa1, 0x67, 0xe0, 0xe9, 0x65, 0x4e, 0x47, 0xa5, + 0xe2, 0xfe, 0xbe, 0x71, 0x7a, 0x50, 0xe1, 0x2f, 0x8a, 0xa3, 0xb7, 0xd0, 0x5a, 0x90, 0x22, 0xf3, + 0x5d, 0x63, 0x34, 0xda, 0x65, 0xde, 0x3d, 0x29, 0xb2, 0xeb, 0x3d, 0x6c, 0x78, 0xe8, 0x12, 0x5c, + 0x26, 0x38, 0x13, 0xd4, 0x3f, 0x30, 0x0a, 0x3b, 0xc5, 0x38, 0x34, 0x8c, 0xeb, 0x3d, 0x5c, 0x73, + 0xab, 0x35, 0xe6, 0x52, 0x69, 0xdf, 0x0b, 0x9c, 0xe8, 0x08, 0x9b, 0x3a, 0x7c, 0x09, 0x9d, 0xcd, + 0x2b, 0xa1, 0x0e, 0x78, 0x9f, 0x4a, 0x3d, 0x96, 0xa5, 0x98, 0x76, 0xf7, 0x50, 0x1b, 0x0e, 0x86, + 0xc2, 0x02, 0xa7, 0x02, 0xef, 0x6d, 0xee, 0xdd, 0xc6, 0xe0, 0x3f, 0x38, 0xa9, 0x3f, 0xcd, 0x6a, + 0xb9, 0xa1, 0x84, 0x56, 0x65, 0xba, 0x4a, 0x51, 0x49, 0xa9, 0x87, 0x97, 0x75, 0xfe, 0x35, 0xaa, + 0xde, 0xd1, 0x8c, 0x71, 0xfa, 0xf1, 0x77, 0x92, 0x6b, 0xfc, 0x4f, 0x69, 0x86, 0xdf, 0x1d, 0x70, + 0xed, 0x25, 0xb7, 0xf8, 0xce, 0xae, 0xaf, 0x21, 0x80, 0xf6, 0x94, 0x29, 0x3a, 0xd1, 0xb7, 0x44, + 0x4f, 0x1e, 0x8c, 0x27, 0x0f, 0x6f, 0xb6, 0x50, 0x08, 0x9d, 0x19, 0xa3, 0x7c, 0x6a, 0xd0, 0x9d, + 0x34, 0xd6, 0x0e, 0xf1, 0x56, 0x6f, 0xf0, 0xe2, 0xeb, 0x99, 0x0d, 0x83, 0xc9, 0xc4, 0x14, 0x49, + 0x26, 0xa7, 0x25, 0xa7, 0x45, 0x62, 0x17, 0x94, 0x90, 0x9c, 0x25, 0xab, 0x50, 0xc6, 0xae, 0xb1, + 0x73, 0xf1, 0x2b, 0x00, 0x00, 0xff, 0xff, 0x11, 0xf1, 0x5d, 0x8e, 0x73, 0x04, 0x00, 0x00, +} diff --git a/staging/src/slime.io/slime/modules/plugin/api/v1alpha1/plugin_manager.proto b/staging/src/slime.io/slime/modules/plugin/api/v1alpha1/plugin_manager.proto new file mode 100644 index 00000000..c5facbe5 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/api/v1alpha1/plugin_manager.proto @@ -0,0 +1,98 @@ +/* +* @Author: yangdihang +* @Date: 2020/5/21 +*/ + +syntax = "proto3"; + +// PluginManager 用于管理插件,以及插件在全局维度上的配置,enable选项可以启用或禁用某插件 +// 示例如下: +// apiVersion: microservice.slime.io/v1alpha1 +// kind: PluginManager +// metadata: +// name: gw-cluster-gateway-proxy +// namespace: gateway-system +// spec: +// plugin: +// - enable: true +// name: envoy.cors +// - enable: true +// name: envoy.fault +// - enable: true +// name: com.netease.iprestriction +// - enable: true +// name: com.netease.transformation +// - enable: false +// name: com.netease.resty +// - enable: true +// name: com.netease.metadatahub +// - enable: true +// name: com.netease.supercache +// inline: +// settings: +// apis_prefix: testremove +// used_caches: +// local: {} +// redis: +// general: +// host: 10.107.16.138 +// port: 6379 +// timeout: 100 +// workloadLabels: +// gw_cluster: gateway-proxy + +import "google/protobuf/struct.proto"; + +package slime.microservice.plugin.v1alpha1; + +option go_package = "slime.io/slime/modules/plugin/api/v1alpha1"; + +message PluginManager { + // Zero or more labels that indicate a specific set of pods/VMs whose + // proxies should be configured to use these additional filters. The + // scope of label search is platform dependent. On Kubernetes, for + // example, the scope includes pods running in all reachable + // namespaces. Omitting the selector applies the filter to all proxies in + // the mesh. + map workload_labels = 1; + + repeated Plugin plugin = 2; + + // Names of gateways where the rule should be applied to. Gateway names + // at the top of the VirtualService (if any) are overridden. The gateway + // match is independent of sourceLabels. + repeated string gateways = 3; +} + +message Plugin { + bool enable = 1; + string name = 2; + // Deprecated + google.protobuf.Struct settings = 3; + enum ListenerType { + Outbound = 0; + Inbound = 1; + Gateway = 2; + } + ListenerType listenerType = 4; + string type_url = 5; + oneof plugin_settings { + Wasm wasm = 6; + // plugin compiled inside envoy + Inline inline = 7; + } + uint32 port = 8; +} + +message Wasm { + string rootID = 1; + string fileName = 2; + google.protobuf.Struct settings = 3; +} + +message Inline { + google.protobuf.Struct settings = 1; + bool directPatch = 2; + string fieldPatchTo = 3; +} + diff --git a/staging/src/slime.io/slime/modules/plugin/api/v1alpha1/plugin_module.pb.go b/staging/src/slime.io/slime/modules/plugin/api/v1alpha1/plugin_module.pb.go new file mode 100644 index 00000000..a7305533 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/api/v1alpha1/plugin_module.pb.go @@ -0,0 +1,201 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: plugin_module.proto + +package v1alpha1 + +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type LocalSource struct { + Mount string `protobuf:"bytes,1,opt,name=mount,proto3" json:"mount,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *LocalSource) Reset() { *m = LocalSource{} } +func (m *LocalSource) String() string { return proto.CompactTextString(m) } +func (*LocalSource) ProtoMessage() {} +func (*LocalSource) Descriptor() ([]byte, []int) { + return fileDescriptor_c98ebf9b1715be96, []int{0} +} +func (m *LocalSource) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_LocalSource.Unmarshal(m, b) +} +func (m *LocalSource) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_LocalSource.Marshal(b, m, deterministic) +} +func (m *LocalSource) XXX_Merge(src proto.Message) { + xxx_messageInfo_LocalSource.Merge(m, src) +} +func (m *LocalSource) XXX_Size() int { + return xxx_messageInfo_LocalSource.Size(m) +} +func (m *LocalSource) XXX_DiscardUnknown() { + xxx_messageInfo_LocalSource.DiscardUnknown(m) +} + +var xxx_messageInfo_LocalSource proto.InternalMessageInfo + +func (m *LocalSource) GetMount() string { + if m != nil { + return m.Mount + } + return "" +} + +type RemoteSource struct { + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RemoteSource) Reset() { *m = RemoteSource{} } +func (m *RemoteSource) String() string { return proto.CompactTextString(m) } +func (*RemoteSource) ProtoMessage() {} +func (*RemoteSource) Descriptor() ([]byte, []int) { + return fileDescriptor_c98ebf9b1715be96, []int{1} +} +func (m *RemoteSource) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_RemoteSource.Unmarshal(m, b) +} +func (m *RemoteSource) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_RemoteSource.Marshal(b, m, deterministic) +} +func (m *RemoteSource) XXX_Merge(src proto.Message) { + xxx_messageInfo_RemoteSource.Merge(m, src) +} +func (m *RemoteSource) XXX_Size() int { + return xxx_messageInfo_RemoteSource.Size(m) +} +func (m *RemoteSource) XXX_DiscardUnknown() { + xxx_messageInfo_RemoteSource.DiscardUnknown(m) +} + +var xxx_messageInfo_RemoteSource proto.InternalMessageInfo + +func (m *RemoteSource) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +type PluginModule struct { + // Types that are valid to be assigned to WasmSource: + // *PluginModule_Local + // *PluginModule_Remote + WasmSource isPluginModule_WasmSource `protobuf_oneof:"wasm_source"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PluginModule) Reset() { *m = PluginModule{} } +func (m *PluginModule) String() string { return proto.CompactTextString(m) } +func (*PluginModule) ProtoMessage() {} +func (*PluginModule) Descriptor() ([]byte, []int) { + return fileDescriptor_c98ebf9b1715be96, []int{2} +} +func (m *PluginModule) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PluginModule.Unmarshal(m, b) +} +func (m *PluginModule) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PluginModule.Marshal(b, m, deterministic) +} +func (m *PluginModule) XXX_Merge(src proto.Message) { + xxx_messageInfo_PluginModule.Merge(m, src) +} +func (m *PluginModule) XXX_Size() int { + return xxx_messageInfo_PluginModule.Size(m) +} +func (m *PluginModule) XXX_DiscardUnknown() { + xxx_messageInfo_PluginModule.DiscardUnknown(m) +} + +var xxx_messageInfo_PluginModule proto.InternalMessageInfo + +type isPluginModule_WasmSource interface { + isPluginModule_WasmSource() +} + +type PluginModule_Local struct { + Local *LocalSource `protobuf:"bytes,2,opt,name=local,proto3,oneof" json:"local,omitempty"` +} +type PluginModule_Remote struct { + Remote *RemoteSource `protobuf:"bytes,3,opt,name=remote,proto3,oneof" json:"remote,omitempty"` +} + +func (*PluginModule_Local) isPluginModule_WasmSource() {} +func (*PluginModule_Remote) isPluginModule_WasmSource() {} + +func (m *PluginModule) GetWasmSource() isPluginModule_WasmSource { + if m != nil { + return m.WasmSource + } + return nil +} + +func (m *PluginModule) GetLocal() *LocalSource { + if x, ok := m.GetWasmSource().(*PluginModule_Local); ok { + return x.Local + } + return nil +} + +func (m *PluginModule) GetRemote() *RemoteSource { + if x, ok := m.GetWasmSource().(*PluginModule_Remote); ok { + return x.Remote + } + return nil +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*PluginModule) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*PluginModule_Local)(nil), + (*PluginModule_Remote)(nil), + } +} + +func init() { + proto.RegisterType((*LocalSource)(nil), "slime.microservice.plugin.v1alpha1.LocalSource") + proto.RegisterType((*RemoteSource)(nil), "slime.microservice.plugin.v1alpha1.RemoteSource") + proto.RegisterType((*PluginModule)(nil), "slime.microservice.plugin.v1alpha1.PluginModule") +} + +func init() { proto.RegisterFile("plugin_module.proto", fileDescriptor_c98ebf9b1715be96) } + +var fileDescriptor_c98ebf9b1715be96 = []byte{ + // 236 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x90, 0xc1, 0x4a, 0x03, 0x31, + 0x14, 0x45, 0x3b, 0x4a, 0x2b, 0xbe, 0xa9, 0x9b, 0xd1, 0xc5, 0x2c, 0x4b, 0xdc, 0x0c, 0x22, 0x89, + 0xd5, 0x3f, 0xe8, 0xc6, 0x22, 0x0a, 0x12, 0x77, 0x6e, 0x4a, 0xcc, 0x3c, 0x34, 0x90, 0xf4, 0x85, + 0x64, 0x52, 0xff, 0xcb, 0x2f, 0x14, 0x93, 0x29, 0x74, 0x67, 0x77, 0xb9, 0xe4, 0xe6, 0xe4, 0x70, + 0xe1, 0xd2, 0xdb, 0xf4, 0x69, 0xb6, 0x1b, 0x47, 0x7d, 0xb2, 0xc8, 0x7d, 0xa0, 0x81, 0x1a, 0x16, + 0xad, 0x71, 0xc8, 0x9d, 0xd1, 0x81, 0x22, 0x86, 0x9d, 0xd1, 0xc8, 0x4b, 0x8f, 0xef, 0x96, 0xca, + 0xfa, 0x2f, 0xb5, 0x64, 0xd7, 0x50, 0x3f, 0x93, 0x56, 0xf6, 0x8d, 0x52, 0xd0, 0xd8, 0x5c, 0xc1, + 0xd4, 0x51, 0xda, 0x0e, 0x6d, 0xb5, 0xa8, 0xba, 0x73, 0x59, 0x02, 0xeb, 0x60, 0x2e, 0xd1, 0xd1, + 0x80, 0x63, 0xab, 0x85, 0x33, 0xd5, 0xf7, 0x01, 0x63, 0x1c, 0x7b, 0xfb, 0xc8, 0x7e, 0x2a, 0x98, + 0xbf, 0xe6, 0x2f, 0x5e, 0xb2, 0x49, 0xf3, 0x08, 0x53, 0xfb, 0xc7, 0x6f, 0x4f, 0x16, 0x55, 0x57, + 0xdf, 0x0b, 0xfe, 0xbf, 0x13, 0x3f, 0x10, 0x5a, 0x4f, 0x64, 0x79, 0xdf, 0x3c, 0xc1, 0x2c, 0x64, + 0x87, 0xf6, 0x34, 0x93, 0xee, 0x8e, 0x21, 0x1d, 0x5a, 0xaf, 0x27, 0x72, 0x24, 0xac, 0x2e, 0xa0, + 0xfe, 0x56, 0xd1, 0x6d, 0x62, 0xbe, 0x58, 0xdd, 0xbe, 0xdf, 0x14, 0x96, 0x21, 0x91, 0x0f, 0xa2, + 0xcc, 0x18, 0x45, 0xe1, 0x09, 0xe5, 0x8d, 0xd8, 0x33, 0x3f, 0x66, 0x79, 0xdc, 0x87, 0xdf, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x2f, 0x61, 0x50, 0x37, 0x73, 0x01, 0x00, 0x00, +} diff --git a/staging/src/slime.io/slime/modules/plugin/api/v1alpha1/plugin_module.proto b/staging/src/slime.io/slime/modules/plugin/api/v1alpha1/plugin_module.proto new file mode 100644 index 00000000..63fd65b5 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/api/v1alpha1/plugin_module.proto @@ -0,0 +1,24 @@ +/* +* @Author: wangchenyu +* @Date: 2022/2/23 +*/ + +syntax = "proto3"; +package slime.microservice.plugin.v1alpha1; + +option go_package = "slime.io/slime/modules/plugin/api/v1alpha1"; + +message LocalSource { + string mount = 1; +} + +message RemoteSource { + string address = 1; +} + +message PluginModule { + oneof wasm_source { + LocalSource local = 2; + RemoteSource remote = 3; + } +} diff --git a/staging/src/slime.io/slime/modules/plugin/api/v1alpha1/wrapper/slime_types.go b/staging/src/slime.io/slime/modules/plugin/api/v1alpha1/wrapper/slime_types.go new file mode 100644 index 00000000..bf710c88 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/api/v1alpha1/wrapper/slime_types.go @@ -0,0 +1,210 @@ +/* +* @Author: yangdihang +* @Date: 2020/6/8 + */ + +package wrapper + +import ( + meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "slime.io/slime/modules/plugin/api/v1alpha1" +) + +type SlimeType interface { + GetSpec() map[string]interface{} + SetSpec(spec map[string]interface{}) + GetObjectMeta() meta_v1.ObjectMeta + SetObjectMeta(metadata meta_v1.ObjectMeta) +} + +type EnvoyPlugin struct { + meta_v1.TypeMeta `json:",inline"` + meta_v1.ObjectMeta `json:"metadata"` + Spec map[string]interface{} `json:"spec"` +} + +// GetSpec from a wrapper +func (in *EnvoyPlugin) GetSpec() map[string]interface{} { + return in.Spec +} + +// SetSpec for a wrapper +func (in *EnvoyPlugin) SetSpec(spec map[string]interface{}) { + in.Spec = spec +} + +// GetObjectMeta from a wrapper +func (in *EnvoyPlugin) GetObjectMeta() meta_v1.ObjectMeta { + return in.ObjectMeta +} + +// SetObjectMeta for a wrapper +func (in *EnvoyPlugin) SetObjectMeta(metadata meta_v1.ObjectMeta) { + in.ObjectMeta = metadata +} + +// GatewayPluginList is the generic Kubernetes API list wrapper +type EnvoyPluginList struct { + meta_v1.TypeMeta `json:",inline"` + meta_v1.ListMeta `json:"metadata"` + Items []EnvoyPlugin `json:"items"` +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvoyPlugin) DeepCopyInto(out *EnvoyPlugin) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RbacConfig. +func (in *EnvoyPlugin) DeepCopy() *EnvoyPlugin { + if in == nil { + return nil + } + out := new(EnvoyPlugin) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *EnvoyPlugin) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvoyPluginList) DeepCopyInto(out *EnvoyPluginList) { + *out = *in + out.TypeMeta = in.TypeMeta + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]EnvoyPlugin, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RbacConfigList. +func (in *EnvoyPluginList) DeepCopy() *EnvoyPluginList { + if in == nil { + return nil + } + out := new(EnvoyPluginList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *EnvoyPluginList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + + return nil +} + +type PluginManager struct { + meta_v1.TypeMeta `json:",inline"` + meta_v1.ObjectMeta `json:"metadata"` + Spec map[string]interface{} `json:"spec"` +} + +// GetSpec from a wrapper +func (in *PluginManager) GetSpec() map[string]interface{} { + return in.Spec +} + +// SetSpec for a wrapper +func (in *PluginManager) SetSpec(spec map[string]interface{}) { + in.Spec = spec +} + +// GetObjectMeta from a wrapper +func (in *PluginManager) GetObjectMeta() meta_v1.ObjectMeta { + return in.ObjectMeta +} + +// SetObjectMeta for a wrapper +func (in *PluginManager) SetObjectMeta(metadata meta_v1.ObjectMeta) { + in.ObjectMeta = metadata +} + +// GatewayPluginList is the generic Kubernetes API list wrapper +type PluginManagerList struct { + meta_v1.TypeMeta `json:",inline"` + meta_v1.ListMeta `json:"metadata"` + Items []PluginManager `json:"items"` +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PluginManager) DeepCopyInto(out *PluginManager) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RbacConfig. +func (in *PluginManager) DeepCopy() *PluginManager { + if in == nil { + return nil + } + out := new(PluginManager) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PluginManager) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PluginManagerList) DeepCopyInto(out *PluginManagerList) { + *out = *in + out.TypeMeta = in.TypeMeta + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]PluginManager, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RbacConfigList. +func (in *PluginManagerList) DeepCopy() *PluginManagerList { + if in == nil { + return nil + } + out := new(PluginManagerList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PluginManagerList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + + return nil +} + +func init() { + v1alpha1.SchemeBuilder.Register(&EnvoyPlugin{}, &EnvoyPluginList{}, + &PluginManager{}, &PluginManagerList{}) +} diff --git a/staging/src/slime.io/slime/modules/plugin/charts/values.yaml b/staging/src/slime.io/slime/modules/plugin/charts/values.yaml new file mode 100644 index 00000000..e69de29b diff --git a/staging/src/slime.io/slime/modules/plugin/config/certmanager/certificate.yaml b/staging/src/slime.io/slime/modules/plugin/config/certmanager/certificate.yaml new file mode 100644 index 00000000..58db114f --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/config/certmanager/certificate.yaml @@ -0,0 +1,26 @@ +# The following manifests contain a self-signed issuer CR and a certificate CR. +# More document can be found at https://docs.cert-manager.io +# WARNING: Targets CertManager 0.11 check https://docs.cert-manager.io/en/latest/tasks/upgrading/index.html for +# breaking changes +apiVersion: cert-manager.io/v1alpha2 +kind: Issuer +metadata: + name: selfsigned-issuer + namespace: system +spec: + selfSigned: {} +--- +apiVersion: cert-manager.io/v1alpha2 +kind: Certificate +metadata: + name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml + namespace: system +spec: + # $(SERVICE_NAME) and $(SERVICE_NAMESPACE) will be substituted by kustomize + dnsNames: + - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc + - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc.cluster.local + issuerRef: + kind: Issuer + name: selfsigned-issuer + secretName: webhook-server-cert # this secret will not be prefixed, since it's not managed by kustomize diff --git a/staging/src/slime.io/slime/modules/plugin/config/certmanager/kustomization.yaml b/staging/src/slime.io/slime/modules/plugin/config/certmanager/kustomization.yaml new file mode 100644 index 00000000..bebea5a5 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/config/certmanager/kustomization.yaml @@ -0,0 +1,5 @@ +resources: +- certificate.yaml + +configurations: +- kustomizeconfig.yaml diff --git a/staging/src/slime.io/slime/modules/plugin/config/certmanager/kustomizeconfig.yaml b/staging/src/slime.io/slime/modules/plugin/config/certmanager/kustomizeconfig.yaml new file mode 100644 index 00000000..90d7c313 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/config/certmanager/kustomizeconfig.yaml @@ -0,0 +1,16 @@ +# This configuration is for teaching kustomize how to update name ref and var substitution +nameReference: +- kind: Issuer + group: cert-manager.io + fieldSpecs: + - kind: Certificate + group: cert-manager.io + path: spec/issuerRef/name + +varReference: +- kind: Certificate + group: cert-manager.io + path: spec/commonName +- kind: Certificate + group: cert-manager.io + path: spec/dnsNames diff --git a/staging/src/slime.io/slime/modules/plugin/config/crd/kustomization.yaml b/staging/src/slime.io/slime/modules/plugin/config/crd/kustomization.yaml new file mode 100644 index 00000000..64f4200b --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/config/crd/kustomization.yaml @@ -0,0 +1,24 @@ +# This kustomization.yaml is not intended to be run by itself, +# since it depends on service name and namespace that are out of this kustomize package. +# It should be run by config/default +resources: +- bases/microservice.slime.io.my.domain_pluginmanagers.yaml +- bases/microservice.slime.io.my.domain_envoyplugins.yaml +# +kubebuilder:scaffold:crdkustomizeresource + +patchesStrategicMerge: +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. +# patches here are for enabling the conversion webhook for each CRD +#- patches/webhook_in_pluginmanagers.yaml +#- patches/webhook_in_envoyplugins.yaml +# +kubebuilder:scaffold:crdkustomizewebhookpatch + +# [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix. +# patches here are for enabling the CA injection for each CRD +#- patches/cainjection_in_pluginmanagers.yaml +#- patches/cainjection_in_envoyplugins.yaml +# +kubebuilder:scaffold:crdkustomizecainjectionpatch + +# the following config is for teaching kustomize how to do kustomization for CRDs. +configurations: +- kustomizeconfig.yaml diff --git a/staging/src/slime.io/slime/modules/plugin/config/crd/kustomizeconfig.yaml b/staging/src/slime.io/slime/modules/plugin/config/crd/kustomizeconfig.yaml new file mode 100644 index 00000000..6f83d9a9 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/config/crd/kustomizeconfig.yaml @@ -0,0 +1,17 @@ +# This file is for teaching kustomize how to substitute name and namespace reference in CRD +nameReference: +- kind: Service + version: v1 + fieldSpecs: + - kind: CustomResourceDefinition + group: apiextensions.k8s.io + path: spec/conversion/webhookClientConfig/service/name + +namespace: +- kind: CustomResourceDefinition + group: apiextensions.k8s.io + path: spec/conversion/webhookClientConfig/service/namespace + create: false + +varReference: +- path: metadata/annotations diff --git a/staging/src/slime.io/slime/modules/plugin/config/crd/patches/cainjection_in_envoyplugins.yaml b/staging/src/slime.io/slime/modules/plugin/config/crd/patches/cainjection_in_envoyplugins.yaml new file mode 100644 index 00000000..f5d564a0 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/config/crd/patches/cainjection_in_envoyplugins.yaml @@ -0,0 +1,8 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: envoyplugins.microservice.slime.io.my.domain diff --git a/staging/src/slime.io/slime/modules/plugin/config/crd/patches/cainjection_in_pluginmanagers.yaml b/staging/src/slime.io/slime/modules/plugin/config/crd/patches/cainjection_in_pluginmanagers.yaml new file mode 100644 index 00000000..0acd8c20 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/config/crd/patches/cainjection_in_pluginmanagers.yaml @@ -0,0 +1,8 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: pluginmanagers.microservice.slime.io.my.domain diff --git a/staging/src/slime.io/slime/modules/plugin/config/crd/patches/webhook_in_envoyplugins.yaml b/staging/src/slime.io/slime/modules/plugin/config/crd/patches/webhook_in_envoyplugins.yaml new file mode 100644 index 00000000..80215e93 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/config/crd/patches/webhook_in_envoyplugins.yaml @@ -0,0 +1,17 @@ +# The following patch enables conversion webhook for CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: envoyplugins.microservice.slime.io.my.domain +spec: + conversion: + strategy: Webhook + webhookClientConfig: + # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, + # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) + caBundle: Cg== + service: + namespace: system + name: webhook-service + path: /convert diff --git a/staging/src/slime.io/slime/modules/plugin/config/crd/patches/webhook_in_pluginmanagers.yaml b/staging/src/slime.io/slime/modules/plugin/config/crd/patches/webhook_in_pluginmanagers.yaml new file mode 100644 index 00000000..798fefe4 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/config/crd/patches/webhook_in_pluginmanagers.yaml @@ -0,0 +1,17 @@ +# The following patch enables conversion webhook for CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: pluginmanagers.microservice.slime.io.my.domain +spec: + conversion: + strategy: Webhook + webhookClientConfig: + # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, + # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) + caBundle: Cg== + service: + namespace: system + name: webhook-service + path: /convert diff --git a/staging/src/slime.io/slime/modules/plugin/config/default/kustomization.yaml b/staging/src/slime.io/slime/modules/plugin/config/default/kustomization.yaml new file mode 100644 index 00000000..39115721 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/config/default/kustomization.yaml @@ -0,0 +1,70 @@ +# Adds namespace to all resources. +namespace: plugin-system + +# Value of this field is prepended to the +# names of all resources, e.g. a deployment named +# "wordpress" becomes "alices-wordpress". +# Note that it should also match with the prefix (text before '-') of the namespace +# field above. +namePrefix: plugin- + +# Labels to add to all resources and selectors. +#commonLabels: +# someName: someValue + +bases: +- ../crd +- ../rbac +- ../manager +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in +# crd/kustomization.yaml +#- ../webhook +# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. +#- ../certmanager +# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. +#- ../prometheus + +patchesStrategicMerge: + # Protect the /metrics endpoint by putting it behind auth. + # If you want your controller-manager to expose the /metrics + # endpoint w/o any authn/z, please comment the following line. +- manager_auth_proxy_patch.yaml + +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in +# crd/kustomization.yaml +#- manager_webhook_patch.yaml + +# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. +# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks. +# 'CERTMANAGER' needs to be enabled to use ca injection +#- webhookcainjection_patch.yaml + +# the following config is for teaching kustomize how to do var substitution +vars: +# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. +#- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR +# objref: +# kind: Certificate +# group: cert-manager.io +# version: v1alpha2 +# name: serving-cert # this name should match the one in certificate.yaml +# fieldref: +# fieldpath: metadata.namespace +#- name: CERTIFICATE_NAME +# objref: +# kind: Certificate +# group: cert-manager.io +# version: v1alpha2 +# name: serving-cert # this name should match the one in certificate.yaml +#- name: SERVICE_NAMESPACE # namespace of the service +# objref: +# kind: Service +# version: v1 +# name: webhook-service +# fieldref: +# fieldpath: metadata.namespace +#- name: SERVICE_NAME +# objref: +# kind: Service +# version: v1 +# name: webhook-service diff --git a/staging/src/slime.io/slime/modules/plugin/config/default/manager_auth_proxy_patch.yaml b/staging/src/slime.io/slime/modules/plugin/config/default/manager_auth_proxy_patch.yaml new file mode 100644 index 00000000..77e743d1 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/config/default/manager_auth_proxy_patch.yaml @@ -0,0 +1,25 @@ +# This patch inject a sidecar container which is a HTTP proxy for the +# controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system +spec: + template: + spec: + containers: + - name: kube-rbac-proxy + image: gcr.io/kubebuilder/kube-rbac-proxy:v0.5.0 + args: + - "--secure-listen-address=0.0.0.0:8443" + - "--upstream=http://127.0.0.1:8080/" + - "--logtostderr=true" + - "--v=10" + ports: + - containerPort: 8443 + name: https + - name: manager + args: + - "--metrics-addr=127.0.0.1:8080" + - "--enable-leader-election" diff --git a/staging/src/slime.io/slime/modules/plugin/config/default/manager_webhook_patch.yaml b/staging/src/slime.io/slime/modules/plugin/config/default/manager_webhook_patch.yaml new file mode 100644 index 00000000..738de350 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/config/default/manager_webhook_patch.yaml @@ -0,0 +1,23 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system +spec: + template: + spec: + containers: + - name: manager + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert + readOnly: true + volumes: + - name: cert + secret: + defaultMode: 420 + secretName: webhook-server-cert diff --git a/staging/src/slime.io/slime/modules/plugin/config/default/webhookcainjection_patch.yaml b/staging/src/slime.io/slime/modules/plugin/config/default/webhookcainjection_patch.yaml new file mode 100644 index 00000000..7e79bf99 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/config/default/webhookcainjection_patch.yaml @@ -0,0 +1,15 @@ +# This patch add annotation to admission webhook config and +# the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize. +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: MutatingWebhookConfiguration +metadata: + name: mutating-webhook-configuration + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) +--- +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: ValidatingWebhookConfiguration +metadata: + name: validating-webhook-configuration + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) diff --git a/staging/src/slime.io/slime/modules/plugin/config/manager/kustomization.yaml b/staging/src/slime.io/slime/modules/plugin/config/manager/kustomization.yaml new file mode 100644 index 00000000..5c5f0b84 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/config/manager/kustomization.yaml @@ -0,0 +1,2 @@ +resources: +- manager.yaml diff --git a/staging/src/slime.io/slime/modules/plugin/config/manager/manager.yaml b/staging/src/slime.io/slime/modules/plugin/config/manager/manager.yaml new file mode 100644 index 00000000..b6c85a52 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/config/manager/manager.yaml @@ -0,0 +1,39 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + control-plane: controller-manager + name: system +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system + labels: + control-plane: controller-manager +spec: + selector: + matchLabels: + control-plane: controller-manager + replicas: 1 + template: + metadata: + labels: + control-plane: controller-manager + spec: + containers: + - command: + - /manager + args: + - --enable-leader-election + image: controller:latest + name: manager + resources: + limits: + cpu: 100m + memory: 30Mi + requests: + cpu: 100m + memory: 20Mi + terminationGracePeriodSeconds: 10 diff --git a/staging/src/slime.io/slime/modules/plugin/config/prometheus/kustomization.yaml b/staging/src/slime.io/slime/modules/plugin/config/prometheus/kustomization.yaml new file mode 100644 index 00000000..ed137168 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/config/prometheus/kustomization.yaml @@ -0,0 +1,2 @@ +resources: +- monitor.yaml diff --git a/staging/src/slime.io/slime/modules/plugin/config/prometheus/monitor.yaml b/staging/src/slime.io/slime/modules/plugin/config/prometheus/monitor.yaml new file mode 100644 index 00000000..9b8047b7 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/config/prometheus/monitor.yaml @@ -0,0 +1,16 @@ + +# Prometheus Monitor Service (Metrics) +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + labels: + control-plane: controller-manager + name: controller-manager-metrics-monitor + namespace: system +spec: + endpoints: + - path: /metrics + port: https + selector: + matchLabels: + control-plane: controller-manager diff --git a/staging/src/slime.io/slime/modules/plugin/config/rbac/auth_proxy_client_clusterrole.yaml b/staging/src/slime.io/slime/modules/plugin/config/rbac/auth_proxy_client_clusterrole.yaml new file mode 100644 index 00000000..7d62534c --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/config/rbac/auth_proxy_client_clusterrole.yaml @@ -0,0 +1,7 @@ +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + name: metrics-reader +rules: +- nonResourceURLs: ["/metrics"] + verbs: ["get"] diff --git a/staging/src/slime.io/slime/modules/plugin/config/rbac/auth_proxy_role.yaml b/staging/src/slime.io/slime/modules/plugin/config/rbac/auth_proxy_role.yaml new file mode 100644 index 00000000..618f5e41 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/config/rbac/auth_proxy_role.yaml @@ -0,0 +1,13 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: proxy-role +rules: +- apiGroups: ["authentication.k8s.io"] + resources: + - tokenreviews + verbs: ["create"] +- apiGroups: ["authorization.k8s.io"] + resources: + - subjectaccessreviews + verbs: ["create"] diff --git a/staging/src/slime.io/slime/modules/plugin/config/rbac/auth_proxy_role_binding.yaml b/staging/src/slime.io/slime/modules/plugin/config/rbac/auth_proxy_role_binding.yaml new file mode 100644 index 00000000..48ed1e4b --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/config/rbac/auth_proxy_role_binding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: proxy-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: proxy-role +subjects: +- kind: ServiceAccount + name: default + namespace: system diff --git a/staging/src/slime.io/slime/modules/plugin/config/rbac/auth_proxy_service.yaml b/staging/src/slime.io/slime/modules/plugin/config/rbac/auth_proxy_service.yaml new file mode 100644 index 00000000..6cf656be --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/config/rbac/auth_proxy_service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + control-plane: controller-manager + name: controller-manager-metrics-service + namespace: system +spec: + ports: + - name: https + port: 8443 + targetPort: https + selector: + control-plane: controller-manager diff --git a/staging/src/slime.io/slime/modules/plugin/config/rbac/envoyplugin_editor_role.yaml b/staging/src/slime.io/slime/modules/plugin/config/rbac/envoyplugin_editor_role.yaml new file mode 100644 index 00000000..db0e1dbf --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/config/rbac/envoyplugin_editor_role.yaml @@ -0,0 +1,24 @@ +# permissions for end users to edit envoyplugins. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: envoyplugin-editor-role +rules: +- apiGroups: + - microservice.slime.io.my.domain + resources: + - envoyplugins + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - microservice.slime.io.my.domain + resources: + - envoyplugins/status + verbs: + - get diff --git a/staging/src/slime.io/slime/modules/plugin/config/rbac/envoyplugin_viewer_role.yaml b/staging/src/slime.io/slime/modules/plugin/config/rbac/envoyplugin_viewer_role.yaml new file mode 100644 index 00000000..76e4215b --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/config/rbac/envoyplugin_viewer_role.yaml @@ -0,0 +1,20 @@ +# permissions for end users to view envoyplugins. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: envoyplugin-viewer-role +rules: +- apiGroups: + - microservice.slime.io.my.domain + resources: + - envoyplugins + verbs: + - get + - list + - watch +- apiGroups: + - microservice.slime.io.my.domain + resources: + - envoyplugins/status + verbs: + - get diff --git a/staging/src/slime.io/slime/modules/plugin/config/rbac/kustomization.yaml b/staging/src/slime.io/slime/modules/plugin/config/rbac/kustomization.yaml new file mode 100644 index 00000000..66c28338 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/config/rbac/kustomization.yaml @@ -0,0 +1,12 @@ +resources: +- role.yaml +- role_binding.yaml +- leader_election_role.yaml +- leader_election_role_binding.yaml +# Comment the following 4 lines if you want to disable +# the auth proxy (https://github.com/brancz/kube-rbac-proxy) +# which protects your /metrics endpoint. +- auth_proxy_service.yaml +- auth_proxy_role.yaml +- auth_proxy_role_binding.yaml +- auth_proxy_client_clusterrole.yaml diff --git a/staging/src/slime.io/slime/modules/plugin/config/rbac/leader_election_role.yaml b/staging/src/slime.io/slime/modules/plugin/config/rbac/leader_election_role.yaml new file mode 100644 index 00000000..eaa79158 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/config/rbac/leader_election_role.yaml @@ -0,0 +1,32 @@ +# permissions to do leader election. +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: leader-election-role +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - configmaps/status + verbs: + - get + - update + - patch +- apiGroups: + - "" + resources: + - events + verbs: + - create diff --git a/staging/src/slime.io/slime/modules/plugin/config/rbac/leader_election_role_binding.yaml b/staging/src/slime.io/slime/modules/plugin/config/rbac/leader_election_role_binding.yaml new file mode 100644 index 00000000..eed16906 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/config/rbac/leader_election_role_binding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: leader-election-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: leader-election-role +subjects: +- kind: ServiceAccount + name: default + namespace: system diff --git a/staging/src/slime.io/slime/modules/plugin/config/rbac/pluginmanager_editor_role.yaml b/staging/src/slime.io/slime/modules/plugin/config/rbac/pluginmanager_editor_role.yaml new file mode 100644 index 00000000..f6b2a914 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/config/rbac/pluginmanager_editor_role.yaml @@ -0,0 +1,24 @@ +# permissions for end users to edit pluginmanagers. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: pluginmanager-editor-role +rules: +- apiGroups: + - microservice.slime.io.my.domain + resources: + - pluginmanagers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - microservice.slime.io.my.domain + resources: + - pluginmanagers/status + verbs: + - get diff --git a/staging/src/slime.io/slime/modules/plugin/config/rbac/pluginmanager_viewer_role.yaml b/staging/src/slime.io/slime/modules/plugin/config/rbac/pluginmanager_viewer_role.yaml new file mode 100644 index 00000000..e0a3ab29 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/config/rbac/pluginmanager_viewer_role.yaml @@ -0,0 +1,20 @@ +# permissions for end users to view pluginmanagers. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: pluginmanager-viewer-role +rules: +- apiGroups: + - microservice.slime.io.my.domain + resources: + - pluginmanagers + verbs: + - get + - list + - watch +- apiGroups: + - microservice.slime.io.my.domain + resources: + - pluginmanagers/status + verbs: + - get diff --git a/staging/src/slime.io/slime/modules/plugin/config/rbac/role_binding.yaml b/staging/src/slime.io/slime/modules/plugin/config/rbac/role_binding.yaml new file mode 100644 index 00000000..8f265870 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/config/rbac/role_binding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: manager-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: system diff --git a/staging/src/slime.io/slime/modules/plugin/config/samples/microservice.slime.io_v1alpha1_envoyplugin.yaml b/staging/src/slime.io/slime/modules/plugin/config/samples/microservice.slime.io_v1alpha1_envoyplugin.yaml new file mode 100644 index 00000000..19c79dbd --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/config/samples/microservice.slime.io_v1alpha1_envoyplugin.yaml @@ -0,0 +1,7 @@ +apiVersion: microservice.slime.io.my.domain/v1alpha1 +kind: EnvoyPlugin +metadata: + name: envoyplugin-sample +spec: + # Add fields here + foo: bar diff --git a/staging/src/slime.io/slime/modules/plugin/config/samples/microservice.slime.io_v1alpha1_pluginmanager.yaml b/staging/src/slime.io/slime/modules/plugin/config/samples/microservice.slime.io_v1alpha1_pluginmanager.yaml new file mode 100644 index 00000000..d8291878 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/config/samples/microservice.slime.io_v1alpha1_pluginmanager.yaml @@ -0,0 +1,7 @@ +apiVersion: microservice.slime.io.my.domain/v1alpha1 +kind: PluginManager +metadata: + name: pluginmanager-sample +spec: + # Add fields here + foo: bar diff --git a/staging/src/slime.io/slime/modules/plugin/config/webhook/kustomization.yaml b/staging/src/slime.io/slime/modules/plugin/config/webhook/kustomization.yaml new file mode 100644 index 00000000..9cf26134 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/config/webhook/kustomization.yaml @@ -0,0 +1,6 @@ +resources: +- manifests.yaml +- service.yaml + +configurations: +- kustomizeconfig.yaml diff --git a/staging/src/slime.io/slime/modules/plugin/config/webhook/kustomizeconfig.yaml b/staging/src/slime.io/slime/modules/plugin/config/webhook/kustomizeconfig.yaml new file mode 100644 index 00000000..25e21e3c --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/config/webhook/kustomizeconfig.yaml @@ -0,0 +1,25 @@ +# the following config is for teaching kustomize where to look at when substituting vars. +# It requires kustomize v2.1.0 or newer to work properly. +nameReference: +- kind: Service + version: v1 + fieldSpecs: + - kind: MutatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/name + - kind: ValidatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/name + +namespace: +- kind: MutatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/namespace + create: true +- kind: ValidatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/namespace + create: true + +varReference: +- path: metadata/annotations diff --git a/staging/src/slime.io/slime/modules/plugin/config/webhook/service.yaml b/staging/src/slime.io/slime/modules/plugin/config/webhook/service.yaml new file mode 100644 index 00000000..31e0f829 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/config/webhook/service.yaml @@ -0,0 +1,12 @@ + +apiVersion: v1 +kind: Service +metadata: + name: webhook-service + namespace: system +spec: + ports: + - port: 443 + targetPort: 9443 + selector: + control-plane: controller-manager diff --git a/staging/src/slime.io/slime/modules/plugin/controllers/controllers.go b/staging/src/slime.io/slime/modules/plugin/controllers/controllers.go new file mode 100644 index 00000000..b4ae0c15 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/controllers/controllers.go @@ -0,0 +1,8 @@ +package controllers + +import ( + frameworkmodel "slime.io/slime/framework/model" + "slime.io/slime/modules/plugin/model" +) + +var log = model.ModuleLog.WithField(frameworkmodel.LogFieldKeyPkg, "controllers") diff --git a/staging/src/slime.io/slime/modules/plugin/controllers/conversion.go b/staging/src/slime.io/slime/modules/plugin/controllers/conversion.go new file mode 100644 index 00000000..3d6349dc --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/controllers/conversion.go @@ -0,0 +1,422 @@ +/* +* @Author: yangdihang +* @Date: 2020/6/8 + */ + +package controllers + +import ( + "fmt" + "strings" + + "slime.io/slime/framework/util" + "slime.io/slime/modules/plugin/api/v1alpha1" + microserviceslimeiov1alpha1types "slime.io/slime/modules/plugin/api/v1alpha1" + microserviceslimeiov1alpha1 "slime.io/slime/modules/plugin/api/v1alpha1/wrapper" + + envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + envoy_extensions_wasm_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/wasm/v3" + "github.com/gogo/protobuf/types" + istio "istio.io/api/networking/v1alpha3" +) + +// genGatewayCfps is a custom func to handle EnvoyPlugin gateway +// default is nil, ignore gateway +var genGatewayCfps func(in *microserviceslimeiov1alpha1types.EnvoyPlugin, namespace string, t target, patchCtx istio.EnvoyFilter_PatchContext, + p *microserviceslimeiov1alpha1types.Plugin, m *v1alpha1.Plugin_Inline) []*istio.EnvoyFilter_EnvoyConfigObjectPatch + +type target struct { + applyTo istio.EnvoyFilter_ApplyTo + host, route string +} + +var ( + directPatchingPlugins = []string{ + util.Envoy_HttpRatelimit, + util.Envoy_Cors, + util.Envoy_Ratelimit_v1, // keep backward compatibility + } +) + +func directPatching(name string) bool { + for _, plugin := range directPatchingPlugins { + if name == plugin { + return true + } + } + return false +} + +// translate EnvoyPlugin +func translatePluginToPatch(name, typeurl string, setting *types.Struct) *istio.EnvoyFilter_Patch { + patch := &istio.EnvoyFilter_Patch{} + patch.Value = &types.Struct{ + Fields: map[string]*types.Value{ + util.Struct_HttpFilter_TypedPerFilterConfig: { + Kind: &types.Value_StructValue{ + StructValue: &types.Struct{ + Fields: map[string]*types.Value{ + name: { + Kind: &types.Value_StructValue{StructValue: &types.Struct{ + Fields: map[string]*types.Value{ + util.Struct_Any_Value: { + Kind: &types.Value_StructValue{StructValue: setting}, + }, + util.Struct_Any_TypedUrl: { + Kind: &types.Value_StringValue{StringValue: typeurl}, + }, + util.Struct_Any_AtType: { + Kind: &types.Value_StringValue{StringValue: util.TypeUrl_UdpaTypedStruct}, + }, + }, + }}, + }, + }, + }, + }, + }, + }, + } + return patch +} + +func translateRlsAndCorsToDirectPatch(settings *types.Struct, applyToHTTPRoute bool) *istio.EnvoyFilter_Patch { + fieldPatchTo := "" + if applyToHTTPRoute { + fieldPatchTo = "route" + } + return translatePluginToDirectPatch(settings, fieldPatchTo) +} + +func translatePluginToDirectPatch(settings *types.Struct, fieldPatchTo string) *istio.EnvoyFilter_Patch { + patch := &istio.EnvoyFilter_Patch{} + if fieldPatchTo != "" { + patch.Value = &types.Struct{ + Fields: map[string]*types.Value{ + fieldPatchTo: { + Kind: &types.Value_StructValue{ + StructValue: settings, + }, + }, + }, + } + } else { + patch.Value = settings + } + return patch +} + +func (r *EnvoyPluginReconciler) translateEnvoyPlugin(cr *microserviceslimeiov1alpha1.EnvoyPlugin, out *istio.EnvoyFilter) { + pb, err := util.FromJSONMap("slime.microservice.plugin.v1alpha1.EnvoyPlugin", cr.Spec) + if err != nil { + log.Errorf("unable to convert envoyPlugin to envoyFilter,%+v", err) + return + } + in := pb.(*microserviceslimeiov1alpha1types.EnvoyPlugin) + + if in.WorkloadSelector != nil { + out.WorkloadSelector = &istio.WorkloadSelector{ + Labels: in.WorkloadSelector.Labels, + } + } + out.ConfigPatches = make([]*istio.EnvoyFilter_EnvoyConfigObjectPatch, 0) + + var targets []target + for _, h := range in.Host { + targets = append(targets, target{ + applyTo: istio.EnvoyFilter_VIRTUAL_HOST, + host: h, + }) + } + for _, fullRoute := range in.Route { + host, route := "", fullRoute + if ss := strings.SplitN(fullRoute, "/", 2); len(ss) == 2 { + host, route = ss[0], ss[1] + } + + targets = append(targets, target{ + applyTo: istio.EnvoyFilter_HTTP_ROUTE, + host: host, + route: route, + }) + } + + for _, t := range targets { + for _, p := range in.Plugins { + if !p.Enable { + continue + } + + if p.PluginSettings == nil { + log.Errorf("empty setting, cause error happend, skip plugin build, plugin: %s", p.Name) + continue + } + + patchCtx := istio.EnvoyFilter_ANY + if !strings.HasPrefix(t.host, "inbound|") { // keep backward compatibility + switch p.ListenerType { + case v1alpha1.Plugin_Outbound: + patchCtx = istio.EnvoyFilter_SIDECAR_OUTBOUND + case v1alpha1.Plugin_Inbound: + patchCtx = istio.EnvoyFilter_SIDECAR_INBOUND + case v1alpha1.Plugin_Gateway: + patchCtx = istio.EnvoyFilter_GATEWAY + } + } + + switch m := p.PluginSettings.(type) { + case *v1alpha1.Plugin_Wasm: + log.Errorf("implentment, cause wasm not been support in envoyplugin settings, skip plugin build, plugin: %s") + continue + case *v1alpha1.Plugin_Inline: + if len(in.Gateway) > 0 && genGatewayCfps != nil { + cfps := genGatewayCfps(in, cr.Namespace, t, patchCtx, p, m) + out.ConfigPatches = append(out.ConfigPatches, cfps...) + } else { + vhost := &istio.EnvoyFilter_RouteConfigurationMatch_VirtualHostMatch{ + Name: t.host, + } + if t.applyTo == istio.EnvoyFilter_HTTP_ROUTE { + vhost.Route = &istio.EnvoyFilter_RouteConfigurationMatch_RouteMatch{ + Name: t.route, + } + } + cfp := generateCfp(t, patchCtx, vhost, p, m) + out.ConfigPatches = append(out.ConfigPatches, cfp) + } + } + } + } + log.Debugf("translate EnvoyPlugin to Envoyfilter: %v", out) +} + +func generateCfp(t target, patchCtx istio.EnvoyFilter_PatchContext, vhost *istio.EnvoyFilter_RouteConfigurationMatch_VirtualHostMatch, + p *microserviceslimeiov1alpha1types.Plugin, m *v1alpha1.Plugin_Inline) *istio.EnvoyFilter_EnvoyConfigObjectPatch { + cfp := &istio.EnvoyFilter_EnvoyConfigObjectPatch{ + ApplyTo: t.applyTo, + Match: &istio.EnvoyFilter_EnvoyConfigObjectMatch{ + Context: patchCtx, + ObjectTypes: &istio.EnvoyFilter_EnvoyConfigObjectMatch_RouteConfiguration{ + RouteConfiguration: &istio.EnvoyFilter_RouteConfigurationMatch{ + Vhost: vhost, + }, + }, + }, + } + + if directPatching(p.Name) { + cfp.Patch = translateRlsAndCorsToDirectPatch(m.Inline.Settings, t.applyTo == istio.EnvoyFilter_HTTP_ROUTE) + } else if m.Inline.DirectPatch { + cfp.Patch = translatePluginToDirectPatch(m.Inline.Settings, m.Inline.FieldPatchTo) + } else { + cfp.Patch = translatePluginToPatch(p.Name, p.TypeUrl, m.Inline.Settings) + } + + cfp.Patch.Operation = istio.EnvoyFilter_Patch_MERGE + return cfp +} + +// translate PluginManager +func (r *PluginManagerReconciler) translatePluginManager(in *v1alpha1.PluginManager, out *istio.EnvoyFilter) { + out.WorkloadSelector = &istio.WorkloadSelector{ + Labels: in.WorkloadLabels, + } + out.ConfigPatches = make([]*istio.EnvoyFilter_EnvoyConfigObjectPatch, 0) + for _, p := range in.Plugin { + if !p.Enable { + continue + } + patch, err := r.convertPluginToPatch(p) + if err != nil { + log.Errorf("cause error happened, skip plugin build, plugin: %s, %+v", p.Name, err) + continue + } + out.ConfigPatches = append(out.ConfigPatches, patch) + } +} + +func (r *PluginManagerReconciler) convertPluginToPatch(in *v1alpha1.Plugin) (*istio.EnvoyFilter_EnvoyConfigObjectPatch, error) { + listener := &istio.EnvoyFilter_ListenerMatch{ + FilterChain: &istio.EnvoyFilter_ListenerMatch_FilterChainMatch{ + Filter: &istio.EnvoyFilter_ListenerMatch_FilterMatch{ + Name: util.Envoy_HttpConnectionManager, + SubFilter: &istio.EnvoyFilter_ListenerMatch_SubFilterMatch{ + Name: util.Envoy_Route, + }, + }, + }, + } + + if in.Port != 0 { + listener.PortNumber = in.Port + } + + out := &istio.EnvoyFilter_EnvoyConfigObjectPatch{ + ApplyTo: istio.EnvoyFilter_HTTP_FILTER, + Match: &istio.EnvoyFilter_EnvoyConfigObjectMatch{ + ObjectTypes: &istio.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ + Listener: listener, + }, + }, + Patch: &istio.EnvoyFilter_Patch{ + Operation: istio.EnvoyFilter_Patch_INSERT_BEFORE, + Value: &types.Struct{ + Fields: map[string]*types.Value{}, + }, + }, + } + + switch in.ListenerType { + case v1alpha1.Plugin_Outbound: + out.Match.Context = istio.EnvoyFilter_SIDECAR_OUTBOUND + case v1alpha1.Plugin_Inbound: + out.Match.Context = istio.EnvoyFilter_SIDECAR_INBOUND + case v1alpha1.Plugin_Gateway: + out.Match.Context = istio.EnvoyFilter_GATEWAY + } + + var err error + if in.PluginSettings != nil { + switch m := in.PluginSettings.(type) { + case *v1alpha1.Plugin_Wasm: + out.Patch.Value.Fields[util.Struct_Wasm_Name] = &types.Value{ + Kind: &types.Value_StringValue{ + StringValue: util.Envoy_FilterHttpWasm, + }, + } + + if m.Wasm.RootID == "" { + err = fmt.Errorf("plugin:%s, wasm插件rootID丢失", in.Name) + } else if m.Wasm.FileName == "" { + err = fmt.Errorf("plugin: %s, wasm 文件缺失", in.Name) + } else { + if err == nil { + filepath := r.wasm.Get(m.Wasm.FileName) + pluginConfig := &envoy_extensions_wasm_v3.PluginConfig{ + Name: in.Name, + RootId: m.Wasm.RootID, + Vm: &envoy_extensions_wasm_v3.PluginConfig_VmConfig{ + VmConfig: &envoy_extensions_wasm_v3.VmConfig{ + VmId: in.Name, + Runtime: util.Envoy_WasmV8, + Code: &envoy_config_core_v3.AsyncDataSource{ + Specifier: &envoy_config_core_v3.AsyncDataSource_Local{ + Local: &envoy_config_core_v3.DataSource{ + Specifier: &envoy_config_core_v3.DataSource_Filename{ + Filename: filepath, + }, + }, + }, + }, + }, + }, + } + settings, err := util.MessageToStruct(pluginConfig) + if m.Wasm.Settings != nil { + isStringSettings := false + + // string类型的配置解析为 google.protobuf.StringValue + if len(m.Wasm.Settings.Fields) == 1 && m.Wasm.Settings.Fields["_string"] != nil { + parseTostring := m.Wasm.Settings.Fields["_string"] + if s, ok := parseTostring.Kind.(*types.Value_StringValue); ok { + isStringSettings = true + settings.Fields[util.Struct_Wasm_Configuration] = &types.Value{ + Kind: &types.Value_StructValue{ + StructValue: &types.Struct{ + Fields: map[string]*types.Value{ + util.Struct_Any_AtType: { + Kind: &types.Value_StringValue{StringValue: util.TypeUrl_StringValue}, + }, + util.Struct_Any_Value: { + Kind: s, + }, + }, + }, + }, + } + } + } + + // 非string类型的配置解析为 "type.googleapis.com/udpa.type.v1.TypedStruct" + if !isStringSettings { + settings.Fields[util.Struct_Wasm_Configuration] = &types.Value{ + Kind: &types.Value_StructValue{ + StructValue: &types.Struct{ + Fields: map[string]*types.Value{ + util.Struct_Any_AtType: { + Kind: &types.Value_StringValue{StringValue: util.TypeUrl_UdpaTypedStruct}, + }, + util.Struct_Any_Value: { + Kind: &types.Value_StructValue{StructValue: m.Wasm.Settings}, + }, + }, + }, + }, + } + } + } + if err == nil { + out.Patch.Value.Fields[util.Struct_HttpFilter_TypedConfig] = &types.Value{ + Kind: &types.Value_StructValue{ + StructValue: &types.Struct{ + Fields: map[string]*types.Value{ + util.Struct_Any_TypedUrl: { + Kind: &types.Value_StringValue{StringValue: util.TypeUrl_EnvoyFilterHttpWasm}, + }, + util.Struct_Any_AtType: { + Kind: &types.Value_StringValue{StringValue: util.TypeUrl_UdpaTypedStruct}, + }, + util.Struct_Any_Value: { + Kind: &types.Value_StructValue{StructValue: &types.Struct{ + Fields: map[string]*types.Value{ + util.Struct_Wasm_Config: { + Kind: &types.Value_StructValue{ + StructValue: settings, + }, + }, + }, + }}, + }, + }, + }, + }, + } + } + } + } + case *v1alpha1.Plugin_Inline: + out.Patch.Value.Fields[util.Struct_HttpFilter_TypedConfig] = &types.Value{ + Kind: &types.Value_StructValue{ + StructValue: &types.Struct{ + Fields: map[string]*types.Value{ + util.Struct_Any_TypedUrl: { + Kind: &types.Value_StringValue{StringValue: in.TypeUrl}, + }, + util.Struct_Any_AtType: { + Kind: &types.Value_StringValue{StringValue: util.TypeUrl_UdpaTypedStruct}, + }, + util.Struct_Any_Value: { + Kind: &types.Value_StructValue{StructValue: m.Inline.Settings}, + }, + }, + }, + }, + } + out.Patch.Value.Fields[util.Struct_HttpFilter_Name] = &types.Value{ + Kind: &types.Value_StringValue{ + StringValue: in.Name, + }, + } + } + } else { + out.Patch.Value.Fields[util.Struct_HttpFilter_Name] = &types.Value{ + Kind: &types.Value_StringValue{ + StringValue: in.Name, + }, + } + } + if err != nil { + return nil, err + } + return out, nil +} diff --git a/staging/src/slime.io/slime/modules/plugin/controllers/envoyplugin_controller.go b/staging/src/slime.io/slime/modules/plugin/controllers/envoyplugin_controller.go new file mode 100644 index 00000000..48c2a339 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/controllers/envoyplugin_controller.go @@ -0,0 +1,141 @@ +/* + + +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 controllers + +import ( + "context" + + istio "istio.io/api/networking/v1alpha3" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "slime.io/slime/framework/apis/networking/v1alpha3" + "slime.io/slime/framework/bootstrap" + "slime.io/slime/framework/model" + "slime.io/slime/framework/util" + microserviceslimeiov1alpha1 "slime.io/slime/modules/plugin/api/v1alpha1/wrapper" +) + +// EnvoyPluginReconciler reconciles a EnvoyPlugin object +type EnvoyPluginReconciler struct { + client.Client + Scheme *runtime.Scheme + Env *bootstrap.Environment +} + +// +kubebuilder:rbac:groups=microservice.slime.io.my.domain,resources=envoyplugins,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=microservice.slime.io.my.domain,resources=envoyplugins/status,verbs=get;update;patch + +func (r *EnvoyPluginReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { + _ = context.Background() + // Fetch the EnvoyPlugin instance + instance := µserviceslimeiov1alpha1.EnvoyPlugin{} + err := r.Client.Get(context.TODO(), req.NamespacedName, instance) + + if err != nil { + if errors.IsNotFound(err) { + return reconcile.Result{}, nil + } else { + return reconcile.Result{}, err + } + } + + istioRev := model.IstioRevFromLabel(instance.Labels) + if !r.Env.RevInScope(istioRev) { + log.Debugf("existing envoyplugin %v istiorev %s but out %s, skip ...", + req.NamespacedName, istioRev, r.Env.IstioRev()) + return reconcile.Result{}, nil + } + + // 资源更新 + ef := r.newEnvoyFilterForEnvoyPlugin(instance) + if ef == nil { + return reconcile.Result{}, nil + } + + // 测试需要 + if r.Scheme != nil { + // Set EnvoyPlugin instance as the owner and controller + if err := controllerutil.SetControllerReference(instance, ef, r.Scheme); err != nil { + return reconcile.Result{}, nil + } + } + model.PatchIstioRevLabel(&ef.Labels, istioRev) + + found := &v1alpha3.EnvoyFilter{} + err = r.Client.Get(context.TODO(), types.NamespacedName{Name: ef.Name, Namespace: ef.Namespace}, found) + if err != nil { + if errors.IsNotFound(err) { + err = nil + found = nil + } else { + return reconcile.Result{}, err + } + } + + if found == nil { + log.Infof("Creating a new EnvoyFilter in %s:%s", ef.Namespace, ef.Name) + err = r.Client.Create(context.TODO(), ef) + if err != nil { + return reconcile.Result{}, err + } + } else if model.IstioRevFromLabel(found.Labels) != istioRev { + log.Debugf("existed envoyfilter %v istioRev %s but our rev %s, skip updating to %+v", + req.NamespacedName, model.IstioRevFromLabel(found.Labels), istioRev, ef) + } else { + log.Infof("Update a EnvoyFilter in %s:%s", ef.Namespace, ef.Name) + ef.ResourceVersion = found.ResourceVersion + err := r.Client.Update(context.TODO(), ef) + if err != nil { + return reconcile.Result{}, err + } + } + + return ctrl.Result{}, nil +} + +func (r *EnvoyPluginReconciler) newEnvoyFilterForEnvoyPlugin(cr *microserviceslimeiov1alpha1.EnvoyPlugin) *v1alpha3.EnvoyFilter { + envoyFilter := &istio.EnvoyFilter{} + r.translateEnvoyPlugin(cr, envoyFilter) + if envoyFilter == nil { + return nil + } + envoyFilterWrapper := &v1alpha3.EnvoyFilter{ + ObjectMeta: metav1.ObjectMeta{ + Name: cr.Name, + Namespace: cr.Namespace, + }, + } + + m, err := util.ProtoToMap(envoyFilter) + if err != nil { + return nil + } + envoyFilterWrapper.Spec = m + return envoyFilterWrapper +} + +func (r *EnvoyPluginReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(µserviceslimeiov1alpha1.EnvoyPlugin{}). + Complete(r) +} diff --git a/staging/src/slime.io/slime/modules/plugin/controllers/pluginmanager_controller.go b/staging/src/slime.io/slime/modules/plugin/controllers/pluginmanager_controller.go new file mode 100644 index 00000000..b6cfe8da --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/controllers/pluginmanager_controller.go @@ -0,0 +1,149 @@ +/* + + +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 controllers + +import ( + "context" + + istio "istio.io/api/networking/v1alpha3" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "slime.io/slime/framework/apis/networking/v1alpha3" + "slime.io/slime/framework/bootstrap" + "slime.io/slime/framework/model" + "slime.io/slime/framework/util" + microserviceslimeiov1alpha1types "slime.io/slime/modules/plugin/api/v1alpha1" + "slime.io/slime/modules/plugin/api/v1alpha1/wrapper" + "slime.io/slime/modules/plugin/controllers/wasm" + + microserviceslimeiov1alpha1 "slime.io/slime/modules/plugin/api/v1alpha1/wrapper" +) + +// PluginManagerReconciler reconciles a PluginManager object +type PluginManagerReconciler struct { + client.Client + Scheme *runtime.Scheme + + wasm wasm.Getter + env *bootstrap.Environment +} + +// +kubebuilder:rbac:groups=microservice.slime.io.my.domain,resources=pluginmanagers,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=microservice.slime.io.my.domain,resources=pluginmanagers/status,verbs=get;update;patch + +func (r *PluginManagerReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { + _ = context.Background() + // your logic here + // Fetch the PluginManager instance + instance := &wrapper.PluginManager{} + err := r.Client.Get(context.TODO(), req.NamespacedName, instance) + + if err != nil { + if errors.IsNotFound(err) { + // TODO del relevant resource + return reconcile.Result{}, nil + } else { + return reconcile.Result{}, err + } + } + + istioRev := model.IstioRevFromLabel(instance.Labels) + if !r.env.RevInScope(istioRev) { + log.Debugf("existing pluginmanager %v istiorev %s but out %s, skip ...", + req.NamespacedName, istioRev, r.env.IstioRev()) + return reconcile.Result{}, nil + } + + // 资源更新 + ef := r.newPluginManagerForEnvoyPlugin(instance) + if ef == nil { + // 由于配置错误导致的,因此直接返回nil,避免reconcile重试 + return reconcile.Result{}, nil + } + if r.Scheme != nil { + // Set EnvoyPlugin instance as the owner and controller + if err := controllerutil.SetControllerReference(instance, ef, r.Scheme); err != nil { + return reconcile.Result{}, nil + } + } + model.PatchIstioRevLabel(&ef.Labels, istioRev) + + found := &v1alpha3.EnvoyFilter{} + nsName := types.NamespacedName{Name: ef.Name, Namespace: ef.Namespace} + err = r.Client.Get(context.TODO(), nsName, found) + + if err != nil { + if errors.IsNotFound(err) { + found = nil + err = nil + } + } + + if found == nil { + log.Infof("Creating a new EnvoyFilter in %s:%s", ef.Namespace, ef.Name) + err = r.Client.Create(context.TODO(), ef) + if err != nil { + return reconcile.Result{}, err + } + } else if model.IstioRevFromLabel(found.Labels) != istioRev { + log.Debugf("existing envoyfilter %v istioRev %s but our %s, skip ...", nsName, model.IstioRevFromLabel(found.Labels), istioRev) + return reconcile.Result{}, nil + } else { + log.Infof("Update a EnvoyFilter in %v", nsName) + ef.ResourceVersion = found.ResourceVersion + err := r.Client.Update(context.TODO(), ef) + if err != nil { + return reconcile.Result{}, err + } + } + + return ctrl.Result{}, nil +} + +func (r *PluginManagerReconciler) newPluginManagerForEnvoyPlugin(cr *wrapper.PluginManager) *v1alpha3.EnvoyFilter { + pb, err := util.FromJSONMap("slime.microservice.plugin.v1alpha1.PluginManager", cr.Spec) + if err != nil { + log.Errorf("unable to convert pluginManager to envoyFilter, %+v", err) + return nil + } + + envoyFilter := &istio.EnvoyFilter{} + r.translatePluginManager(pb.(*microserviceslimeiov1alpha1types.PluginManager), envoyFilter) + envoyFilterWrapper := &v1alpha3.EnvoyFilter{ + ObjectMeta: metav1.ObjectMeta{ + Name: cr.Name, + Namespace: cr.Namespace, + }, + } + if m, err := util.ProtoToMap(envoyFilter); err == nil { + envoyFilterWrapper.Spec = m + return envoyFilterWrapper + } + return nil +} + +func (r *PluginManagerReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(µserviceslimeiov1alpha1.PluginManager{}). + Complete(r) +} diff --git a/staging/src/slime.io/slime/modules/plugin/controllers/suite_test.go b/staging/src/slime.io/slime/modules/plugin/controllers/suite_test.go new file mode 100644 index 00000000..0487c476 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/controllers/suite_test.go @@ -0,0 +1,81 @@ +/* + + +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 controllers + +import ( + "path/filepath" + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + "sigs.k8s.io/controller-runtime/pkg/envtest/printer" + microserviceslimeiov1alpha1 "slime.io/slime/modules/plugin/api/v1alpha1" + // +kubebuilder:scaffold:imports +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +var ( + cfg *rest.Config + k8sClient client.Client + testEnv *envtest.Environment +) + +func TestAPIs(t *testing.T) { + RegisterFailHandler(Fail) + + RunSpecsWithDefaultAndCustomReporters(t, + "Controller Suite", + []Reporter{printer.NewlineReporter{}}) +} + +var _ = BeforeSuite(func(done Done) { + By("bootstrapping test environment") + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")}, + } + + var err error + cfg, err = testEnv.Start() + Expect(err).ToNot(HaveOccurred()) + Expect(cfg).ToNot(BeNil()) + + err = microserviceslimeiov1alpha1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + err = microserviceslimeiov1alpha1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + // +kubebuilder:scaffold:scheme + + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(err).ToNot(HaveOccurred()) + Expect(k8sClient).ToNot(BeNil()) + + close(done) +}, 60) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + err := testEnv.Stop() + Expect(err).ToNot(HaveOccurred()) +}) diff --git a/staging/src/slime.io/slime/modules/plugin/controllers/wasm/source.go b/staging/src/slime.io/slime/modules/plugin/controllers/wasm/source.go new file mode 100644 index 00000000..796d0e46 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/controllers/wasm/source.go @@ -0,0 +1,19 @@ +package wasm + +import "strings" + +type Getter interface { + Get(wasmfile string) string +} + +type LocalSource struct { + Mount string +} + +func (l *LocalSource) Get(wasmfile string) string { + if strings.HasSuffix(wasmfile, "/") { + return l.Mount + wasmfile + } else { + return l.Mount + "/" + wasmfile + } +} diff --git a/staging/src/slime.io/slime/modules/plugin/go.mod b/staging/src/slime.io/slime/modules/plugin/go.mod new file mode 100644 index 00000000..efb4671a --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/go.mod @@ -0,0 +1,24 @@ +module slime.io/slime/modules/plugin + +go 1.13 + +require ( + github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad + github.com/gogo/protobuf v1.3.2 + github.com/golang/protobuf v1.4.2 + github.com/onsi/ginkgo v1.11.0 + github.com/onsi/gomega v1.8.1 + github.com/sirupsen/logrus v1.4.2 + istio.io/api v0.0.0-20210322145030-ec7ef4cd6eaf + k8s.io/apimachinery v0.20.2 + k8s.io/client-go v0.17.2 + sigs.k8s.io/controller-runtime v0.5.0 + slime.io/slime/framework v0.0.0 +) + +replace ( + k8s.io/api => k8s.io/api v0.17.2 + k8s.io/apimachinery => k8s.io/apimachinery v0.17.2 + + slime.io/slime/framework => ../../../../../../framework +) diff --git a/staging/src/slime.io/slime/modules/plugin/go.sum b/staging/src/slime.io/slime/modules/plugin/go.sum new file mode 100644 index 00000000..3ed39bd5 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/go.sum @@ -0,0 +1,548 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= +github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= +github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 h1:cqQfy1jclcSy/FwLjemeg3SR1yaINm74aQyupQ0Bl8M= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= +github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad h1:EmNYJhPYy0pOFjCx2PrgtaBXmee0iUX9hLlxE1xHOJE= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M= +github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= +github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/zapr v0.1.0 h1:h+WVe9j6HAA01niTJPA/kKH0i7e0rLZBCwauQFcRE54= +github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= +github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= +github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= +github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= +github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= +github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= +github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk= +github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= +github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= +github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= +github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= +github.com/go-openapi/spec v0.19.3 h1:0XRyw8kguri6Yw4SxhsQA/atC88yqrk0+G4YhI2wabc= +github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= +github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= +github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= +github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7 h1:u4bArs140e9+AfE52mFHOXVFnOSBJBRlzTHrOPLOIhE= +github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho= +github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995/go.mod h1:lJgMEyOkYFkPcDKwRXegd+iM6E7matEszMG5HhwytU8= +github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk= +github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= +github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= +github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= +github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw= +github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.8.1 h1:C5Dqfs/LeauYDX0jJXIe2SWmwCbGzx9yF8C8xy3Lh34= +github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/orcaman/concurrent-map v0.0.0-20210106121528-16402b402231 h1:fa50YL1pzKW+1SsBnJDOHppJN9stOEwS+CRWyUtyYGU= +github.com/orcaman/concurrent-map v0.0.0-20210106121528-16402b402231/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.0.1 h1:xyiBuvkD2g5n7cYzx6u2sxQvsAy4QJsZFCzGVdzOXZ0= +gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= +gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +istio.io/api v0.0.0-20210322145030-ec7ef4cd6eaf h1:wAkMg4dDRWkas2Gzkln5926OvHw3g/XbKsjICF/msQw= +istio.io/api v0.0.0-20210322145030-ec7ef4cd6eaf/go.mod h1:nsSFw1LIMmGL7r/+6fJI6FxeG/UGlLxRK8bkojIvBVs= +istio.io/gogo-genproto v0.0.0-20210113155706-4daf5697332f h1:9710FpGLvIJ1GGEbpuTh1smVBv+r8cJfR3G82ouSxIQ= +istio.io/gogo-genproto v0.0.0-20210113155706-4daf5697332f/go.mod h1:6BwTZRNbWS570wHX/uR1Wqk5e0157TofTAUMzT7N4+s= +k8s.io/api v0.17.2 h1:NF1UFXcKN7/OOv1uxdRz3qfra8AHsPav5M93hlV9+Dc= +k8s.io/api v0.17.2/go.mod h1:BS9fjjLc4CMuqfSO8vgbHPKMt5+SF0ET6u/RVDihTo4= +k8s.io/apiextensions-apiserver v0.17.2 h1:cP579D2hSZNuO/rZj9XFRzwJNYb41DbNANJb6Kolpss= +k8s.io/apiextensions-apiserver v0.17.2/go.mod h1:4KdMpjkEjjDI2pPfBA15OscyNldHWdBCfsWMDWAmSTs= +k8s.io/apimachinery v0.17.2 h1:hwDQQFbdRlpnnsR64Asdi55GyCaIP/3WQpMmbNBeWr4= +k8s.io/apimachinery v0.17.2/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= +k8s.io/apiserver v0.17.2/go.mod h1:lBmw/TtQdtxvrTk0e2cgtOxHizXI+d0mmGQURIHQZlo= +k8s.io/cli-runtime v0.17.2/go.mod h1:aa8t9ziyQdbkuizkNLAw3qe3srSyWh9zlSB7zTqRNPI= +k8s.io/client-go v0.17.2 h1:ndIfkfXEGrNhLIgkr0+qhRguSD3u6DCmonepn1O6NYc= +k8s.io/client-go v0.17.2/go.mod h1:QAzRgsa0C2xl4/eVpeVAZMvikCn8Nm81yqVx3Kk9XYI= +k8s.io/code-generator v0.17.2/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s= +k8s.io/component-base v0.17.2/go.mod h1:zMPW3g5aH7cHJpKYQ/ZsGMcgbsA/VyhEugF3QT1awLs= +k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= +k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= +k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLyy7rfbeuf1PYyBf973pgyU= +k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= +k8s.io/kubectl v0.17.2/go.mod h1:y4rfLV0n6aPmvbRCqZQjvOp3ezxsFgpqL+zF5jH/lxk= +k8s.io/metrics v0.17.2/go.mod h1:3TkNHET4ROd+NfzNxkjoVfQ0Ob4iZnaHmSEA4vYpwLw= +k8s.io/utils v0.0.0-20191114184206-e782cd3c129f h1:GiPwtSzdP43eI1hpPCbROQCCIgCuiMMNF8YUVLF3vJo= +k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= +modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= +modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= +modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= +modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= +sigs.k8s.io/controller-runtime v0.5.0 h1:CbqIy5fbUX+4E9bpnBFd204YAzRYlM9SWW77BbrcDQo= +sigs.k8s.io/controller-runtime v0.5.0/go.mod h1:REiJzC7Y00U+2YkMbT8wxgrsX5USpXKGhb2sCtAXiT8= +sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= +sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= +sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18= +sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI= diff --git a/staging/src/slime.io/slime/modules/plugin/hack/boilerplate.go.txt b/staging/src/slime.io/slime/modules/plugin/hack/boilerplate.go.txt new file mode 100644 index 00000000..c38248e0 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/hack/boilerplate.go.txt @@ -0,0 +1,15 @@ +/* + + +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. +*/ diff --git a/staging/src/slime.io/slime/modules/plugin/install/samples/plugin/slimeboot_plugin.yaml b/staging/src/slime.io/slime/modules/plugin/install/samples/plugin/slimeboot_plugin.yaml new file mode 100644 index 00000000..1362bd46 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/install/samples/plugin/slimeboot_plugin.yaml @@ -0,0 +1,14 @@ +apiVersion: config.netease.com/v1alpha1 +kind: SlimeBoot +metadata: + name: plugin + namespace: mesh-operator +spec: + module: + - name: plugin # custom value + kind: plugin # should be "plugin" + enable: true + image: + pullPolicy: Always + repository: docker.io/slimeio/slime-plugin + tag: v0.2.0_linux_amd64 diff --git a/staging/src/slime.io/slime/modules/plugin/main.go b/staging/src/slime.io/slime/modules/plugin/main.go new file mode 100644 index 00000000..1571ee68 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/main.go @@ -0,0 +1,27 @@ +/* + + +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 ( + "slime.io/slime/framework/model/module" + "slime.io/slime/modules/plugin/model" + modmodule "slime.io/slime/modules/plugin/module" +) + +func main() { + module.Main(model.ModuleName, []module.Module{&modmodule.Module{}}) +} diff --git a/staging/src/slime.io/slime/modules/plugin/model/model.go b/staging/src/slime.io/slime/modules/plugin/model/model.go new file mode 100644 index 00000000..f3f52e2e --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/model/model.go @@ -0,0 +1,10 @@ +package model + +import ( + "github.com/sirupsen/logrus" + frameworkmodel "slime.io/slime/framework/model" +) + +const ModuleName = "plugin" + +var ModuleLog = logrus.WithField(frameworkmodel.LogFieldKeyModule, ModuleName) diff --git a/staging/src/slime.io/slime/modules/plugin/module/module.go b/staging/src/slime.io/slime/modules/plugin/module/module.go new file mode 100644 index 00000000..e8668b3e --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/module/module.go @@ -0,0 +1,74 @@ +package module + +import ( + "os" + + "slime.io/slime/framework/model/module" + "slime.io/slime/modules/plugin/model" + + "github.com/golang/protobuf/proto" + "k8s.io/apimachinery/pkg/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "sigs.k8s.io/controller-runtime/pkg/manager" + istionetworkingapi "slime.io/slime/framework/apis/networking/v1alpha3" + "slime.io/slime/framework/bootstrap" + pluginapiv1alpha1 "slime.io/slime/modules/plugin/api/v1alpha1" + "slime.io/slime/modules/plugin/controllers" +) + +var log = model.ModuleLog + +type Module struct { + config pluginapiv1alpha1.PluginModule +} + +func (m *Module) Kind() string { + return model.ModuleName +} + +func (m *Module) Config() proto.Message { + return &m.config +} + +func (m *Module) InitScheme(scheme *runtime.Scheme) error { + for _, f := range []func(*runtime.Scheme) error{ + clientgoscheme.AddToScheme, + pluginapiv1alpha1.AddToScheme, + istionetworkingapi.AddToScheme, + } { + if err := f(scheme); err != nil { + return err + } + } + return nil +} + +func (m *Module) Clone() module.Module { + ret := *m + return &ret +} + +func (m *Module) InitManager(mgr manager.Manager, env bootstrap.Environment, cbs module.InitCallbacks) error { + cfg := &m.config + + _ = cfg // unused until now + + var err error + if err = (&controllers.PluginManagerReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + log.Errorf("unable to create pluginManager controller, %+v", err) + os.Exit(1) + } + if err = (&controllers.EnvoyPluginReconciler{ + Env: &env, + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + log.Errorf("unable to create EnvoyPlugin controller, %+v", err) + os.Exit(1) + } + + return nil +} diff --git a/staging/src/slime.io/slime/modules/plugin/publish.sh b/staging/src/slime.io/slime/modules/plugin/publish.sh new file mode 100644 index 00000000..c8898911 --- /dev/null +++ b/staging/src/slime.io/slime/modules/plugin/publish.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +export MOD=plugin +../../../../../../bin/publish.sh "$@"